engine.cc 936 KB
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 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 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 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 104 105 106 107 108 109 110 111 112 113 114 115 116 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 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 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 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 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 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 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 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 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 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 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 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 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 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 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 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 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 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 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 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 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 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 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 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 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 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 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 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 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 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 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 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 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 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 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 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 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 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 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 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 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 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 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 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 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 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 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 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 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 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 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 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 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 4780 4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893 4894 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033 5034 5035 5036 5037 5038 5039 5040 5041 5042 5043 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258 5259 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 5381 5382 5383 5384 5385 5386 5387 5388 5389 5390 5391 5392 5393 5394 5395 5396 5397 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407 5408 5409 5410 5411 5412 5413 5414 5415 5416 5417 5418 5419 5420 5421 5422 5423 5424 5425 5426 5427 5428 5429 5430 5431 5432 5433 5434 5435 5436 5437 5438 5439 5440 5441 5442 5443 5444 5445 5446 5447 5448 5449 5450 5451 5452 5453 5454 5455 5456 5457 5458 5459 5460 5461 5462 5463 5464 5465 5466 5467 5468 5469 5470 5471 5472 5473 5474 5475 5476 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 5498 5499 5500 5501 5502 5503 5504 5505 5506 5507 5508 5509 5510 5511 5512 5513 5514 5515 5516 5517 5518 5519 5520 5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538 5539 5540 5541 5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565 5566 5567 5568 5569 5570 5571 5572 5573 5574 5575 5576 5577 5578 5579 5580 5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615 5616 5617 5618 5619 5620 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 5693 5694 5695 5696 5697 5698 5699 5700 5701 5702 5703 5704 5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715 5716 5717 5718 5719 5720 5721 5722 5723 5724 5725 5726 5727 5728 5729 5730 5731 5732 5733 5734 5735 5736 5737 5738 5739 5740 5741 5742 5743 5744 5745 5746 5747 5748 5749 5750 5751 5752 5753 5754 5755 5756 5757 5758 5759 5760 5761 5762 5763 5764 5765 5766 5767 5768 5769 5770 5771 5772 5773 5774 5775 5776 5777 5778 5779 5780 5781 5782 5783 5784 5785 5786 5787 5788 5789 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799 5800 5801 5802 5803 5804 5805 5806 5807 5808 5809 5810 5811 5812 5813 5814 5815 5816 5817 5818 5819 5820 5821 5822 5823 5824 5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 5839 5840 5841 5842 5843 5844 5845 5846 5847 5848 5849 5850 5851 5852 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 5907 5908 5909 5910 5911 5912 5913 5914 5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956 5957 5958 5959 5960 5961 5962 5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996 5997 5998 5999 6000 6001 6002 6003 6004 6005 6006 6007 6008 6009 6010 6011 6012 6013 6014 6015 6016 6017 6018 6019 6020 6021 6022 6023 6024 6025 6026 6027 6028 6029 6030 6031 6032 6033 6034 6035 6036 6037 6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 6050 6051 6052 6053 6054 6055 6056 6057 6058 6059 6060 6061 6062 6063 6064 6065 6066 6067 6068 6069 6070 6071 6072 6073 6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085 6086 6087 6088 6089 6090 6091 6092 6093 6094 6095 6096 6097 6098 6099 6100 6101 6102 6103 6104 6105 6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150 6151 6152 6153 6154 6155 6156 6157 6158 6159 6160 6161 6162 6163 6164 6165 6166 6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 6178 6179 6180 6181 6182 6183 6184 6185 6186 6187 6188 6189 6190 6191 6192 6193 6194 6195 6196 6197 6198 6199 6200 6201 6202 6203 6204 6205 6206 6207 6208 6209 6210 6211 6212 6213 6214 6215 6216 6217 6218 6219 6220 6221 6222 6223 6224 6225 6226 6227 6228 6229 6230 6231 6232 6233 6234 6235 6236 6237 6238 6239 6240 6241 6242 6243 6244 6245 6246 6247 6248 6249 6250 6251 6252 6253 6254 6255 6256 6257 6258 6259 6260 6261 6262 6263 6264 6265 6266 6267 6268 6269 6270 6271 6272 6273 6274 6275 6276 6277 6278 6279 6280 6281 6282 6283 6284 6285 6286 6287 6288 6289 6290 6291 6292 6293 6294 6295 6296 6297 6298 6299 6300 6301 6302 6303 6304 6305 6306 6307 6308 6309 6310 6311 6312 6313 6314 6315 6316 6317 6318 6319 6320 6321 6322 6323 6324 6325 6326 6327 6328 6329 6330 6331 6332 6333 6334 6335 6336 6337 6338 6339 6340 6341 6342 6343 6344 6345 6346 6347 6348 6349 6350 6351 6352 6353 6354 6355 6356 6357 6358 6359 6360 6361 6362 6363 6364 6365 6366 6367 6368 6369 6370 6371 6372 6373 6374 6375 6376 6377 6378 6379 6380 6381 6382 6383 6384 6385 6386 6387 6388 6389 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400 6401 6402 6403 6404 6405 6406 6407 6408 6409 6410 6411 6412 6413 6414 6415 6416 6417 6418 6419 6420 6421 6422 6423 6424 6425 6426 6427 6428 6429 6430 6431 6432 6433 6434 6435 6436 6437 6438 6439 6440 6441 6442 6443 6444 6445 6446 6447 6448 6449 6450 6451 6452 6453 6454 6455 6456 6457 6458 6459 6460 6461 6462 6463 6464 6465 6466 6467 6468 6469 6470 6471 6472 6473 6474 6475 6476 6477 6478 6479 6480 6481 6482 6483 6484 6485 6486 6487 6488 6489 6490 6491 6492 6493 6494 6495 6496 6497 6498 6499 6500 6501 6502 6503 6504 6505 6506 6507 6508 6509 6510 6511 6512 6513 6514 6515 6516 6517 6518 6519 6520 6521 6522 6523 6524 6525 6526 6527 6528 6529 6530 6531 6532 6533 6534 6535 6536 6537 6538 6539 6540 6541 6542 6543 6544 6545 6546 6547 6548 6549 6550 6551 6552 6553 6554 6555 6556 6557 6558 6559 6560 6561 6562 6563 6564 6565 6566 6567 6568 6569 6570 6571 6572 6573 6574 6575 6576 6577 6578 6579 6580 6581 6582 6583 6584 6585 6586 6587 6588 6589 6590 6591 6592 6593 6594 6595 6596 6597 6598 6599 6600 6601 6602 6603 6604 6605 6606 6607 6608 6609 6610 6611 6612 6613 6614 6615 6616 6617 6618 6619 6620 6621 6622 6623 6624 6625 6626 6627 6628 6629 6630 6631 6632 6633 6634 6635 6636 6637 6638 6639 6640 6641 6642 6643 6644 6645 6646 6647 6648 6649 6650 6651 6652 6653 6654 6655 6656 6657 6658 6659 6660 6661 6662 6663 6664 6665 6666 6667 6668 6669 6670 6671 6672 6673 6674 6675 6676 6677 6678 6679 6680 6681 6682 6683 6684 6685 6686 6687 6688 6689 6690 6691 6692 6693 6694 6695 6696 6697 6698 6699 6700 6701 6702 6703 6704 6705 6706 6707 6708 6709 6710 6711 6712 6713 6714 6715 6716 6717 6718 6719 6720 6721 6722 6723 6724 6725 6726 6727 6728 6729 6730 6731 6732 6733 6734 6735 6736 6737 6738 6739 6740 6741 6742 6743 6744 6745 6746 6747 6748 6749 6750 6751 6752 6753 6754 6755 6756 6757 6758 6759 6760 6761 6762 6763 6764 6765 6766 6767 6768 6769 6770 6771 6772 6773 6774 6775 6776 6777 6778 6779 6780 6781 6782 6783 6784 6785 6786 6787 6788 6789 6790 6791 6792 6793 6794 6795 6796 6797 6798 6799 6800 6801 6802 6803 6804 6805 6806 6807 6808 6809 6810 6811 6812 6813 6814 6815 6816 6817 6818 6819 6820 6821 6822 6823 6824 6825 6826 6827 6828 6829 6830 6831 6832 6833 6834 6835 6836 6837 6838 6839 6840 6841 6842 6843 6844 6845 6846 6847 6848 6849 6850 6851 6852 6853 6854 6855 6856 6857 6858 6859 6860 6861 6862 6863 6864 6865 6866 6867 6868 6869 6870 6871 6872 6873 6874 6875 6876 6877 6878 6879 6880 6881 6882 6883 6884 6885 6886 6887 6888 6889 6890 6891 6892 6893 6894 6895 6896 6897 6898 6899 6900 6901 6902 6903 6904 6905 6906 6907 6908 6909 6910 6911 6912 6913 6914 6915 6916 6917 6918 6919 6920 6921 6922 6923 6924 6925 6926 6927 6928 6929 6930 6931 6932 6933 6934 6935 6936 6937 6938 6939 6940 6941 6942 6943 6944 6945 6946 6947 6948 6949 6950 6951 6952 6953 6954 6955 6956 6957 6958 6959 6960 6961 6962 6963 6964 6965 6966 6967 6968 6969 6970 6971 6972 6973 6974 6975 6976 6977 6978 6979 6980 6981 6982 6983 6984 6985 6986 6987 6988 6989 6990 6991 6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 7002 7003 7004 7005 7006 7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7017 7018 7019 7020 7021 7022 7023 7024 7025 7026 7027 7028 7029 7030 7031 7032 7033 7034 7035 7036 7037 7038 7039 7040 7041 7042 7043 7044 7045 7046 7047 7048 7049 7050 7051 7052 7053 7054 7055 7056 7057 7058 7059 7060 7061 7062 7063 7064 7065 7066 7067 7068 7069 7070 7071 7072 7073 7074 7075 7076 7077 7078 7079 7080 7081 7082 7083 7084 7085 7086 7087 7088 7089 7090 7091 7092 7093 7094 7095 7096 7097 7098 7099 7100 7101 7102 7103 7104 7105 7106 7107 7108 7109 7110 7111 7112 7113 7114 7115 7116 7117 7118 7119 7120 7121 7122 7123 7124 7125 7126 7127 7128 7129 7130 7131 7132 7133 7134 7135 7136 7137 7138 7139 7140 7141 7142 7143 7144 7145 7146 7147 7148 7149 7150 7151 7152 7153 7154 7155 7156 7157 7158 7159 7160 7161 7162 7163 7164 7165 7166 7167 7168 7169 7170 7171 7172 7173 7174 7175 7176 7177 7178 7179 7180 7181 7182 7183 7184 7185 7186 7187 7188 7189 7190 7191 7192 7193 7194 7195 7196 7197 7198 7199 7200 7201 7202 7203 7204 7205 7206 7207 7208 7209 7210 7211 7212 7213 7214 7215 7216 7217 7218 7219 7220 7221 7222 7223 7224 7225 7226 7227 7228 7229 7230 7231 7232 7233 7234 7235 7236 7237 7238 7239 7240 7241 7242 7243 7244 7245 7246 7247 7248 7249 7250 7251 7252 7253 7254 7255 7256 7257 7258 7259 7260 7261 7262 7263 7264 7265 7266 7267 7268 7269 7270 7271 7272 7273 7274 7275 7276 7277 7278 7279 7280 7281 7282 7283 7284 7285 7286 7287 7288 7289 7290 7291 7292 7293 7294 7295 7296 7297 7298 7299 7300 7301 7302 7303 7304 7305 7306 7307 7308 7309 7310 7311 7312 7313 7314 7315 7316 7317 7318 7319 7320 7321 7322 7323 7324 7325 7326 7327 7328 7329 7330 7331 7332 7333 7334 7335 7336 7337 7338 7339 7340 7341 7342 7343 7344 7345 7346 7347 7348 7349 7350 7351 7352 7353 7354 7355 7356 7357 7358 7359 7360 7361 7362 7363 7364 7365 7366 7367 7368 7369 7370 7371 7372 7373 7374 7375 7376 7377 7378 7379 7380 7381 7382 7383 7384 7385 7386 7387 7388 7389 7390 7391 7392 7393 7394 7395 7396 7397 7398 7399 7400 7401 7402 7403 7404 7405 7406 7407 7408 7409 7410 7411 7412 7413 7414 7415 7416 7417 7418 7419 7420 7421 7422 7423 7424 7425 7426 7427 7428 7429 7430 7431 7432 7433 7434 7435 7436 7437 7438 7439 7440 7441 7442 7443 7444 7445 7446 7447 7448 7449 7450 7451 7452 7453 7454 7455 7456 7457 7458 7459 7460 7461 7462 7463 7464 7465 7466 7467 7468 7469 7470 7471 7472 7473 7474 7475 7476 7477 7478 7479 7480 7481 7482 7483 7484 7485 7486 7487 7488 7489 7490 7491 7492 7493 7494 7495 7496 7497 7498 7499 7500 7501 7502 7503 7504 7505 7506 7507 7508 7509 7510 7511 7512 7513 7514 7515 7516 7517 7518 7519 7520 7521 7522 7523 7524 7525 7526 7527 7528 7529 7530 7531 7532 7533 7534 7535 7536 7537 7538 7539 7540 7541 7542 7543 7544 7545 7546 7547 7548 7549 7550 7551 7552 7553 7554 7555 7556 7557 7558 7559 7560 7561 7562 7563 7564 7565 7566 7567 7568 7569 7570 7571 7572 7573 7574 7575 7576 7577 7578 7579 7580 7581 7582 7583 7584 7585 7586 7587 7588 7589 7590 7591 7592 7593 7594 7595 7596 7597 7598 7599 7600 7601 7602 7603 7604 7605 7606 7607 7608 7609 7610 7611 7612 7613 7614 7615 7616 7617 7618 7619 7620 7621 7622 7623 7624 7625 7626 7627 7628 7629 7630 7631 7632 7633 7634 7635 7636 7637 7638 7639 7640 7641 7642 7643 7644 7645 7646 7647 7648 7649 7650 7651 7652 7653 7654 7655 7656 7657 7658 7659 7660 7661 7662 7663 7664 7665 7666 7667 7668 7669 7670 7671 7672 7673 7674 7675 7676 7677 7678 7679 7680 7681 7682 7683 7684 7685 7686 7687 7688 7689 7690 7691 7692 7693 7694 7695 7696 7697 7698 7699 7700 7701 7702 7703 7704 7705 7706 7707 7708 7709 7710 7711 7712 7713 7714 7715 7716 7717 7718 7719 7720 7721 7722 7723 7724 7725 7726 7727 7728 7729 7730 7731 7732 7733 7734 7735 7736 7737 7738 7739 7740 7741 7742 7743 7744 7745 7746 7747 7748 7749 7750 7751 7752 7753 7754 7755 7756 7757 7758 7759 7760 7761 7762 7763 7764 7765 7766 7767 7768 7769 7770 7771 7772 7773 7774 7775 7776 7777 7778 7779 7780 7781 7782 7783 7784 7785 7786 7787 7788 7789 7790 7791 7792 7793 7794 7795 7796 7797 7798 7799 7800 7801 7802 7803 7804 7805 7806 7807 7808 7809 7810 7811 7812 7813 7814 7815 7816 7817 7818 7819 7820 7821 7822 7823 7824 7825 7826 7827 7828 7829 7830 7831 7832 7833 7834 7835 7836 7837 7838 7839 7840 7841 7842 7843 7844 7845 7846 7847 7848 7849 7850 7851 7852 7853 7854 7855 7856 7857 7858 7859 7860 7861 7862 7863 7864 7865 7866 7867 7868 7869 7870 7871 7872 7873 7874 7875 7876 7877 7878 7879 7880 7881 7882 7883 7884 7885 7886 7887 7888 7889 7890 7891 7892 7893 7894 7895 7896 7897 7898 7899 7900 7901 7902 7903 7904 7905 7906 7907 7908 7909 7910 7911 7912 7913 7914 7915 7916 7917 7918 7919 7920 7921 7922 7923 7924 7925 7926 7927 7928 7929 7930 7931 7932 7933 7934 7935 7936 7937 7938 7939 7940 7941 7942 7943 7944 7945 7946 7947 7948 7949 7950 7951 7952 7953 7954 7955 7956 7957 7958 7959 7960 7961 7962 7963 7964 7965 7966 7967 7968 7969 7970 7971 7972 7973 7974 7975 7976 7977 7978 7979 7980 7981 7982 7983 7984 7985 7986 7987 7988 7989 7990 7991 7992 7993 7994 7995 7996 7997 7998 7999 8000 8001 8002 8003 8004 8005 8006 8007 8008 8009 8010 8011 8012 8013 8014 8015 8016 8017 8018 8019 8020 8021 8022 8023 8024 8025 8026 8027 8028 8029 8030 8031 8032 8033 8034 8035 8036 8037 8038 8039 8040 8041 8042 8043 8044 8045 8046 8047 8048 8049 8050 8051 8052 8053 8054 8055 8056 8057 8058 8059 8060 8061 8062 8063 8064 8065 8066 8067 8068 8069 8070 8071 8072 8073 8074 8075 8076 8077 8078 8079 8080 8081 8082 8083 8084 8085 8086 8087 8088 8089 8090 8091 8092 8093 8094 8095 8096 8097 8098 8099 8100 8101 8102 8103 8104 8105 8106 8107 8108 8109 8110 8111 8112 8113 8114 8115 8116 8117 8118 8119 8120 8121 8122 8123 8124 8125 8126 8127 8128 8129 8130 8131 8132 8133 8134 8135 8136 8137 8138 8139 8140 8141 8142 8143 8144 8145 8146 8147 8148 8149 8150 8151 8152 8153 8154 8155 8156 8157 8158 8159 8160 8161 8162 8163 8164 8165 8166 8167 8168 8169 8170 8171 8172 8173 8174 8175 8176 8177 8178 8179 8180 8181 8182 8183 8184 8185 8186 8187 8188 8189 8190 8191 8192 8193 8194 8195 8196 8197 8198 8199 8200 8201 8202 8203 8204 8205 8206 8207 8208 8209 8210 8211 8212 8213 8214 8215 8216 8217 8218 8219 8220 8221 8222 8223 8224 8225 8226 8227 8228 8229 8230 8231 8232 8233 8234 8235 8236 8237 8238 8239 8240 8241 8242 8243 8244 8245 8246 8247 8248 8249 8250 8251 8252 8253 8254 8255 8256 8257 8258 8259 8260 8261 8262 8263 8264 8265 8266 8267 8268 8269 8270 8271 8272 8273 8274 8275 8276 8277 8278 8279 8280 8281 8282 8283 8284 8285 8286 8287 8288 8289 8290 8291 8292 8293 8294 8295 8296 8297 8298 8299 8300 8301 8302 8303 8304 8305 8306 8307 8308 8309 8310 8311 8312 8313 8314 8315 8316 8317 8318 8319 8320 8321 8322 8323 8324 8325 8326 8327 8328 8329 8330 8331 8332 8333 8334 8335 8336 8337 8338 8339 8340 8341 8342 8343 8344 8345 8346 8347 8348 8349 8350 8351 8352 8353 8354 8355 8356 8357 8358 8359 8360 8361 8362 8363 8364 8365 8366 8367 8368 8369 8370 8371 8372 8373 8374 8375 8376 8377 8378 8379 8380 8381 8382 8383 8384 8385 8386 8387 8388 8389 8390 8391 8392 8393 8394 8395 8396 8397 8398 8399 8400 8401 8402 8403 8404 8405 8406 8407 8408 8409 8410 8411 8412 8413 8414 8415 8416 8417 8418 8419 8420 8421 8422 8423 8424 8425 8426 8427 8428 8429 8430 8431 8432 8433 8434 8435 8436 8437 8438 8439 8440 8441 8442 8443 8444 8445 8446 8447 8448 8449 8450 8451 8452 8453 8454 8455 8456 8457 8458 8459 8460 8461 8462 8463 8464 8465 8466 8467 8468 8469 8470 8471 8472 8473 8474 8475 8476 8477 8478 8479 8480 8481 8482 8483 8484 8485 8486 8487 8488 8489 8490 8491 8492 8493 8494 8495 8496 8497 8498 8499 8500 8501 8502 8503 8504 8505 8506 8507 8508 8509 8510 8511 8512 8513 8514 8515 8516 8517 8518 8519 8520 8521 8522 8523 8524 8525 8526 8527 8528 8529 8530 8531 8532 8533 8534 8535 8536 8537 8538 8539 8540 8541 8542 8543 8544 8545 8546 8547 8548 8549 8550 8551 8552 8553 8554 8555 8556 8557 8558 8559 8560 8561 8562 8563 8564 8565 8566 8567 8568 8569 8570 8571 8572 8573 8574 8575 8576 8577 8578 8579 8580 8581 8582 8583 8584 8585 8586 8587 8588 8589 8590 8591 8592 8593 8594 8595 8596 8597 8598 8599 8600 8601 8602 8603 8604 8605 8606 8607 8608 8609 8610 8611 8612 8613 8614 8615 8616 8617 8618 8619 8620 8621 8622 8623 8624 8625 8626 8627 8628 8629 8630 8631 8632 8633 8634 8635 8636 8637 8638 8639 8640 8641 8642 8643 8644 8645 8646 8647 8648 8649 8650 8651 8652 8653 8654 8655 8656 8657 8658 8659 8660 8661 8662 8663 8664 8665 8666 8667 8668 8669 8670 8671 8672 8673 8674 8675 8676 8677 8678 8679 8680 8681 8682 8683 8684 8685 8686 8687 8688 8689 8690 8691 8692 8693 8694 8695 8696 8697 8698 8699 8700 8701 8702 8703 8704 8705 8706 8707 8708 8709 8710 8711 8712 8713 8714 8715 8716 8717 8718 8719 8720 8721 8722 8723 8724 8725 8726 8727 8728 8729 8730 8731 8732 8733 8734 8735 8736 8737 8738 8739 8740 8741 8742 8743 8744 8745 8746 8747 8748 8749 8750 8751 8752 8753 8754 8755 8756 8757 8758 8759 8760 8761 8762 8763 8764 8765 8766 8767 8768 8769 8770 8771 8772 8773 8774 8775 8776 8777 8778 8779 8780 8781 8782 8783 8784 8785 8786 8787 8788 8789 8790 8791 8792 8793 8794 8795 8796 8797 8798 8799 8800 8801 8802 8803 8804 8805 8806 8807 8808 8809 8810 8811 8812 8813 8814 8815 8816 8817 8818 8819 8820 8821 8822 8823 8824 8825 8826 8827 8828 8829 8830 8831 8832 8833 8834 8835 8836 8837 8838 8839 8840 8841 8842 8843 8844 8845 8846 8847 8848 8849 8850 8851 8852 8853 8854 8855 8856 8857 8858 8859 8860 8861 8862 8863 8864 8865 8866 8867 8868 8869 8870 8871 8872 8873 8874 8875 8876 8877 8878 8879 8880 8881 8882 8883 8884 8885 8886 8887 8888 8889 8890 8891 8892 8893 8894 8895 8896 8897 8898 8899 8900 8901 8902 8903 8904 8905 8906 8907 8908 8909 8910 8911 8912 8913 8914 8915 8916 8917 8918 8919 8920 8921 8922 8923 8924 8925 8926 8927 8928 8929 8930 8931 8932 8933 8934 8935 8936 8937 8938 8939 8940 8941 8942 8943 8944 8945 8946 8947 8948 8949 8950 8951 8952 8953 8954 8955 8956 8957 8958 8959 8960 8961 8962 8963 8964 8965 8966 8967 8968 8969 8970 8971 8972 8973 8974 8975 8976 8977 8978 8979 8980 8981 8982 8983 8984 8985 8986 8987 8988 8989 8990 8991 8992 8993 8994 8995 8996 8997 8998 8999 9000 9001 9002 9003 9004 9005 9006 9007 9008 9009 9010 9011 9012 9013 9014 9015 9016 9017 9018 9019 9020 9021 9022 9023 9024 9025 9026 9027 9028 9029 9030 9031 9032 9033 9034 9035 9036 9037 9038 9039 9040 9041 9042 9043 9044 9045 9046 9047 9048 9049 9050 9051 9052 9053 9054 9055 9056 9057 9058 9059 9060 9061 9062 9063 9064 9065 9066 9067 9068 9069 9070 9071 9072 9073 9074 9075 9076 9077 9078 9079 9080 9081 9082 9083 9084 9085 9086 9087 9088 9089 9090 9091 9092 9093 9094 9095 9096 9097 9098 9099 9100 9101 9102 9103 9104 9105 9106 9107 9108 9109 9110 9111 9112 9113 9114 9115 9116 9117 9118 9119 9120 9121 9122 9123 9124 9125 9126 9127 9128 9129 9130 9131 9132 9133 9134 9135 9136 9137 9138 9139 9140 9141 9142 9143 9144 9145 9146 9147 9148 9149 9150 9151 9152 9153 9154 9155 9156 9157 9158 9159 9160 9161 9162 9163 9164 9165 9166 9167 9168 9169 9170 9171 9172 9173 9174 9175 9176 9177 9178 9179 9180 9181 9182 9183 9184 9185 9186 9187 9188 9189 9190 9191 9192 9193 9194 9195 9196 9197 9198 9199 9200 9201 9202 9203 9204 9205 9206 9207 9208 9209 9210 9211 9212 9213 9214 9215 9216 9217 9218 9219 9220 9221 9222 9223 9224 9225 9226 9227 9228 9229 9230 9231 9232 9233 9234 9235 9236 9237 9238 9239 9240 9241 9242 9243 9244 9245 9246 9247 9248 9249 9250 9251 9252 9253 9254 9255 9256 9257 9258 9259 9260 9261 9262 9263 9264 9265 9266 9267 9268 9269 9270 9271 9272 9273 9274 9275 9276 9277 9278 9279 9280 9281 9282 9283 9284 9285 9286 9287 9288 9289 9290 9291 9292 9293 9294 9295 9296 9297 9298 9299 9300 9301 9302 9303 9304 9305 9306 9307 9308 9309 9310 9311 9312 9313 9314 9315 9316 9317 9318 9319 9320 9321 9322 9323 9324 9325 9326 9327 9328 9329 9330 9331 9332 9333 9334 9335 9336 9337 9338 9339 9340 9341 9342 9343 9344 9345 9346 9347 9348 9349 9350 9351 9352 9353 9354 9355 9356 9357 9358 9359 9360 9361 9362 9363 9364 9365 9366 9367 9368 9369 9370 9371 9372 9373 9374 9375 9376 9377 9378 9379 9380 9381 9382 9383 9384 9385 9386 9387 9388 9389 9390 9391 9392 9393 9394 9395 9396 9397 9398 9399 9400 9401 9402 9403 9404 9405 9406 9407 9408 9409 9410 9411 9412 9413 9414 9415 9416 9417 9418 9419 9420 9421 9422 9423 9424 9425 9426 9427 9428 9429 9430 9431 9432 9433 9434 9435 9436 9437 9438 9439 9440 9441 9442 9443 9444 9445 9446 9447 9448 9449 9450 9451 9452 9453 9454 9455 9456 9457 9458 9459 9460 9461 9462 9463 9464 9465 9466 9467 9468 9469 9470 9471 9472 9473 9474 9475 9476 9477 9478 9479 9480 9481 9482 9483 9484 9485 9486 9487 9488 9489 9490 9491 9492 9493 9494 9495 9496 9497 9498 9499 9500 9501 9502 9503 9504 9505 9506 9507 9508 9509 9510 9511 9512 9513 9514 9515 9516 9517 9518 9519 9520 9521 9522 9523 9524 9525 9526 9527 9528 9529 9530 9531 9532 9533 9534 9535 9536 9537 9538 9539 9540 9541 9542 9543 9544 9545 9546 9547 9548 9549 9550 9551 9552 9553 9554 9555 9556 9557 9558 9559 9560 9561 9562 9563 9564 9565 9566 9567 9568 9569 9570 9571 9572 9573 9574 9575 9576 9577 9578 9579 9580 9581 9582 9583 9584 9585 9586 9587 9588 9589 9590 9591 9592 9593 9594 9595 9596 9597 9598 9599 9600 9601 9602 9603 9604 9605 9606 9607 9608 9609 9610 9611 9612 9613 9614 9615 9616 9617 9618 9619 9620 9621 9622 9623 9624 9625 9626 9627 9628 9629 9630 9631 9632 9633 9634 9635 9636 9637 9638 9639 9640 9641 9642 9643 9644 9645 9646 9647 9648 9649 9650 9651 9652 9653 9654 9655 9656 9657 9658 9659 9660 9661 9662 9663 9664 9665 9666 9667 9668 9669 9670 9671 9672 9673 9674 9675 9676 9677 9678 9679 9680 9681 9682 9683 9684 9685 9686 9687 9688 9689 9690 9691 9692 9693 9694 9695 9696 9697 9698 9699 9700 9701 9702 9703 9704 9705 9706 9707 9708 9709 9710 9711 9712 9713 9714 9715 9716 9717 9718 9719 9720 9721 9722 9723 9724 9725 9726 9727 9728 9729 9730 9731 9732 9733 9734 9735 9736 9737 9738 9739 9740 9741 9742 9743 9744 9745 9746 9747 9748 9749 9750 9751 9752 9753 9754 9755 9756 9757 9758 9759 9760 9761 9762 9763 9764 9765 9766 9767 9768 9769 9770 9771 9772 9773 9774 9775 9776 9777 9778 9779 9780 9781 9782 9783 9784 9785 9786 9787 9788 9789 9790 9791 9792 9793 9794 9795 9796 9797 9798 9799 9800 9801 9802 9803 9804 9805 9806 9807 9808 9809 9810 9811 9812 9813 9814 9815 9816 9817 9818 9819 9820 9821 9822 9823 9824 9825 9826 9827 9828 9829 9830 9831 9832 9833 9834 9835 9836 9837 9838 9839 9840 9841 9842 9843 9844 9845 9846 9847 9848 9849 9850 9851 9852 9853 9854 9855 9856 9857 9858 9859 9860 9861 9862 9863 9864 9865 9866 9867 9868 9869 9870 9871 9872 9873 9874 9875 9876 9877 9878 9879 9880 9881 9882 9883 9884 9885 9886 9887 9888 9889 9890 9891 9892 9893 9894 9895 9896 9897 9898 9899 9900 9901 9902 9903 9904 9905 9906 9907 9908 9909 9910 9911 9912 9913 9914 9915 9916 9917 9918 9919 9920 9921 9922 9923 9924 9925 9926 9927 9928 9929 9930 9931 9932 9933 9934 9935 9936 9937 9938 9939 9940 9941 9942 9943 9944 9945 9946 9947 9948 9949 9950 9951 9952 9953 9954 9955 9956 9957 9958 9959 9960 9961 9962 9963 9964 9965 9966 9967 9968 9969 9970 9971 9972 9973 9974 9975 9976 9977 9978 9979 9980 9981 9982 9983 9984 9985 9986 9987 9988 9989 9990 9991 9992 9993 9994 9995 9996 9997 9998 9999 10000 10001 10002 10003 10004 10005 10006 10007 10008 10009 10010 10011 10012 10013 10014 10015 10016 10017 10018 10019 10020 10021 10022 10023 10024 10025 10026 10027 10028 10029 10030 10031 10032 10033 10034 10035 10036 10037 10038 10039 10040 10041 10042 10043 10044 10045 10046 10047 10048 10049 10050 10051 10052 10053 10054 10055 10056 10057 10058 10059 10060 10061 10062 10063 10064 10065 10066 10067 10068 10069 10070 10071 10072 10073 10074 10075 10076 10077 10078 10079 10080 10081 10082 10083 10084 10085 10086 10087 10088 10089 10090 10091 10092 10093 10094 10095 10096 10097 10098 10099 10100 10101 10102 10103 10104 10105 10106 10107 10108 10109 10110 10111 10112 10113 10114 10115 10116 10117 10118 10119 10120 10121 10122 10123 10124 10125 10126 10127 10128 10129 10130 10131 10132 10133 10134 10135 10136 10137 10138 10139 10140 10141 10142 10143 10144 10145 10146 10147 10148 10149 10150 10151 10152 10153 10154 10155 10156 10157 10158 10159 10160 10161 10162 10163 10164 10165 10166 10167 10168 10169 10170 10171 10172 10173 10174 10175 10176 10177 10178 10179 10180 10181 10182 10183 10184 10185 10186 10187 10188 10189 10190 10191 10192 10193 10194 10195 10196 10197 10198 10199 10200 10201 10202 10203 10204 10205 10206 10207 10208 10209 10210 10211 10212 10213 10214 10215 10216 10217 10218 10219 10220 10221 10222 10223 10224 10225 10226 10227 10228 10229 10230 10231 10232 10233 10234 10235 10236 10237 10238 10239 10240 10241 10242 10243 10244 10245 10246 10247 10248 10249 10250 10251 10252 10253 10254 10255 10256 10257 10258 10259 10260 10261 10262 10263 10264 10265 10266 10267 10268 10269 10270 10271 10272 10273 10274 10275 10276 10277 10278 10279 10280 10281 10282 10283 10284 10285 10286 10287 10288 10289 10290 10291 10292 10293 10294 10295 10296 10297 10298 10299 10300 10301 10302 10303 10304 10305 10306 10307 10308 10309 10310 10311 10312 10313 10314 10315 10316 10317 10318 10319 10320 10321 10322 10323 10324 10325 10326 10327 10328 10329 10330 10331 10332 10333 10334 10335 10336 10337 10338 10339 10340 10341 10342 10343 10344 10345 10346 10347 10348 10349 10350 10351 10352 10353 10354 10355 10356 10357 10358 10359 10360 10361 10362 10363 10364 10365 10366 10367 10368 10369 10370 10371 10372 10373 10374 10375 10376 10377 10378 10379 10380 10381 10382 10383 10384 10385 10386 10387 10388 10389 10390 10391 10392 10393 10394 10395 10396 10397 10398 10399 10400 10401 10402 10403 10404 10405 10406 10407 10408 10409 10410 10411 10412 10413 10414 10415 10416 10417 10418 10419 10420 10421 10422 10423 10424 10425 10426 10427 10428 10429 10430 10431 10432 10433 10434 10435 10436 10437 10438 10439 10440 10441 10442 10443 10444 10445 10446 10447 10448 10449 10450 10451 10452 10453 10454 10455 10456 10457 10458 10459 10460 10461 10462 10463 10464 10465 10466 10467 10468 10469 10470 10471 10472 10473 10474 10475 10476 10477 10478 10479 10480 10481 10482 10483 10484 10485 10486 10487 10488 10489 10490 10491 10492 10493 10494 10495 10496 10497 10498 10499 10500 10501 10502 10503 10504 10505 10506 10507 10508 10509 10510 10511 10512 10513 10514 10515 10516 10517 10518 10519 10520 10521 10522 10523 10524 10525 10526 10527 10528 10529 10530 10531 10532 10533 10534 10535 10536 10537 10538 10539 10540 10541 10542 10543 10544 10545 10546 10547 10548 10549 10550 10551 10552 10553 10554 10555 10556 10557 10558 10559 10560 10561 10562 10563 10564 10565 10566 10567 10568 10569 10570 10571 10572 10573 10574 10575 10576 10577 10578 10579 10580 10581 10582 10583 10584 10585 10586 10587 10588 10589 10590 10591 10592 10593 10594 10595 10596 10597 10598 10599 10600 10601 10602 10603 10604 10605 10606 10607 10608 10609 10610 10611 10612 10613 10614 10615 10616 10617 10618 10619 10620 10621 10622 10623 10624 10625 10626 10627 10628 10629 10630 10631 10632 10633 10634 10635 10636 10637 10638 10639 10640 10641 10642 10643 10644 10645 10646 10647 10648 10649 10650 10651 10652 10653 10654 10655 10656 10657 10658 10659 10660 10661 10662 10663 10664 10665 10666 10667 10668 10669 10670 10671 10672 10673 10674 10675 10676 10677 10678 10679 10680 10681 10682 10683 10684 10685 10686 10687 10688 10689 10690 10691 10692 10693 10694 10695 10696 10697 10698 10699 10700 10701 10702 10703 10704 10705 10706 10707 10708 10709 10710 10711 10712 10713 10714 10715 10716 10717 10718 10719 10720 10721 10722 10723 10724 10725 10726 10727 10728 10729 10730 10731 10732 10733 10734 10735 10736 10737 10738 10739 10740 10741 10742 10743 10744 10745 10746 10747 10748 10749 10750 10751 10752 10753 10754 10755 10756 10757 10758 10759 10760 10761 10762 10763 10764 10765 10766 10767 10768 10769 10770 10771 10772 10773 10774 10775 10776 10777 10778 10779 10780 10781 10782 10783 10784 10785 10786 10787 10788 10789 10790 10791 10792 10793 10794 10795 10796 10797 10798 10799 10800 10801 10802 10803 10804 10805 10806 10807 10808 10809 10810 10811 10812 10813 10814 10815 10816 10817 10818 10819 10820 10821 10822 10823 10824 10825 10826 10827 10828 10829 10830 10831 10832 10833 10834 10835 10836 10837 10838 10839 10840 10841 10842 10843 10844 10845 10846 10847 10848 10849 10850 10851 10852 10853 10854 10855 10856 10857 10858 10859 10860 10861 10862 10863 10864 10865 10866 10867 10868 10869 10870 10871 10872 10873 10874 10875 10876 10877 10878 10879 10880 10881 10882 10883 10884 10885 10886 10887 10888 10889 10890 10891 10892 10893 10894 10895 10896 10897 10898 10899 10900 10901 10902 10903 10904 10905 10906 10907 10908 10909 10910 10911 10912 10913 10914 10915 10916 10917 10918 10919 10920 10921 10922 10923 10924 10925 10926 10927 10928 10929 10930 10931 10932 10933 10934 10935 10936 10937 10938 10939 10940 10941 10942 10943 10944 10945 10946 10947 10948 10949 10950 10951 10952 10953 10954 10955 10956 10957 10958 10959 10960 10961 10962 10963 10964 10965 10966 10967 10968 10969 10970 10971 10972 10973 10974 10975 10976 10977 10978 10979 10980 10981 10982 10983 10984 10985 10986 10987 10988 10989 10990 10991 10992 10993 10994 10995 10996 10997 10998 10999 11000 11001 11002 11003 11004 11005 11006 11007 11008 11009 11010 11011 11012 11013 11014 11015 11016 11017 11018 11019 11020 11021 11022 11023 11024 11025 11026 11027 11028 11029 11030 11031 11032 11033 11034 11035 11036 11037 11038 11039 11040 11041 11042 11043 11044 11045 11046 11047 11048 11049 11050 11051 11052 11053 11054 11055 11056 11057 11058 11059 11060 11061 11062 11063 11064 11065 11066 11067 11068 11069 11070 11071 11072 11073 11074 11075 11076 11077 11078 11079 11080 11081 11082 11083 11084 11085 11086 11087 11088 11089 11090 11091 11092 11093 11094 11095 11096 11097 11098 11099 11100 11101 11102 11103 11104 11105 11106 11107 11108 11109 11110 11111 11112 11113 11114 11115 11116 11117 11118 11119 11120 11121 11122 11123 11124 11125 11126 11127 11128 11129 11130 11131 11132 11133 11134 11135 11136 11137 11138 11139 11140 11141 11142 11143 11144 11145 11146 11147 11148 11149 11150 11151 11152 11153 11154 11155 11156 11157 11158 11159 11160 11161 11162 11163 11164 11165 11166 11167 11168 11169 11170 11171 11172 11173 11174 11175 11176 11177 11178 11179 11180 11181 11182 11183 11184 11185 11186 11187 11188 11189 11190 11191 11192 11193 11194 11195 11196 11197 11198 11199 11200 11201 11202 11203 11204 11205 11206 11207 11208 11209 11210 11211 11212 11213 11214 11215 11216 11217 11218 11219 11220 11221 11222 11223 11224 11225 11226 11227 11228 11229 11230 11231 11232 11233 11234 11235 11236 11237 11238 11239 11240 11241 11242 11243 11244 11245 11246 11247 11248 11249 11250 11251 11252 11253 11254 11255 11256 11257 11258 11259 11260 11261 11262 11263 11264 11265 11266 11267 11268 11269 11270 11271 11272 11273 11274 11275 11276 11277 11278 11279 11280 11281 11282 11283 11284 11285 11286 11287 11288 11289 11290 11291 11292 11293 11294 11295 11296 11297 11298 11299 11300 11301 11302 11303 11304 11305 11306 11307 11308 11309 11310 11311 11312 11313 11314 11315 11316 11317 11318 11319 11320 11321 11322 11323 11324 11325 11326 11327 11328 11329 11330 11331 11332 11333 11334 11335 11336 11337 11338 11339 11340 11341 11342 11343 11344 11345 11346 11347 11348 11349 11350 11351 11352 11353 11354 11355 11356 11357 11358 11359 11360 11361 11362 11363 11364 11365 11366 11367 11368 11369 11370 11371 11372 11373 11374 11375 11376 11377 11378 11379 11380 11381 11382 11383 11384 11385 11386 11387 11388 11389 11390 11391 11392 11393 11394 11395 11396 11397 11398 11399 11400 11401 11402 11403 11404 11405 11406 11407 11408 11409 11410 11411 11412 11413 11414 11415 11416 11417 11418 11419 11420 11421 11422 11423 11424 11425 11426 11427 11428 11429 11430 11431 11432 11433 11434 11435 11436 11437 11438 11439 11440 11441 11442 11443 11444 11445 11446 11447 11448 11449 11450 11451 11452 11453 11454 11455 11456 11457 11458 11459 11460 11461 11462 11463 11464 11465 11466 11467 11468 11469 11470 11471 11472 11473 11474 11475 11476 11477 11478 11479 11480 11481 11482 11483 11484 11485 11486 11487 11488 11489 11490 11491 11492 11493 11494 11495 11496 11497 11498 11499 11500 11501 11502 11503 11504 11505 11506 11507 11508 11509 11510 11511 11512 11513 11514 11515 11516 11517 11518 11519 11520 11521 11522 11523 11524 11525 11526 11527 11528 11529 11530 11531 11532 11533 11534 11535 11536 11537 11538 11539 11540 11541 11542 11543 11544 11545 11546 11547 11548 11549 11550 11551 11552 11553 11554 11555 11556 11557 11558 11559 11560 11561 11562 11563 11564 11565 11566 11567 11568 11569 11570 11571 11572 11573 11574 11575 11576 11577 11578 11579 11580 11581 11582 11583 11584 11585 11586 11587 11588 11589 11590 11591 11592 11593 11594 11595 11596 11597 11598 11599 11600 11601 11602 11603 11604 11605 11606 11607 11608 11609 11610 11611 11612 11613 11614 11615 11616 11617 11618 11619 11620 11621 11622 11623 11624 11625 11626 11627 11628 11629 11630 11631 11632 11633 11634 11635 11636 11637 11638 11639 11640 11641 11642 11643 11644 11645 11646 11647 11648 11649 11650 11651 11652 11653 11654 11655 11656 11657 11658 11659 11660 11661 11662 11663 11664 11665 11666 11667 11668 11669 11670 11671 11672 11673 11674 11675 11676 11677 11678 11679 11680 11681 11682 11683 11684 11685 11686 11687 11688 11689 11690 11691 11692 11693 11694 11695 11696 11697 11698 11699 11700 11701 11702 11703 11704 11705 11706 11707 11708 11709 11710 11711 11712 11713 11714 11715 11716 11717 11718 11719 11720 11721 11722 11723 11724 11725 11726 11727 11728 11729 11730 11731 11732 11733 11734 11735 11736 11737 11738 11739 11740 11741 11742 11743 11744 11745 11746 11747 11748 11749 11750 11751 11752 11753 11754 11755 11756 11757 11758 11759 11760 11761 11762 11763 11764 11765 11766 11767 11768 11769 11770 11771 11772 11773 11774 11775 11776 11777 11778 11779 11780 11781 11782 11783 11784 11785 11786 11787 11788 11789 11790 11791 11792 11793 11794 11795 11796 11797 11798 11799 11800 11801 11802 11803 11804 11805 11806 11807 11808 11809 11810 11811 11812 11813 11814 11815 11816 11817 11818 11819 11820 11821 11822 11823 11824 11825 11826 11827 11828 11829 11830 11831 11832 11833 11834 11835 11836 11837 11838 11839 11840 11841 11842 11843 11844 11845 11846 11847 11848 11849 11850 11851 11852 11853 11854 11855 11856 11857 11858 11859 11860 11861 11862 11863 11864 11865 11866 11867 11868 11869 11870 11871 11872 11873 11874 11875 11876 11877 11878 11879 11880 11881 11882 11883 11884 11885 11886 11887 11888 11889 11890 11891 11892 11893 11894 11895 11896 11897 11898 11899 11900 11901 11902 11903 11904 11905 11906 11907 11908 11909 11910 11911 11912 11913 11914 11915 11916 11917 11918 11919 11920 11921 11922 11923 11924 11925 11926 11927 11928 11929 11930 11931 11932 11933 11934 11935 11936 11937 11938 11939 11940 11941 11942 11943 11944 11945 11946 11947 11948 11949 11950 11951 11952 11953 11954 11955 11956 11957 11958 11959 11960 11961 11962 11963 11964 11965 11966 11967 11968 11969 11970 11971 11972 11973 11974 11975 11976 11977 11978 11979 11980 11981 11982 11983 11984 11985 11986 11987 11988 11989 11990 11991 11992 11993 11994 11995 11996 11997 11998 11999 12000 12001 12002 12003 12004 12005 12006 12007 12008 12009 12010 12011 12012 12013 12014 12015 12016 12017 12018 12019 12020 12021 12022 12023 12024 12025 12026 12027 12028 12029 12030 12031 12032 12033 12034 12035 12036 12037 12038 12039 12040 12041 12042 12043 12044 12045 12046 12047 12048 12049 12050 12051 12052 12053 12054 12055 12056 12057 12058 12059 12060 12061 12062 12063 12064 12065 12066 12067 12068 12069 12070 12071 12072 12073 12074 12075 12076 12077 12078 12079 12080 12081 12082 12083 12084 12085 12086 12087 12088 12089 12090 12091 12092 12093 12094 12095 12096 12097 12098 12099 12100 12101 12102 12103 12104 12105 12106 12107 12108 12109 12110 12111 12112 12113 12114 12115 12116 12117 12118 12119 12120 12121 12122 12123 12124 12125 12126 12127 12128 12129 12130 12131 12132 12133 12134 12135 12136 12137 12138 12139 12140 12141 12142 12143 12144 12145 12146 12147 12148 12149 12150 12151 12152 12153 12154 12155 12156 12157 12158 12159 12160 12161 12162 12163 12164 12165 12166 12167 12168 12169 12170 12171 12172 12173 12174 12175 12176 12177 12178 12179 12180 12181 12182 12183 12184 12185 12186 12187 12188 12189 12190 12191 12192 12193 12194 12195 12196 12197 12198 12199 12200 12201 12202 12203 12204 12205 12206 12207 12208 12209 12210 12211 12212 12213 12214 12215 12216 12217 12218 12219 12220 12221 12222 12223 12224 12225 12226 12227 12228 12229 12230 12231 12232 12233 12234 12235 12236 12237 12238 12239 12240 12241 12242 12243 12244 12245 12246 12247 12248 12249 12250 12251 12252 12253 12254 12255 12256 12257 12258 12259 12260 12261 12262 12263 12264 12265 12266 12267 12268 12269 12270 12271 12272 12273 12274 12275 12276 12277 12278 12279 12280 12281 12282 12283 12284 12285 12286 12287 12288 12289 12290 12291 12292 12293 12294 12295 12296 12297 12298 12299 12300 12301 12302 12303 12304 12305 12306 12307 12308 12309 12310 12311 12312 12313 12314 12315 12316 12317 12318 12319 12320 12321 12322 12323 12324 12325 12326 12327 12328 12329 12330 12331 12332 12333 12334 12335 12336 12337 12338 12339 12340 12341 12342 12343 12344 12345 12346 12347 12348 12349 12350 12351 12352 12353 12354 12355 12356 12357 12358 12359 12360 12361 12362 12363 12364 12365 12366 12367 12368 12369 12370 12371 12372 12373 12374 12375 12376 12377 12378 12379 12380 12381 12382 12383 12384 12385 12386 12387 12388 12389 12390 12391 12392 12393 12394 12395 12396 12397 12398 12399 12400 12401 12402 12403 12404 12405 12406 12407 12408 12409 12410 12411 12412 12413 12414 12415 12416 12417 12418 12419 12420 12421 12422 12423 12424 12425 12426 12427 12428 12429 12430 12431 12432 12433 12434 12435 12436 12437 12438 12439 12440 12441 12442 12443 12444 12445 12446 12447 12448 12449 12450 12451 12452 12453 12454 12455 12456 12457 12458 12459 12460 12461 12462 12463 12464 12465 12466 12467 12468 12469 12470 12471 12472 12473 12474 12475 12476 12477 12478 12479 12480 12481 12482 12483 12484 12485 12486 12487 12488 12489 12490 12491 12492 12493 12494 12495 12496 12497 12498 12499 12500 12501 12502 12503 12504 12505 12506 12507 12508 12509 12510 12511 12512 12513 12514 12515 12516 12517 12518 12519 12520 12521 12522 12523 12524 12525 12526 12527 12528 12529 12530 12531 12532 12533 12534 12535 12536 12537 12538 12539 12540 12541 12542 12543 12544 12545 12546 12547 12548 12549 12550 12551 12552 12553 12554 12555 12556 12557 12558 12559 12560 12561 12562 12563 12564 12565 12566 12567 12568 12569 12570 12571 12572 12573 12574 12575 12576 12577 12578 12579 12580 12581 12582 12583 12584 12585 12586 12587 12588 12589 12590 12591 12592 12593 12594 12595 12596 12597 12598 12599 12600 12601 12602 12603 12604 12605 12606 12607 12608 12609 12610 12611 12612 12613 12614 12615 12616 12617 12618 12619 12620 12621 12622 12623 12624 12625 12626 12627 12628 12629 12630 12631 12632 12633 12634 12635 12636 12637 12638 12639 12640 12641 12642 12643 12644 12645 12646 12647 12648 12649 12650 12651 12652 12653 12654 12655 12656 12657 12658 12659 12660 12661 12662 12663 12664 12665 12666 12667 12668 12669 12670 12671 12672 12673 12674 12675 12676 12677 12678 12679 12680 12681 12682 12683 12684 12685 12686 12687 12688 12689 12690 12691 12692 12693 12694 12695 12696 12697 12698 12699 12700 12701 12702 12703 12704 12705 12706 12707 12708 12709 12710 12711 12712 12713 12714 12715 12716 12717 12718 12719 12720 12721 12722 12723 12724 12725 12726 12727 12728 12729 12730 12731 12732 12733 12734 12735 12736 12737 12738 12739 12740 12741 12742 12743 12744 12745 12746 12747 12748 12749 12750 12751 12752 12753 12754 12755 12756 12757 12758 12759 12760 12761 12762 12763 12764 12765 12766 12767 12768 12769 12770 12771 12772 12773 12774 12775 12776 12777 12778 12779 12780 12781 12782 12783 12784 12785 12786 12787 12788 12789 12790 12791 12792 12793 12794 12795 12796 12797 12798 12799 12800 12801 12802 12803 12804 12805 12806 12807 12808 12809 12810 12811 12812 12813 12814 12815 12816 12817 12818 12819 12820 12821 12822 12823 12824 12825 12826 12827 12828 12829 12830 12831 12832 12833 12834 12835 12836 12837 12838 12839 12840 12841 12842 12843 12844 12845 12846 12847 12848 12849 12850 12851 12852 12853 12854 12855 12856 12857 12858 12859 12860 12861 12862 12863 12864 12865 12866 12867 12868 12869 12870 12871 12872 12873 12874 12875 12876 12877 12878 12879 12880 12881 12882 12883 12884 12885 12886 12887 12888 12889 12890 12891 12892 12893 12894 12895 12896 12897 12898 12899 12900 12901 12902 12903 12904 12905 12906 12907 12908 12909 12910 12911 12912 12913 12914 12915 12916 12917 12918 12919 12920 12921 12922 12923 12924 12925 12926 12927 12928 12929 12930 12931 12932 12933 12934 12935 12936 12937 12938 12939 12940 12941 12942 12943 12944 12945 12946 12947 12948 12949 12950 12951 12952 12953 12954 12955 12956 12957 12958 12959 12960 12961 12962 12963 12964 12965 12966 12967 12968 12969 12970 12971 12972 12973 12974 12975 12976 12977 12978 12979 12980 12981 12982 12983 12984 12985 12986 12987 12988 12989 12990 12991 12992 12993 12994 12995 12996 12997 12998 12999 13000 13001 13002 13003 13004 13005 13006 13007 13008 13009 13010 13011 13012 13013 13014 13015 13016 13017 13018 13019 13020 13021 13022 13023 13024 13025 13026 13027 13028 13029 13030 13031 13032 13033 13034 13035 13036 13037 13038 13039 13040 13041 13042 13043 13044 13045 13046 13047 13048 13049 13050 13051 13052 13053 13054 13055 13056 13057 13058 13059 13060 13061 13062 13063 13064 13065 13066 13067 13068 13069 13070 13071 13072 13073 13074 13075 13076 13077 13078 13079 13080 13081 13082 13083 13084 13085 13086 13087 13088 13089 13090 13091 13092 13093 13094 13095 13096 13097 13098 13099 13100 13101 13102 13103 13104 13105 13106 13107 13108 13109 13110 13111 13112 13113 13114 13115 13116 13117 13118 13119 13120 13121 13122 13123 13124 13125 13126 13127 13128 13129 13130 13131 13132 13133 13134 13135 13136 13137 13138 13139 13140 13141 13142 13143 13144 13145 13146 13147 13148 13149 13150 13151 13152 13153 13154 13155 13156 13157 13158 13159 13160 13161 13162 13163 13164 13165 13166 13167 13168 13169 13170 13171 13172 13173 13174 13175 13176 13177 13178 13179 13180 13181 13182 13183 13184 13185 13186 13187 13188 13189 13190 13191 13192 13193 13194 13195 13196 13197 13198 13199 13200 13201 13202 13203 13204 13205 13206 13207 13208 13209 13210 13211 13212 13213 13214 13215 13216 13217 13218 13219 13220 13221 13222 13223 13224 13225 13226 13227 13228 13229 13230 13231 13232 13233 13234 13235 13236 13237 13238 13239 13240 13241 13242 13243 13244 13245 13246 13247 13248 13249 13250 13251 13252 13253 13254 13255 13256 13257 13258 13259 13260 13261 13262 13263 13264 13265 13266 13267 13268 13269 13270 13271 13272 13273 13274 13275 13276 13277 13278 13279 13280 13281 13282 13283 13284 13285 13286 13287 13288 13289 13290 13291 13292 13293 13294 13295 13296 13297 13298 13299 13300 13301 13302 13303 13304 13305 13306 13307 13308 13309 13310 13311 13312 13313 13314 13315 13316 13317 13318 13319 13320 13321 13322 13323 13324 13325 13326 13327 13328 13329 13330 13331 13332 13333 13334 13335 13336 13337 13338 13339 13340 13341 13342 13343 13344 13345 13346 13347 13348 13349 13350 13351 13352 13353 13354 13355 13356 13357 13358 13359 13360 13361 13362 13363 13364 13365 13366 13367 13368 13369 13370 13371 13372 13373 13374 13375 13376 13377 13378 13379 13380 13381 13382 13383 13384 13385 13386 13387 13388 13389 13390 13391 13392 13393 13394 13395 13396 13397 13398 13399 13400 13401 13402 13403 13404 13405 13406 13407 13408 13409 13410 13411 13412 13413 13414 13415 13416 13417 13418 13419 13420 13421 13422 13423 13424 13425 13426 13427 13428 13429 13430 13431 13432 13433 13434 13435 13436 13437 13438 13439 13440 13441 13442 13443 13444 13445 13446 13447 13448 13449 13450 13451 13452 13453 13454 13455 13456 13457 13458 13459 13460 13461 13462 13463 13464 13465 13466 13467 13468 13469 13470 13471 13472 13473 13474 13475 13476 13477 13478 13479 13480 13481 13482 13483 13484 13485 13486 13487 13488 13489 13490 13491 13492 13493 13494 13495 13496 13497 13498 13499 13500 13501 13502 13503 13504 13505 13506 13507 13508 13509 13510 13511 13512 13513 13514 13515 13516 13517 13518 13519 13520 13521 13522 13523 13524 13525 13526 13527 13528 13529 13530 13531 13532 13533 13534 13535 13536 13537 13538 13539 13540 13541 13542 13543 13544 13545 13546 13547 13548 13549 13550 13551 13552 13553 13554 13555 13556 13557 13558 13559 13560 13561 13562 13563 13564 13565 13566 13567 13568 13569 13570 13571 13572 13573 13574 13575 13576 13577 13578 13579 13580 13581 13582 13583 13584 13585 13586 13587 13588 13589 13590 13591 13592 13593 13594 13595 13596 13597 13598 13599 13600 13601 13602 13603 13604 13605 13606 13607 13608 13609 13610 13611 13612 13613 13614 13615 13616 13617 13618 13619 13620 13621 13622 13623 13624 13625 13626 13627 13628 13629 13630 13631 13632 13633 13634 13635 13636 13637 13638 13639 13640 13641 13642 13643 13644 13645 13646 13647 13648 13649 13650 13651 13652 13653 13654 13655 13656 13657 13658 13659 13660 13661 13662 13663 13664 13665 13666 13667 13668 13669 13670 13671 13672 13673 13674 13675 13676 13677 13678 13679 13680 13681 13682 13683 13684 13685 13686 13687 13688 13689 13690 13691 13692 13693 13694 13695 13696 13697 13698 13699 13700 13701 13702 13703 13704 13705 13706 13707 13708 13709 13710 13711 13712 13713 13714 13715 13716 13717 13718 13719 13720 13721 13722 13723 13724 13725 13726 13727 13728 13729 13730 13731 13732 13733 13734 13735 13736 13737 13738 13739 13740 13741 13742 13743 13744 13745 13746 13747 13748 13749 13750 13751 13752 13753 13754 13755 13756 13757 13758 13759 13760 13761 13762 13763 13764 13765 13766 13767 13768 13769 13770 13771 13772 13773 13774 13775 13776 13777 13778 13779 13780 13781 13782 13783 13784 13785 13786 13787 13788 13789 13790 13791 13792 13793 13794 13795 13796 13797 13798 13799 13800 13801 13802 13803 13804 13805 13806 13807 13808 13809 13810 13811 13812 13813 13814 13815 13816 13817 13818 13819 13820 13821 13822 13823 13824 13825 13826 13827 13828 13829 13830 13831 13832 13833 13834 13835 13836 13837 13838 13839 13840 13841 13842 13843 13844 13845 13846 13847 13848 13849 13850 13851 13852 13853 13854 13855 13856 13857 13858 13859 13860 13861 13862 13863 13864 13865 13866 13867 13868 13869 13870 13871 13872 13873 13874 13875 13876 13877 13878 13879 13880 13881 13882 13883 13884 13885 13886 13887 13888 13889 13890 13891 13892 13893 13894 13895 13896 13897 13898 13899 13900 13901 13902 13903 13904 13905 13906 13907 13908 13909 13910 13911 13912 13913 13914 13915 13916 13917 13918 13919 13920 13921 13922 13923 13924 13925 13926 13927 13928 13929 13930 13931 13932 13933 13934 13935 13936 13937 13938 13939 13940 13941 13942 13943 13944 13945 13946 13947 13948 13949 13950 13951 13952 13953 13954 13955 13956 13957 13958 13959 13960 13961 13962 13963 13964 13965 13966 13967 13968 13969 13970 13971 13972 13973 13974 13975 13976 13977 13978 13979 13980 13981 13982 13983 13984 13985 13986 13987 13988 13989 13990 13991 13992 13993 13994 13995 13996 13997 13998 13999 14000 14001 14002 14003 14004 14005 14006 14007 14008 14009 14010 14011 14012 14013 14014 14015 14016 14017 14018 14019 14020 14021 14022 14023 14024 14025 14026 14027 14028 14029 14030 14031 14032 14033 14034 14035 14036 14037 14038 14039 14040 14041 14042 14043 14044 14045 14046 14047 14048 14049 14050 14051 14052 14053 14054 14055 14056 14057 14058 14059 14060 14061 14062 14063 14064 14065 14066 14067 14068 14069 14070 14071 14072 14073 14074 14075 14076 14077 14078 14079 14080 14081 14082 14083 14084 14085 14086 14087 14088 14089 14090 14091 14092 14093 14094 14095 14096 14097 14098 14099 14100 14101 14102 14103 14104 14105 14106 14107 14108 14109 14110 14111 14112 14113 14114 14115 14116 14117 14118 14119 14120 14121 14122 14123 14124 14125 14126 14127 14128 14129 14130 14131 14132 14133 14134 14135 14136 14137 14138 14139 14140 14141 14142 14143 14144 14145 14146 14147 14148 14149 14150 14151 14152 14153 14154 14155 14156 14157 14158 14159 14160 14161 14162 14163 14164 14165 14166 14167 14168 14169 14170 14171 14172 14173 14174 14175 14176 14177 14178 14179 14180 14181 14182 14183 14184 14185 14186 14187 14188 14189 14190 14191 14192 14193 14194 14195 14196 14197 14198 14199 14200 14201 14202 14203 14204 14205 14206 14207 14208 14209 14210 14211 14212 14213 14214 14215 14216 14217 14218 14219 14220 14221 14222 14223 14224 14225 14226 14227 14228 14229 14230 14231 14232 14233 14234 14235 14236 14237 14238 14239 14240 14241 14242 14243 14244 14245 14246 14247 14248 14249 14250 14251 14252 14253 14254 14255 14256 14257 14258 14259 14260 14261 14262 14263 14264 14265 14266 14267 14268 14269 14270 14271 14272 14273 14274 14275 14276 14277 14278 14279 14280 14281 14282 14283 14284 14285 14286 14287 14288 14289 14290 14291 14292 14293 14294 14295 14296 14297 14298 14299 14300 14301 14302 14303 14304 14305 14306 14307 14308 14309 14310 14311 14312 14313 14314 14315 14316 14317 14318 14319 14320 14321 14322 14323 14324 14325 14326 14327 14328 14329 14330 14331 14332 14333 14334 14335 14336 14337 14338 14339 14340 14341 14342 14343 14344 14345 14346 14347 14348 14349 14350 14351 14352 14353 14354 14355 14356 14357 14358 14359 14360 14361 14362 14363 14364 14365 14366 14367 14368 14369 14370 14371 14372 14373 14374 14375 14376 14377 14378 14379 14380 14381 14382 14383 14384 14385 14386 14387 14388 14389 14390 14391 14392 14393 14394 14395 14396 14397 14398 14399 14400 14401 14402 14403 14404 14405 14406 14407 14408 14409 14410 14411 14412 14413 14414 14415 14416 14417 14418 14419 14420 14421 14422 14423 14424 14425 14426 14427 14428 14429 14430 14431 14432 14433 14434 14435 14436 14437 14438 14439 14440 14441 14442 14443 14444 14445 14446 14447 14448 14449 14450 14451 14452 14453 14454 14455 14456 14457 14458 14459 14460 14461 14462 14463 14464 14465 14466 14467 14468 14469 14470 14471 14472 14473 14474 14475 14476 14477 14478 14479 14480 14481 14482 14483 14484 14485 14486 14487 14488 14489 14490 14491 14492 14493 14494 14495 14496 14497 14498 14499 14500 14501 14502 14503 14504 14505 14506 14507 14508 14509 14510 14511 14512 14513 14514 14515 14516 14517 14518 14519 14520 14521 14522 14523 14524 14525 14526 14527 14528 14529 14530 14531 14532 14533 14534 14535 14536 14537 14538 14539 14540 14541 14542 14543 14544 14545 14546 14547 14548 14549 14550 14551 14552 14553 14554 14555 14556 14557 14558 14559 14560 14561 14562 14563 14564 14565 14566 14567 14568 14569 14570 14571 14572 14573 14574 14575 14576 14577 14578 14579 14580 14581 14582 14583 14584 14585 14586 14587 14588 14589 14590 14591 14592 14593 14594 14595 14596 14597 14598 14599 14600 14601 14602 14603 14604 14605 14606 14607 14608 14609 14610 14611 14612 14613 14614 14615 14616 14617 14618 14619 14620 14621 14622 14623 14624 14625 14626 14627 14628 14629 14630 14631 14632 14633 14634 14635 14636 14637 14638 14639 14640 14641 14642 14643 14644 14645 14646 14647 14648 14649 14650 14651 14652 14653 14654 14655 14656 14657 14658 14659 14660 14661 14662 14663 14664 14665 14666 14667 14668 14669 14670 14671 14672 14673 14674 14675 14676 14677 14678 14679 14680 14681 14682 14683 14684 14685 14686 14687 14688 14689 14690 14691 14692 14693 14694 14695 14696 14697 14698 14699 14700 14701 14702 14703 14704 14705 14706 14707 14708 14709 14710 14711 14712 14713 14714 14715 14716 14717 14718 14719 14720 14721 14722 14723 14724 14725 14726 14727 14728 14729 14730 14731 14732 14733 14734 14735 14736 14737 14738 14739 14740 14741 14742 14743 14744 14745 14746 14747 14748 14749 14750 14751 14752 14753 14754 14755 14756 14757 14758 14759 14760 14761 14762 14763 14764 14765 14766 14767 14768 14769 14770 14771 14772 14773 14774 14775 14776 14777 14778 14779 14780 14781 14782 14783 14784 14785 14786 14787 14788 14789 14790 14791 14792 14793 14794 14795 14796 14797 14798 14799 14800 14801 14802 14803 14804 14805 14806 14807 14808 14809 14810 14811 14812 14813 14814 14815 14816 14817 14818 14819 14820 14821 14822 14823 14824 14825 14826 14827 14828 14829 14830 14831 14832 14833 14834 14835 14836 14837 14838 14839 14840 14841 14842 14843 14844 14845 14846 14847 14848 14849 14850 14851 14852 14853 14854 14855 14856 14857 14858 14859 14860 14861 14862 14863 14864 14865 14866 14867 14868 14869 14870 14871 14872 14873 14874 14875 14876 14877 14878 14879 14880 14881 14882 14883 14884 14885 14886 14887 14888 14889 14890 14891 14892 14893 14894 14895 14896 14897 14898 14899 14900 14901 14902 14903 14904 14905 14906 14907 14908 14909 14910 14911 14912 14913 14914 14915 14916 14917 14918 14919 14920 14921 14922 14923 14924 14925 14926 14927 14928 14929 14930 14931 14932 14933 14934 14935 14936 14937 14938 14939 14940 14941 14942 14943 14944 14945 14946 14947 14948 14949 14950 14951 14952 14953 14954 14955 14956 14957 14958 14959 14960 14961 14962 14963 14964 14965 14966 14967 14968 14969 14970 14971 14972 14973 14974 14975 14976 14977 14978 14979 14980 14981 14982 14983 14984 14985 14986 14987 14988 14989 14990 14991 14992 14993 14994 14995 14996 14997 14998 14999 15000 15001 15002 15003 15004 15005 15006 15007 15008 15009 15010 15011 15012 15013 15014 15015 15016 15017 15018 15019 15020 15021 15022 15023 15024 15025 15026 15027 15028 15029 15030 15031 15032 15033 15034 15035 15036 15037 15038 15039 15040 15041 15042 15043 15044 15045 15046 15047 15048 15049 15050 15051 15052 15053 15054 15055 15056 15057 15058 15059 15060 15061 15062 15063 15064 15065 15066 15067 15068 15069 15070 15071 15072 15073 15074 15075 15076 15077 15078 15079 15080 15081 15082 15083 15084 15085 15086 15087 15088 15089 15090 15091 15092 15093 15094 15095 15096 15097 15098 15099 15100 15101 15102 15103 15104 15105 15106 15107 15108 15109 15110 15111 15112 15113 15114 15115 15116 15117 15118 15119 15120 15121 15122 15123 15124 15125 15126 15127 15128 15129 15130 15131 15132 15133 15134 15135 15136 15137 15138 15139 15140 15141 15142 15143 15144 15145 15146 15147 15148 15149 15150 15151 15152 15153 15154 15155 15156 15157 15158 15159 15160 15161 15162 15163 15164 15165 15166 15167 15168 15169 15170 15171 15172 15173 15174 15175 15176 15177 15178 15179 15180 15181 15182 15183 15184 15185 15186 15187 15188 15189 15190 15191 15192 15193 15194 15195 15196 15197 15198 15199 15200 15201 15202 15203 15204 15205 15206 15207 15208 15209 15210 15211 15212 15213 15214 15215 15216 15217 15218 15219 15220 15221 15222 15223 15224 15225 15226 15227 15228 15229 15230 15231 15232 15233 15234 15235 15236 15237 15238 15239 15240 15241 15242 15243 15244 15245 15246 15247 15248 15249 15250 15251 15252 15253 15254 15255 15256 15257 15258 15259 15260 15261 15262 15263 15264 15265 15266 15267 15268 15269 15270 15271 15272 15273 15274 15275 15276 15277 15278 15279 15280 15281 15282 15283 15284 15285 15286 15287 15288 15289 15290 15291 15292 15293 15294 15295 15296 15297 15298 15299 15300 15301 15302 15303 15304 15305 15306 15307 15308 15309 15310 15311 15312 15313 15314 15315 15316 15317 15318 15319 15320 15321 15322 15323 15324 15325 15326 15327 15328 15329 15330 15331 15332 15333 15334 15335 15336 15337 15338 15339 15340 15341 15342 15343 15344 15345 15346 15347 15348 15349 15350 15351 15352 15353 15354 15355 15356 15357 15358 15359 15360 15361 15362 15363 15364 15365 15366 15367 15368 15369 15370 15371 15372 15373 15374 15375 15376 15377 15378 15379 15380 15381 15382 15383 15384 15385 15386 15387 15388 15389 15390 15391 15392 15393 15394 15395 15396 15397 15398 15399 15400 15401 15402 15403 15404 15405 15406 15407 15408 15409 15410 15411 15412 15413 15414 15415 15416 15417 15418 15419 15420 15421 15422 15423 15424 15425 15426 15427 15428 15429 15430 15431 15432 15433 15434 15435 15436 15437 15438 15439 15440 15441 15442 15443 15444 15445 15446 15447 15448 15449 15450 15451 15452 15453 15454 15455 15456 15457 15458 15459 15460 15461 15462 15463 15464 15465 15466 15467 15468 15469 15470 15471 15472 15473 15474 15475 15476 15477 15478 15479 15480 15481 15482 15483 15484 15485 15486 15487 15488 15489 15490 15491 15492 15493 15494 15495 15496 15497 15498 15499 15500 15501 15502 15503 15504 15505 15506 15507 15508 15509 15510 15511 15512 15513 15514 15515 15516 15517 15518 15519 15520 15521 15522 15523 15524 15525 15526 15527 15528 15529 15530 15531 15532 15533 15534 15535 15536 15537 15538 15539 15540 15541 15542 15543 15544 15545 15546 15547 15548 15549 15550 15551 15552 15553 15554 15555 15556 15557 15558 15559 15560 15561 15562 15563 15564 15565 15566 15567 15568 15569 15570 15571 15572 15573 15574 15575 15576 15577 15578 15579 15580 15581 15582 15583 15584 15585 15586 15587 15588 15589 15590 15591 15592 15593 15594 15595 15596 15597 15598 15599 15600 15601 15602 15603 15604 15605 15606 15607 15608 15609 15610 15611 15612 15613 15614 15615 15616 15617 15618 15619 15620 15621 15622 15623 15624 15625 15626 15627 15628 15629 15630 15631 15632 15633 15634 15635 15636 15637 15638 15639 15640 15641 15642 15643 15644 15645 15646 15647 15648 15649 15650 15651 15652 15653 15654 15655 15656 15657 15658 15659 15660 15661 15662 15663 15664 15665 15666 15667 15668 15669 15670 15671 15672 15673 15674 15675 15676 15677 15678 15679 15680 15681 15682 15683 15684 15685 15686 15687 15688 15689 15690 15691 15692 15693 15694 15695 15696 15697 15698 15699 15700 15701 15702 15703 15704 15705 15706 15707 15708 15709 15710 15711 15712 15713 15714 15715 15716 15717 15718 15719 15720 15721 15722 15723 15724 15725 15726 15727 15728 15729 15730 15731 15732 15733 15734 15735 15736 15737 15738 15739 15740 15741 15742 15743 15744 15745 15746 15747 15748 15749 15750 15751 15752 15753 15754 15755 15756 15757 15758 15759 15760 15761 15762 15763 15764 15765 15766 15767 15768 15769 15770 15771 15772 15773 15774 15775 15776 15777 15778 15779 15780 15781 15782 15783 15784 15785 15786 15787 15788 15789 15790 15791 15792 15793 15794 15795 15796 15797 15798 15799 15800 15801 15802 15803 15804 15805 15806 15807 15808 15809 15810 15811 15812 15813 15814 15815 15816 15817 15818 15819 15820 15821 15822 15823 15824 15825 15826 15827 15828 15829 15830 15831 15832 15833 15834 15835 15836 15837 15838 15839 15840 15841 15842 15843 15844 15845 15846 15847 15848 15849 15850 15851 15852 15853 15854 15855 15856 15857 15858 15859 15860 15861 15862 15863 15864 15865 15866 15867 15868 15869 15870 15871 15872 15873 15874 15875 15876 15877 15878 15879 15880 15881 15882 15883 15884 15885 15886 15887 15888 15889 15890 15891 15892 15893 15894 15895 15896 15897 15898 15899 15900 15901 15902 15903 15904 15905 15906 15907 15908 15909 15910 15911 15912 15913 15914 15915 15916 15917 15918 15919 15920 15921 15922 15923 15924 15925 15926 15927 15928 15929 15930 15931 15932 15933 15934 15935 15936 15937 15938 15939 15940 15941 15942 15943 15944 15945 15946 15947 15948 15949 15950 15951 15952 15953 15954 15955 15956 15957 15958 15959 15960 15961 15962 15963 15964 15965 15966 15967 15968 15969 15970 15971 15972 15973 15974 15975 15976 15977 15978 15979 15980 15981 15982 15983 15984 15985 15986 15987 15988 15989 15990 15991 15992 15993 15994 15995 15996 15997 15998 15999 16000 16001 16002 16003 16004 16005 16006 16007 16008 16009 16010 16011 16012 16013 16014 16015 16016 16017 16018 16019 16020 16021 16022 16023 16024 16025 16026 16027 16028 16029 16030 16031 16032 16033 16034 16035 16036 16037 16038 16039 16040 16041 16042 16043 16044 16045 16046 16047 16048 16049 16050 16051 16052 16053 16054 16055 16056 16057 16058 16059 16060 16061 16062 16063 16064 16065 16066 16067 16068 16069 16070 16071 16072 16073 16074 16075 16076 16077 16078 16079 16080 16081 16082 16083 16084 16085 16086 16087 16088 16089 16090 16091 16092 16093 16094 16095 16096 16097 16098 16099 16100 16101 16102 16103 16104 16105 16106 16107 16108 16109 16110 16111 16112 16113 16114 16115 16116 16117 16118 16119 16120 16121 16122 16123 16124 16125 16126 16127 16128 16129 16130 16131 16132 16133 16134 16135 16136 16137 16138 16139 16140 16141 16142 16143 16144 16145 16146 16147 16148 16149 16150 16151 16152 16153 16154 16155 16156 16157 16158 16159 16160 16161 16162 16163 16164 16165 16166 16167 16168 16169 16170 16171 16172 16173 16174 16175 16176 16177 16178 16179 16180 16181 16182 16183 16184 16185 16186 16187 16188 16189 16190 16191 16192 16193 16194 16195 16196 16197 16198 16199 16200 16201 16202 16203 16204 16205 16206 16207 16208 16209 16210 16211 16212 16213 16214 16215 16216 16217 16218 16219 16220 16221 16222 16223 16224 16225 16226 16227 16228 16229 16230 16231 16232 16233 16234 16235 16236 16237 16238 16239 16240 16241 16242 16243 16244 16245 16246 16247 16248 16249 16250 16251 16252 16253 16254 16255 16256 16257 16258 16259 16260 16261 16262 16263 16264 16265 16266 16267 16268 16269 16270 16271 16272 16273 16274 16275 16276 16277 16278 16279 16280 16281 16282 16283 16284 16285 16286 16287 16288 16289 16290 16291 16292 16293 16294 16295 16296 16297 16298 16299 16300 16301 16302 16303 16304 16305 16306 16307 16308 16309 16310 16311 16312 16313 16314 16315 16316 16317 16318 16319 16320 16321 16322 16323 16324 16325 16326 16327 16328 16329 16330 16331 16332 16333 16334 16335 16336 16337 16338 16339 16340 16341 16342 16343 16344 16345 16346 16347 16348 16349 16350 16351 16352 16353 16354 16355 16356 16357 16358 16359 16360 16361 16362 16363 16364 16365 16366 16367 16368 16369 16370 16371 16372 16373 16374 16375 16376 16377 16378 16379 16380 16381 16382 16383 16384 16385 16386 16387 16388 16389 16390 16391 16392 16393 16394 16395 16396 16397 16398 16399 16400 16401 16402 16403 16404 16405 16406 16407 16408 16409 16410 16411 16412 16413 16414 16415 16416 16417 16418 16419 16420 16421 16422 16423 16424 16425 16426 16427 16428 16429 16430 16431 16432 16433 16434 16435 16436 16437 16438 16439 16440 16441 16442 16443 16444 16445 16446 16447 16448 16449 16450 16451 16452 16453 16454 16455 16456 16457 16458 16459 16460 16461 16462 16463 16464 16465 16466 16467 16468 16469 16470 16471 16472 16473 16474 16475 16476 16477 16478 16479 16480 16481 16482 16483 16484 16485 16486 16487 16488 16489 16490 16491 16492 16493 16494 16495 16496 16497 16498 16499 16500 16501 16502 16503 16504 16505 16506 16507 16508 16509 16510 16511 16512 16513 16514 16515 16516 16517 16518 16519 16520 16521 16522 16523 16524 16525 16526 16527 16528 16529 16530 16531 16532 16533 16534 16535 16536 16537 16538 16539 16540 16541 16542 16543 16544 16545 16546 16547 16548 16549 16550 16551 16552 16553 16554 16555 16556 16557 16558 16559 16560 16561 16562 16563 16564 16565 16566 16567 16568 16569 16570 16571 16572 16573 16574 16575 16576 16577 16578 16579 16580 16581 16582 16583 16584 16585 16586 16587 16588 16589 16590 16591 16592 16593 16594 16595 16596 16597 16598 16599 16600 16601 16602 16603 16604 16605 16606 16607 16608 16609 16610 16611 16612 16613 16614 16615 16616 16617 16618 16619 16620 16621 16622 16623 16624 16625 16626 16627 16628 16629 16630 16631 16632 16633 16634 16635 16636 16637 16638 16639 16640 16641 16642 16643 16644 16645 16646 16647 16648 16649 16650 16651 16652 16653 16654 16655 16656 16657 16658 16659 16660 16661 16662 16663 16664 16665 16666 16667 16668 16669 16670 16671 16672 16673 16674 16675 16676 16677 16678 16679 16680 16681 16682 16683 16684 16685 16686 16687 16688 16689 16690 16691 16692 16693 16694 16695 16696 16697 16698 16699 16700 16701 16702 16703 16704 16705 16706 16707 16708 16709 16710 16711 16712 16713 16714 16715 16716 16717 16718 16719 16720 16721 16722 16723 16724 16725 16726 16727 16728 16729 16730 16731 16732 16733 16734 16735 16736 16737 16738 16739 16740 16741 16742 16743 16744 16745 16746 16747 16748 16749 16750 16751 16752 16753 16754 16755 16756 16757 16758 16759 16760 16761 16762 16763 16764 16765 16766 16767 16768 16769 16770 16771 16772 16773 16774 16775 16776 16777 16778 16779 16780 16781 16782 16783 16784 16785 16786 16787 16788 16789 16790 16791 16792 16793 16794 16795 16796 16797 16798 16799 16800 16801 16802 16803 16804 16805 16806 16807 16808 16809 16810 16811 16812 16813 16814 16815 16816 16817 16818 16819 16820 16821 16822 16823 16824 16825 16826 16827 16828 16829 16830 16831 16832 16833 16834 16835 16836 16837 16838 16839 16840 16841 16842 16843 16844 16845 16846 16847 16848 16849 16850 16851 16852 16853 16854 16855 16856 16857 16858 16859 16860 16861 16862 16863 16864 16865 16866 16867 16868 16869 16870 16871 16872 16873 16874 16875 16876 16877 16878 16879 16880 16881 16882 16883 16884 16885 16886 16887 16888 16889 16890 16891 16892 16893 16894 16895 16896 16897 16898 16899 16900 16901 16902 16903 16904 16905 16906 16907 16908 16909 16910 16911 16912 16913 16914 16915 16916 16917 16918 16919 16920 16921 16922 16923 16924 16925 16926 16927 16928 16929 16930 16931 16932 16933 16934 16935 16936 16937 16938 16939 16940 16941 16942 16943 16944 16945 16946 16947 16948 16949 16950 16951 16952 16953 16954 16955 16956 16957 16958 16959 16960 16961 16962 16963 16964 16965 16966 16967 16968 16969 16970 16971 16972 16973 16974 16975 16976 16977 16978 16979 16980 16981 16982 16983 16984 16985 16986 16987 16988 16989 16990 16991 16992 16993 16994 16995 16996 16997 16998 16999 17000 17001 17002 17003 17004 17005 17006 17007 17008 17009 17010 17011 17012 17013 17014 17015 17016 17017 17018 17019 17020 17021 17022 17023 17024 17025 17026 17027 17028 17029 17030 17031 17032 17033 17034 17035 17036 17037 17038 17039 17040 17041 17042 17043 17044 17045 17046 17047 17048 17049 17050 17051 17052 17053 17054 17055 17056 17057 17058 17059 17060 17061 17062 17063 17064 17065 17066 17067 17068 17069 17070 17071 17072 17073 17074 17075 17076 17077 17078 17079 17080 17081 17082 17083 17084 17085 17086 17087 17088 17089 17090 17091 17092 17093 17094 17095 17096 17097 17098 17099 17100 17101 17102 17103 17104 17105 17106 17107 17108 17109 17110 17111 17112 17113 17114 17115 17116 17117 17118 17119 17120 17121 17122 17123 17124 17125 17126 17127 17128 17129 17130 17131 17132 17133 17134 17135 17136 17137 17138 17139 17140 17141 17142 17143 17144 17145 17146 17147 17148 17149 17150 17151 17152 17153 17154 17155 17156 17157 17158 17159 17160 17161 17162 17163 17164 17165 17166 17167 17168 17169 17170 17171 17172 17173 17174 17175 17176 17177 17178 17179 17180 17181 17182 17183 17184 17185 17186 17187 17188 17189 17190 17191 17192 17193 17194 17195 17196 17197 17198 17199 17200 17201 17202 17203 17204 17205 17206 17207 17208 17209 17210 17211 17212 17213 17214 17215 17216 17217 17218 17219 17220 17221 17222 17223 17224 17225 17226 17227 17228 17229 17230 17231 17232 17233 17234 17235 17236 17237 17238 17239 17240 17241 17242 17243 17244 17245 17246 17247 17248 17249 17250 17251 17252 17253 17254 17255 17256 17257 17258 17259 17260 17261 17262 17263 17264 17265 17266 17267 17268 17269 17270 17271 17272 17273 17274 17275 17276 17277 17278 17279 17280 17281 17282 17283 17284 17285 17286 17287 17288 17289 17290 17291 17292 17293 17294 17295 17296 17297 17298 17299 17300 17301 17302 17303 17304 17305 17306 17307 17308 17309 17310 17311 17312 17313 17314 17315 17316 17317 17318 17319 17320 17321 17322 17323 17324 17325 17326 17327 17328 17329 17330 17331 17332 17333 17334 17335 17336 17337 17338 17339 17340 17341 17342 17343 17344 17345 17346 17347 17348 17349 17350 17351 17352 17353 17354 17355 17356 17357 17358 17359 17360 17361 17362 17363 17364 17365 17366 17367 17368 17369 17370 17371 17372 17373 17374 17375 17376 17377 17378 17379 17380 17381 17382 17383 17384 17385 17386 17387 17388 17389 17390 17391 17392 17393 17394 17395 17396 17397 17398 17399 17400 17401 17402 17403 17404 17405 17406 17407 17408 17409 17410 17411 17412 17413 17414 17415 17416 17417 17418 17419 17420 17421 17422 17423 17424 17425 17426 17427 17428 17429 17430 17431 17432 17433 17434 17435 17436 17437 17438 17439 17440 17441 17442 17443 17444 17445 17446 17447 17448 17449 17450 17451 17452 17453 17454 17455 17456 17457 17458 17459 17460 17461 17462 17463 17464 17465 17466 17467 17468 17469 17470 17471 17472 17473 17474 17475 17476 17477 17478 17479 17480 17481 17482 17483 17484 17485 17486 17487 17488 17489 17490 17491 17492 17493 17494 17495 17496 17497 17498 17499 17500 17501 17502 17503 17504 17505 17506 17507 17508 17509 17510 17511 17512 17513 17514 17515 17516 17517 17518 17519 17520 17521 17522 17523 17524 17525 17526 17527 17528 17529 17530 17531 17532 17533 17534 17535 17536 17537 17538 17539 17540 17541 17542 17543 17544 17545 17546 17547 17548 17549 17550 17551 17552 17553 17554 17555 17556 17557 17558 17559 17560 17561 17562 17563 17564 17565 17566 17567 17568 17569 17570 17571 17572 17573 17574 17575 17576 17577 17578 17579 17580 17581 17582 17583 17584 17585 17586 17587 17588 17589 17590 17591 17592 17593 17594 17595 17596 17597 17598 17599 17600 17601 17602 17603 17604 17605 17606 17607 17608 17609 17610 17611 17612 17613 17614 17615 17616 17617 17618 17619 17620 17621 17622 17623 17624 17625 17626 17627 17628 17629 17630 17631 17632 17633 17634 17635 17636 17637 17638 17639 17640 17641 17642 17643 17644 17645 17646 17647 17648 17649 17650 17651 17652 17653 17654 17655 17656 17657 17658 17659 17660 17661 17662 17663 17664 17665 17666 17667 17668 17669 17670 17671 17672 17673 17674 17675 17676 17677 17678 17679 17680 17681 17682 17683 17684 17685 17686 17687 17688 17689 17690 17691 17692 17693 17694 17695 17696 17697 17698 17699 17700 17701 17702 17703 17704 17705 17706 17707 17708 17709 17710 17711 17712 17713 17714 17715 17716 17717 17718 17719 17720 17721 17722 17723 17724 17725 17726 17727 17728 17729 17730 17731 17732 17733 17734 17735 17736 17737 17738 17739 17740 17741 17742 17743 17744 17745 17746 17747 17748 17749 17750 17751 17752 17753 17754 17755 17756 17757 17758 17759 17760 17761 17762 17763 17764 17765 17766 17767 17768 17769 17770 17771 17772 17773 17774 17775 17776 17777 17778 17779 17780 17781 17782 17783 17784 17785 17786 17787 17788 17789 17790 17791 17792 17793 17794 17795 17796 17797 17798 17799 17800 17801 17802 17803 17804 17805 17806 17807 17808 17809 17810 17811 17812 17813 17814 17815 17816 17817 17818 17819 17820 17821 17822 17823 17824 17825 17826 17827 17828 17829 17830 17831 17832 17833 17834 17835 17836 17837 17838 17839 17840 17841 17842 17843 17844 17845 17846 17847 17848 17849 17850 17851 17852 17853 17854 17855 17856 17857 17858 17859 17860 17861 17862 17863 17864 17865 17866 17867 17868 17869 17870 17871 17872 17873 17874 17875 17876 17877 17878 17879 17880 17881 17882 17883 17884 17885 17886 17887 17888 17889 17890 17891 17892 17893 17894 17895 17896 17897 17898 17899 17900 17901 17902 17903 17904 17905 17906 17907 17908 17909 17910 17911 17912 17913 17914 17915 17916 17917 17918 17919 17920 17921 17922 17923 17924 17925 17926 17927 17928 17929 17930 17931 17932 17933 17934 17935 17936 17937 17938 17939 17940 17941 17942 17943 17944 17945 17946 17947 17948 17949 17950 17951 17952 17953 17954 17955 17956 17957 17958 17959 17960 17961 17962 17963 17964 17965 17966 17967 17968 17969 17970 17971 17972 17973 17974 17975 17976 17977 17978 17979 17980 17981 17982 17983 17984 17985 17986 17987 17988 17989 17990 17991 17992 17993 17994 17995 17996 17997 17998 17999 18000 18001 18002 18003 18004 18005 18006 18007 18008 18009 18010 18011 18012 18013 18014 18015 18016 18017 18018 18019 18020 18021 18022 18023 18024 18025 18026 18027 18028 18029 18030 18031 18032 18033 18034 18035 18036 18037 18038 18039 18040 18041 18042 18043 18044 18045 18046 18047 18048 18049 18050 18051 18052 18053 18054 18055 18056 18057 18058 18059 18060 18061 18062 18063 18064 18065 18066 18067 18068 18069 18070 18071 18072 18073 18074 18075 18076 18077 18078 18079 18080 18081 18082 18083 18084 18085 18086 18087 18088 18089 18090 18091 18092 18093 18094 18095 18096 18097 18098 18099 18100 18101 18102 18103 18104 18105 18106 18107 18108 18109 18110 18111 18112 18113 18114 18115 18116 18117 18118 18119 18120 18121 18122 18123 18124 18125 18126 18127 18128 18129 18130 18131 18132 18133 18134 18135 18136 18137 18138 18139 18140 18141 18142 18143 18144 18145 18146 18147 18148 18149 18150 18151 18152 18153 18154 18155 18156 18157 18158 18159 18160 18161 18162 18163 18164 18165 18166 18167 18168 18169 18170 18171 18172 18173 18174 18175 18176 18177 18178 18179 18180 18181 18182 18183 18184 18185 18186 18187 18188 18189 18190 18191 18192 18193 18194 18195 18196 18197 18198 18199 18200 18201 18202 18203 18204 18205 18206 18207 18208 18209 18210 18211 18212 18213 18214 18215 18216 18217 18218 18219 18220 18221 18222 18223 18224 18225 18226 18227 18228 18229 18230 18231 18232 18233 18234 18235 18236 18237 18238 18239 18240 18241 18242 18243 18244 18245 18246 18247 18248 18249 18250 18251 18252 18253 18254 18255 18256 18257 18258 18259 18260 18261 18262 18263 18264 18265 18266 18267 18268 18269 18270 18271 18272 18273 18274 18275 18276 18277 18278 18279 18280 18281 18282 18283 18284 18285 18286 18287 18288 18289 18290 18291 18292 18293 18294 18295 18296 18297 18298 18299 18300 18301 18302 18303 18304 18305 18306 18307 18308 18309 18310 18311 18312 18313 18314 18315 18316 18317 18318 18319 18320 18321 18322 18323 18324 18325 18326 18327 18328 18329 18330 18331 18332 18333 18334 18335 18336 18337 18338 18339 18340 18341 18342 18343 18344 18345 18346 18347 18348 18349 18350 18351 18352 18353 18354 18355 18356 18357 18358 18359 18360 18361 18362 18363 18364 18365 18366 18367 18368 18369 18370 18371 18372 18373 18374 18375 18376 18377 18378 18379 18380 18381 18382 18383 18384 18385 18386 18387 18388 18389 18390 18391 18392 18393 18394 18395 18396 18397 18398 18399 18400 18401 18402 18403 18404 18405 18406 18407 18408 18409 18410 18411 18412 18413 18414 18415 18416 18417 18418 18419 18420 18421 18422 18423 18424 18425 18426 18427 18428 18429 18430 18431 18432 18433 18434 18435 18436 18437 18438 18439 18440 18441 18442 18443 18444 18445 18446 18447 18448 18449 18450 18451 18452 18453 18454 18455 18456 18457 18458 18459 18460 18461 18462 18463 18464 18465 18466 18467 18468 18469 18470 18471 18472 18473 18474 18475 18476 18477 18478 18479 18480 18481 18482 18483 18484 18485 18486 18487 18488 18489 18490 18491 18492 18493 18494 18495 18496 18497 18498 18499 18500 18501 18502 18503 18504 18505 18506 18507 18508 18509 18510 18511 18512 18513 18514 18515 18516 18517 18518 18519 18520 18521 18522 18523 18524 18525 18526 18527 18528 18529 18530 18531 18532 18533 18534 18535 18536 18537 18538 18539 18540 18541 18542 18543 18544 18545 18546 18547 18548 18549 18550 18551 18552 18553 18554 18555 18556 18557 18558 18559 18560 18561 18562 18563 18564 18565 18566 18567 18568 18569 18570 18571 18572 18573 18574 18575 18576 18577 18578 18579 18580 18581 18582 18583 18584 18585 18586 18587 18588 18589 18590 18591 18592 18593 18594 18595 18596 18597 18598 18599 18600 18601 18602 18603 18604 18605 18606 18607 18608 18609 18610 18611 18612 18613 18614 18615 18616 18617 18618 18619 18620 18621 18622 18623 18624 18625 18626 18627 18628 18629 18630 18631 18632 18633 18634 18635 18636 18637 18638 18639 18640 18641 18642 18643 18644 18645 18646 18647 18648 18649 18650 18651 18652 18653 18654 18655 18656 18657 18658 18659 18660 18661 18662 18663 18664 18665 18666 18667 18668 18669 18670 18671 18672 18673 18674 18675 18676 18677 18678 18679 18680 18681 18682 18683 18684 18685 18686 18687 18688 18689 18690 18691 18692 18693 18694 18695 18696 18697 18698 18699 18700 18701 18702 18703 18704 18705 18706 18707 18708 18709 18710 18711 18712 18713 18714 18715 18716 18717 18718 18719 18720 18721 18722 18723 18724 18725 18726 18727 18728 18729 18730 18731 18732 18733 18734 18735 18736 18737 18738 18739 18740 18741 18742 18743 18744 18745 18746 18747 18748 18749 18750 18751 18752 18753 18754 18755 18756 18757 18758 18759 18760 18761 18762 18763 18764 18765 18766 18767 18768 18769 18770 18771 18772 18773 18774 18775 18776 18777 18778 18779 18780 18781 18782 18783 18784 18785 18786 18787 18788 18789 18790 18791 18792 18793 18794 18795 18796 18797 18798 18799 18800 18801 18802 18803 18804 18805 18806 18807 18808 18809 18810 18811 18812 18813 18814 18815 18816 18817 18818 18819 18820 18821 18822 18823 18824 18825 18826 18827 18828 18829 18830 18831 18832 18833 18834 18835 18836 18837 18838 18839 18840 18841 18842 18843 18844 18845 18846 18847 18848 18849 18850 18851 18852 18853 18854 18855 18856 18857 18858 18859 18860 18861 18862 18863 18864 18865 18866 18867 18868 18869 18870 18871 18872 18873 18874 18875 18876 18877 18878 18879 18880 18881 18882 18883 18884 18885 18886 18887 18888 18889 18890 18891 18892 18893 18894 18895 18896 18897 18898 18899 18900 18901 18902 18903 18904 18905 18906 18907 18908 18909 18910 18911 18912 18913 18914 18915 18916 18917 18918 18919 18920 18921 18922 18923 18924 18925 18926 18927 18928 18929 18930 18931 18932 18933 18934 18935 18936 18937 18938 18939 18940 18941 18942 18943 18944 18945 18946 18947 18948 18949 18950 18951 18952 18953 18954 18955 18956 18957 18958 18959 18960 18961 18962 18963 18964 18965 18966 18967 18968 18969 18970 18971 18972 18973 18974 18975 18976 18977 18978 18979 18980 18981 18982 18983 18984 18985 18986 18987 18988 18989 18990 18991 18992 18993 18994 18995 18996 18997 18998 18999 19000 19001 19002 19003 19004 19005 19006 19007 19008 19009 19010 19011 19012 19013 19014 19015 19016 19017 19018 19019 19020 19021 19022 19023 19024 19025 19026 19027 19028 19029 19030 19031 19032 19033 19034 19035 19036 19037 19038 19039 19040 19041 19042 19043 19044 19045 19046 19047 19048 19049 19050 19051 19052 19053 19054 19055 19056 19057 19058 19059 19060 19061 19062 19063 19064 19065 19066 19067 19068 19069 19070 19071 19072 19073 19074 19075 19076 19077 19078 19079 19080 19081 19082 19083 19084 19085 19086 19087 19088 19089 19090 19091 19092 19093 19094 19095 19096 19097 19098 19099 19100 19101 19102 19103 19104 19105 19106 19107 19108 19109 19110 19111 19112 19113 19114 19115 19116 19117 19118 19119 19120 19121 19122 19123 19124 19125 19126 19127 19128 19129 19130 19131 19132 19133 19134 19135 19136 19137 19138 19139 19140 19141 19142 19143 19144 19145 19146 19147 19148 19149 19150 19151 19152 19153 19154 19155 19156 19157 19158 19159 19160 19161 19162 19163 19164 19165 19166 19167 19168 19169 19170 19171 19172 19173 19174 19175 19176 19177 19178 19179 19180 19181 19182 19183 19184 19185 19186 19187 19188 19189 19190 19191 19192 19193 19194 19195 19196 19197 19198 19199 19200 19201 19202 19203 19204 19205 19206 19207 19208 19209 19210 19211 19212 19213 19214 19215 19216 19217 19218 19219 19220 19221 19222 19223 19224 19225 19226 19227 19228 19229 19230 19231 19232 19233 19234 19235 19236 19237 19238 19239 19240 19241 19242 19243 19244 19245 19246 19247 19248 19249 19250 19251 19252 19253 19254 19255 19256 19257 19258 19259 19260 19261 19262 19263 19264 19265 19266 19267 19268 19269 19270 19271 19272 19273 19274 19275 19276 19277 19278 19279 19280 19281 19282 19283 19284 19285 19286 19287 19288 19289 19290 19291 19292 19293 19294 19295 19296 19297 19298 19299 19300 19301 19302 19303 19304 19305 19306 19307 19308 19309 19310 19311 19312 19313 19314 19315 19316 19317 19318 19319 19320 19321 19322 19323 19324 19325 19326 19327 19328 19329 19330 19331 19332 19333 19334 19335 19336 19337 19338 19339 19340 19341 19342 19343 19344 19345 19346 19347 19348 19349 19350 19351 19352 19353 19354 19355 19356 19357 19358 19359 19360 19361 19362 19363 19364 19365 19366 19367 19368 19369 19370 19371 19372 19373 19374 19375 19376 19377 19378 19379 19380 19381 19382 19383 19384 19385 19386 19387 19388 19389 19390 19391 19392 19393 19394 19395 19396 19397 19398 19399 19400 19401 19402 19403 19404 19405 19406 19407 19408 19409 19410 19411 19412 19413 19414 19415 19416 19417 19418 19419 19420 19421 19422 19423 19424 19425 19426 19427 19428 19429 19430 19431 19432 19433 19434 19435 19436 19437 19438 19439 19440 19441 19442 19443 19444 19445 19446 19447 19448 19449 19450 19451 19452 19453 19454 19455 19456 19457 19458 19459 19460 19461 19462 19463 19464 19465 19466 19467 19468 19469 19470 19471 19472 19473 19474 19475 19476 19477 19478 19479 19480 19481 19482 19483 19484 19485 19486 19487 19488 19489 19490 19491 19492 19493 19494 19495 19496 19497 19498 19499 19500 19501 19502 19503 19504 19505 19506 19507 19508 19509 19510 19511 19512 19513 19514 19515 19516 19517 19518 19519 19520 19521 19522 19523 19524 19525 19526 19527 19528 19529 19530 19531 19532 19533 19534 19535 19536 19537 19538 19539 19540 19541 19542 19543 19544 19545 19546 19547 19548 19549 19550 19551 19552 19553 19554 19555 19556 19557 19558 19559 19560 19561 19562 19563 19564 19565 19566 19567 19568 19569 19570 19571 19572 19573 19574 19575 19576 19577 19578 19579 19580 19581 19582 19583 19584 19585 19586 19587 19588 19589 19590 19591 19592 19593 19594 19595 19596 19597 19598 19599 19600 19601 19602 19603 19604 19605 19606 19607 19608 19609 19610 19611 19612 19613 19614 19615 19616 19617 19618 19619 19620 19621 19622 19623 19624 19625 19626 19627 19628 19629 19630 19631 19632 19633 19634 19635 19636 19637 19638 19639 19640 19641 19642 19643 19644 19645 19646 19647 19648 19649 19650 19651 19652 19653 19654 19655 19656 19657 19658 19659 19660 19661 19662 19663 19664 19665 19666 19667 19668 19669 19670 19671 19672 19673 19674 19675 19676 19677 19678 19679 19680 19681 19682 19683 19684 19685 19686 19687 19688 19689 19690 19691 19692 19693 19694 19695 19696 19697 19698 19699 19700 19701 19702 19703 19704 19705 19706 19707 19708 19709 19710 19711 19712 19713 19714 19715 19716 19717 19718 19719 19720 19721 19722 19723 19724 19725 19726 19727 19728 19729 19730 19731 19732 19733 19734 19735 19736 19737 19738 19739 19740 19741 19742 19743 19744 19745 19746 19747 19748 19749 19750 19751 19752 19753 19754 19755 19756 19757 19758 19759 19760 19761 19762 19763 19764 19765 19766 19767 19768 19769 19770 19771 19772 19773 19774 19775 19776 19777 19778 19779 19780 19781 19782 19783 19784 19785 19786 19787 19788 19789 19790 19791 19792 19793 19794 19795 19796 19797 19798 19799 19800 19801 19802 19803 19804 19805 19806 19807 19808 19809 19810 19811 19812 19813 19814 19815 19816 19817 19818 19819 19820 19821 19822 19823 19824 19825 19826 19827 19828 19829 19830 19831 19832 19833 19834 19835 19836 19837 19838 19839 19840 19841 19842 19843 19844 19845 19846 19847 19848 19849 19850 19851 19852 19853 19854 19855 19856 19857 19858 19859 19860 19861 19862 19863 19864 19865 19866 19867 19868 19869 19870 19871 19872 19873 19874 19875 19876 19877 19878 19879 19880 19881 19882 19883 19884 19885 19886 19887 19888 19889 19890 19891 19892 19893 19894 19895 19896 19897 19898 19899 19900 19901 19902 19903 19904 19905 19906 19907 19908 19909 19910 19911 19912 19913 19914 19915 19916 19917 19918 19919 19920 19921 19922 19923 19924 19925 19926 19927 19928 19929 19930 19931 19932 19933 19934 19935 19936 19937 19938 19939 19940 19941 19942 19943 19944 19945 19946 19947 19948 19949 19950 19951 19952 19953 19954 19955 19956 19957 19958 19959 19960 19961 19962 19963 19964 19965 19966 19967 19968 19969 19970 19971 19972 19973 19974 19975 19976 19977 19978 19979 19980 19981 19982 19983 19984 19985 19986 19987 19988 19989 19990 19991 19992 19993 19994 19995 19996 19997 19998 19999 20000 20001 20002 20003 20004 20005 20006 20007 20008 20009 20010 20011 20012 20013 20014 20015 20016 20017 20018 20019 20020 20021 20022 20023 20024 20025 20026 20027 20028 20029 20030 20031 20032 20033 20034 20035 20036 20037 20038 20039 20040 20041 20042 20043 20044 20045 20046 20047 20048 20049 20050 20051 20052 20053 20054 20055 20056 20057 20058 20059 20060 20061 20062 20063 20064 20065 20066 20067 20068 20069 20070 20071 20072 20073 20074 20075 20076 20077 20078 20079 20080 20081 20082 20083 20084 20085 20086 20087 20088 20089 20090 20091 20092 20093 20094 20095 20096 20097 20098 20099 20100 20101 20102 20103 20104 20105 20106 20107 20108 20109 20110 20111 20112 20113 20114 20115 20116 20117 20118 20119 20120 20121 20122 20123 20124 20125 20126 20127 20128 20129 20130 20131 20132 20133 20134 20135 20136 20137 20138 20139 20140 20141 20142 20143 20144 20145 20146 20147 20148 20149 20150 20151 20152 20153 20154 20155 20156 20157 20158 20159 20160 20161 20162 20163 20164 20165 20166 20167 20168 20169 20170 20171 20172 20173 20174 20175 20176 20177 20178 20179 20180 20181 20182 20183 20184 20185 20186 20187 20188 20189 20190 20191 20192 20193 20194 20195 20196 20197 20198 20199 20200 20201 20202 20203 20204 20205 20206 20207 20208 20209 20210 20211 20212 20213 20214 20215 20216 20217 20218 20219 20220 20221 20222 20223 20224 20225 20226 20227 20228 20229 20230 20231 20232 20233 20234 20235 20236 20237 20238 20239 20240 20241 20242 20243 20244 20245 20246 20247 20248 20249 20250 20251 20252 20253 20254 20255 20256 20257 20258 20259 20260 20261 20262 20263 20264 20265 20266 20267 20268 20269 20270 20271 20272 20273 20274 20275 20276 20277 20278 20279 20280 20281 20282 20283 20284 20285 20286 20287 20288 20289 20290 20291 20292 20293 20294 20295 20296 20297 20298 20299 20300 20301 20302 20303 20304 20305 20306 20307 20308 20309 20310 20311 20312 20313 20314 20315 20316 20317 20318 20319 20320 20321 20322 20323 20324 20325 20326 20327 20328 20329 20330 20331 20332 20333 20334 20335 20336 20337 20338 20339 20340 20341 20342 20343 20344 20345 20346 20347 20348 20349 20350 20351 20352 20353 20354 20355 20356 20357 20358 20359 20360 20361 20362 20363 20364 20365 20366 20367 20368 20369 20370 20371 20372 20373 20374 20375 20376 20377 20378 20379 20380 20381 20382 20383 20384 20385 20386 20387 20388 20389 20390 20391 20392 20393 20394 20395 20396 20397 20398 20399 20400 20401 20402 20403 20404 20405 20406 20407 20408 20409 20410 20411 20412 20413 20414 20415 20416 20417 20418 20419 20420 20421 20422 20423 20424 20425 20426 20427 20428 20429 20430 20431 20432 20433 20434 20435 20436 20437 20438 20439 20440 20441 20442 20443 20444 20445 20446 20447 20448 20449 20450 20451 20452 20453 20454 20455 20456 20457 20458 20459 20460 20461 20462 20463 20464 20465 20466 20467 20468 20469 20470 20471 20472 20473 20474 20475 20476 20477 20478 20479 20480 20481 20482 20483 20484 20485 20486 20487 20488 20489 20490 20491 20492 20493 20494 20495 20496 20497 20498 20499 20500 20501 20502 20503 20504 20505 20506 20507 20508 20509 20510 20511 20512 20513 20514 20515 20516 20517 20518 20519 20520 20521 20522 20523 20524 20525 20526 20527 20528 20529 20530 20531 20532 20533 20534 20535 20536 20537 20538 20539 20540 20541 20542 20543 20544 20545 20546 20547 20548 20549 20550 20551 20552 20553 20554 20555 20556 20557 20558 20559 20560 20561 20562 20563 20564 20565 20566 20567 20568 20569 20570 20571 20572 20573 20574 20575 20576 20577 20578 20579 20580 20581 20582 20583 20584 20585 20586 20587 20588 20589 20590 20591 20592 20593 20594 20595 20596 20597 20598 20599 20600 20601 20602 20603 20604 20605 20606 20607 20608 20609 20610 20611 20612 20613 20614 20615 20616 20617 20618 20619 20620 20621 20622 20623 20624 20625 20626 20627 20628 20629 20630 20631 20632 20633 20634 20635 20636 20637 20638 20639 20640 20641 20642 20643 20644 20645 20646 20647 20648 20649 20650 20651 20652 20653 20654 20655 20656 20657 20658 20659 20660 20661 20662 20663 20664 20665 20666 20667 20668 20669 20670 20671 20672 20673 20674 20675 20676 20677 20678 20679 20680 20681 20682 20683 20684 20685 20686 20687 20688 20689 20690 20691 20692 20693 20694 20695 20696 20697 20698 20699 20700 20701 20702 20703 20704 20705 20706 20707 20708 20709 20710 20711 20712 20713 20714 20715 20716 20717 20718 20719 20720 20721 20722 20723 20724 20725 20726 20727 20728 20729 20730 20731 20732 20733 20734 20735 20736 20737 20738 20739 20740 20741 20742 20743 20744 20745 20746 20747 20748 20749 20750 20751 20752 20753 20754 20755 20756 20757 20758 20759 20760 20761 20762 20763 20764 20765 20766 20767 20768 20769 20770 20771 20772 20773 20774 20775 20776 20777 20778 20779 20780 20781 20782 20783 20784 20785 20786 20787 20788 20789 20790 20791 20792 20793 20794 20795 20796 20797 20798 20799 20800 20801 20802 20803 20804 20805 20806 20807 20808 20809 20810 20811 20812 20813 20814 20815 20816 20817 20818 20819 20820 20821 20822 20823 20824 20825 20826 20827 20828 20829 20830 20831 20832 20833 20834 20835 20836 20837 20838 20839 20840 20841 20842 20843 20844 20845 20846 20847 20848 20849 20850 20851 20852 20853 20854 20855 20856 20857 20858 20859 20860 20861 20862 20863 20864 20865 20866 20867 20868 20869 20870 20871 20872 20873 20874 20875 20876 20877 20878 20879 20880 20881 20882 20883 20884 20885 20886 20887 20888 20889 20890 20891 20892 20893 20894 20895 20896 20897 20898 20899 20900 20901 20902 20903 20904 20905 20906 20907 20908 20909 20910 20911 20912 20913 20914 20915 20916 20917 20918 20919 20920 20921 20922 20923 20924 20925 20926 20927 20928 20929 20930 20931 20932 20933 20934 20935 20936 20937 20938 20939 20940 20941 20942 20943 20944 20945 20946 20947 20948 20949 20950 20951 20952 20953 20954 20955 20956 20957 20958 20959 20960 20961 20962 20963 20964 20965 20966 20967 20968 20969 20970 20971 20972 20973 20974 20975 20976 20977 20978 20979 20980 20981 20982 20983 20984 20985 20986 20987 20988 20989 20990 20991 20992 20993 20994 20995 20996 20997 20998 20999 21000 21001 21002 21003 21004 21005 21006 21007 21008 21009 21010 21011 21012 21013 21014 21015 21016 21017 21018 21019 21020 21021 21022 21023 21024 21025 21026 21027 21028 21029 21030 21031 21032 21033 21034 21035 21036 21037 21038 21039 21040 21041 21042 21043 21044 21045 21046 21047 21048 21049 21050 21051 21052 21053 21054 21055 21056 21057 21058 21059 21060 21061 21062 21063 21064 21065 21066 21067 21068 21069 21070 21071 21072 21073 21074 21075 21076 21077 21078 21079 21080 21081 21082 21083 21084 21085 21086 21087 21088 21089 21090 21091 21092 21093 21094 21095 21096 21097 21098 21099 21100 21101 21102 21103 21104 21105 21106 21107 21108 21109 21110 21111 21112 21113 21114 21115 21116 21117 21118 21119 21120 21121 21122 21123 21124 21125 21126 21127 21128 21129 21130 21131 21132 21133 21134 21135 21136 21137 21138 21139 21140 21141 21142 21143 21144 21145 21146 21147 21148 21149 21150 21151 21152 21153 21154 21155 21156 21157 21158 21159 21160 21161 21162 21163 21164 21165 21166 21167 21168 21169 21170 21171 21172 21173 21174 21175 21176 21177 21178 21179 21180 21181 21182 21183 21184 21185 21186 21187 21188 21189 21190 21191 21192 21193 21194 21195 21196 21197 21198 21199 21200 21201 21202 21203 21204 21205 21206 21207 21208 21209 21210 21211 21212 21213 21214 21215 21216 21217 21218 21219 21220 21221 21222 21223 21224 21225 21226 21227 21228 21229 21230 21231 21232 21233 21234 21235 21236 21237 21238 21239 21240 21241 21242 21243 21244 21245 21246 21247 21248 21249 21250 21251 21252 21253 21254 21255 21256 21257 21258 21259 21260 21261 21262 21263 21264 21265 21266 21267 21268 21269 21270 21271 21272 21273 21274 21275 21276 21277 21278 21279 21280 21281 21282 21283 21284 21285 21286 21287 21288 21289 21290 21291 21292 21293 21294 21295 21296 21297 21298 21299 21300 21301 21302 21303 21304 21305 21306 21307 21308 21309 21310 21311 21312 21313 21314 21315 21316 21317 21318 21319 21320 21321 21322 21323 21324 21325 21326 21327 21328 21329 21330 21331 21332 21333 21334 21335 21336 21337 21338 21339 21340 21341 21342 21343 21344 21345 21346 21347 21348 21349 21350 21351 21352 21353 21354 21355 21356 21357 21358 21359 21360 21361 21362 21363 21364 21365 21366 21367
// engine.cc
// 8/9/2013 jichi
// Branch: ITH_Engine/engine.cpp, revision 133

#ifdef _MSC_VER
# pragma warning (disable:4100)   // C4100: unreference formal parameter
# pragma warning (disable:4819)
#endif // _MSC_VER

#include "src/engine/engine.h"
#include "src/engine/match.h"
#include "src/engine/hookdefs.h"
#include "src/util/util.h"
#include "src/main.h"
#include "src/engine/mono/funcinfo.h"
#include "src/engine/ppsspp/funcinfo.h"
#include "src/except.h"
#include "ithsys/ithsys.h"
#include "memdbg/memsearch.h"
#include "ntinspect/ntinspect.h"
#include "disasm/disasm.h"
#include "winversion/winversion.h"
#include "hashutil/hashstr.h"
#include "cpputil/cppcstring.h"
#include "ccutil/ccmacro.h"
#include "mono/monoobject.h"
//#include <boost/foreach.hpp>
#include <cstdio>
#include <string>

#define hashstr hashutil::djb2

// jichi 7/6/2014: read esp_base
#define retof(esp_base)         *(DWORD *)(esp_base) // return address
#define regof(name, esp_base)   *(DWORD *)((esp_base) + pusha_##name##_off - 4)
#define argof(count, esp_base)  *(DWORD *)((esp_base) + 4 * (count)) // starts from 1 instead of 0

//#define ConsoleOutput(...)  (void)0     // jichi 8/18/2013: I don't need ConsoleOutput

//#define DEBUG "engine.h"

enum { VNR_TEXT_CAPACITY = 1500 }; // estimated max number of bytes allowed in VNR, slightly larger than VNR's text limit (1000)

#ifdef DEBUG
# include "src/util/growl.h"
//# include "uniquemap.h"
//# include "uniquemap.cc"
namespace { // unnamed debug functions
// jichi 12/17/2013: Copied from int TextHook::GetLength(DWORD base, DWORD in)
int GetHookDataLength(const HookParam &hp, DWORD base, DWORD in)
{
  if (CC_UNLIKELY(!base))
    return 0;
  int len;
  switch (hp.length_offset) {
  default: // jichi 12/26/2013: I should not put this default branch to the end
    len = *((int *)base + hp.length_offset);
    if (len >= 0) {
      if (hp.type & USING_UNICODE)
        len <<= 1;
      break;
    }
    else if (len != -1)
      break;
    //len == -1 then continue to case 0.
  case 0:
    if (hp.type & USING_UNICODE)
      len = wcslen((LPCWSTR)in) << 1;
    else
      len = strlen((LPCSTR)in);
    break;
  case 1:
    if (hp.type & USING_UNICODE)
      len = 2;
    else {
      if (hp.type & BIG_ENDIAN)
        in >>= 8;
      len = LeadByteTable[in&0xff];  //Slightly faster than IsDBCSLeadByte
    }
    break;
  }
  // jichi 12/25/2013: This function originally return -1 if failed
  //return len;
  return max(0, len);
}
} // unnamed
#endif // DEBUG

namespace { // unnamed helpers

int PPSSPP_VERSION[4] = { 0, 9, 8, 0 }; // 0.9.8 by default

enum : DWORD {
  PPSSPP_MEMORY_SEARCH_STEP_98 = 0x01000000
  , PPSSPP_MEMORY_SEARCH_STEP_99 = 0x00050000
  //, step = 0x1000 // step  must be at least 0x1000 (offset in SearchPattern)
  //, step = 0x00010000 // crash otoboku PSP on 0.9.9 since 5pb is wrongly inserted
};

enum : BYTE { XX = MemDbg::WidecardByte }; // 0x11
#define XX2 XX,XX       // WORD
#define XX4 XX2,XX2     // DWORD
#define XX8 XX4,XX4     // QWORD

// jichi 8/18/2013: Original maximum relative address in ITH
//enum { MAX_REL_ADDR = 0x200000 };

// jichi 10/1/2013: Increase relative address limit. Certain game engine like Artemis has larger code region
enum : DWORD { MAX_REL_ADDR = 0x00300000 };

static union {
  char text_buffer[0x1000];
  wchar_t wc_buffer[0x800];

  struct { // CodeSection
    DWORD base;
    DWORD size;
  } code_section[0x200];
};

char text_buffer_prev[0x1000];
DWORD buffer_index,
      buffer_length;

BOOL SafeFillRange(LPCWSTR dll, DWORD *lower, DWORD *upper)
{
  BOOL r = FALSE;
  ITH_WITH_SEH(r = FillRange(dll, lower, upper));
  return r;
}

// jichi 3/11/2014: The original FindEntryAligned function could raise exceptions without admin priv
DWORD SafeFindEntryAligned(DWORD start, DWORD back_range)
{
  DWORD r = 0;
  ITH_WITH_SEH(r = Util::FindEntryAligned(start, back_range));
  return r;
}

ULONG SafeFindEnclosingAlignedFunction(DWORD addr, DWORD range)
{
  ULONG r = 0;
  ITH_WITH_SEH(r = MemDbg::findEnclosingAlignedFunction(addr, range)); // this function might raise if failed
  return r;
}

ULONG SafeFindBytes(LPCVOID pattern, DWORD patternSize, DWORD lowerBound, DWORD upperBound)
{
  ULONG r = 0;
  ITH_WITH_SEH(r = MemDbg::findBytes(pattern, patternSize, lowerBound, upperBound));
  return r;
}

ULONG SafeMatchBytes(LPCVOID pattern, DWORD patternSize, DWORD lowerBound, DWORD upperBound, BYTE wildcard = XX)
{
  ULONG r = 0;
  ITH_WITH_SEH(r = MemDbg::matchBytes(pattern, patternSize, lowerBound, upperBound, wildcard));
  return r;
}

// jichi 7/17/2014: Search mapped memory for emulators
ULONG _SafeMatchBytesInMappedMemory(LPCVOID pattern, DWORD patternSize, BYTE wildcard,
                                   ULONG start, ULONG stop, ULONG step)
{
  for (ULONG i = start; i < stop; i += step) // + patternSize to avoid overlap
    if (ULONG r = SafeMatchBytes(pattern, patternSize, i, i + step + patternSize + 1, wildcard))
      return r;
  return 0;
}

inline ULONG SafeMatchBytesInGCMemory(LPCVOID pattern, DWORD patternSize)
{
  enum : ULONG {
    start = MemDbg::MappedMemoryStartAddress // 0x01000000
    , stop = MemDbg::MemoryStopAddress // 0x7ffeffff
    , step = start
  };
  return _SafeMatchBytesInMappedMemory(pattern, patternSize, XX, start, stop, step);
}

inline ULONG SafeMatchBytesInPSPMemory(LPCVOID pattern, DWORD patternSize, DWORD start = MemDbg::MappedMemoryStartAddress, DWORD stop = MemDbg::MemoryStopAddress)
{
  ULONG step = PPSSPP_VERSION[1] == 9 && PPSSPP_VERSION[2] == 8 ? PPSSPP_MEMORY_SEARCH_STEP_98 : PPSSPP_MEMORY_SEARCH_STEP_99;
  return _SafeMatchBytesInMappedMemory(pattern, patternSize, XX, start, stop, step);
}

inline ULONG SafeMatchBytesInPS2Memory(LPCVOID pattern, DWORD patternSize)
{
  // PCSX2 memory range
  // ds: begin from 0x20000000
  // cs: begin from 0x30000000
  enum : ULONG {
    //start = MemDbg::MappedMemoryStartAddress // 0x01000000
    start = 0x30000000 // larger than PSP to skip the garbage memory
    , stop = 0x40000000 // larger than PSP as PS2 has larger memory
    , step = 0x00010000 // smaller than PPS
    //, step = 0x00050000 // the same as PPS
    //, step = 0x1000 // step  must be at least 0x1000 (offset in SearchPattern)
  };
  return _SafeMatchBytesInMappedMemory(pattern, patternSize, XX, start, stop, step);
}

// 7/29/2014 jichi: I should move these functions to different files
// String utilities
// Return the address of the first non-zero address
LPCSTR reverse_search_begin(const char *s, int maxsize = VNR_TEXT_CAPACITY)
{
  if (*s)
    for (int i = 0; i < maxsize; i++, s--)
      if (!*s)
        return s + 1;
  return nullptr;
}

bool all_ascii(const char *s, int maxsize = VNR_TEXT_CAPACITY)
{
  if (s)
    for (int i = 0; i < maxsize && *s; i++, s++)
      if ((BYTE)*s > 127) // unsigned char
        return false;
  return true;
}

bool all_ascii(const wchar_t *s, int maxsize = VNR_TEXT_CAPACITY)
{
  if (s)
    for (int i = 0; i < maxsize && *s; i++, s++)
      if (*s > 127) // unsigned char
        return false;
  return true;
}

// String filters

void CharReplacer(char *str, size_t *size, char fr, char to)
{
  size_t len = *size;
  for (size_t i = 0; i < len; i++)
    if (str[i] == fr)
      str[i] = to;
}

void WideCharReplacer(wchar_t *str, size_t *size, wchar_t fr, wchar_t to)
{
  size_t len = *size / 2;
  for (size_t i = 0; i < len; i++)
    if (str[i] == fr)
      str[i] = to;
}

void CharFilter(char *str, size_t *size, char ch)
{
  size_t len = *size,
         curlen;
  for (char *cur = (char *)::memchr(str, ch, len);
       (cur && --len && (curlen = len - (cur - str)));
       cur = (char *)::memchr(cur, ch, curlen))
    ::memmove(cur, cur + 1, curlen);
  *size = len;
}

void WideCharFilter(wchar_t *str, size_t *size, wchar_t ch)
{
  size_t len = *size / 2,
         curlen;
  for (wchar_t *cur = cpp_wcsnchr(str, ch, len);
       (cur && --len && (curlen = len - (cur - str)));
       cur = cpp_wcsnchr(cur, ch, curlen))
    ::memmove(cur, cur + 1, 2 * curlen);
  *size = len * 2;
}

void CharsFilter(char *str, size_t *size, const char *chars)
{
  size_t len = *size,
         curlen;
  for (char *cur = cpp_strnpbrk(str, chars, len);
       (cur && --len && (curlen = len - (cur - str)));
       cur = cpp_strnpbrk(cur, chars, curlen))
    ::memmove(cur, cur + 1, curlen);
  *size = len;
}

void WideCharsFilter(wchar_t *str, size_t *size, const wchar_t *chars)
{
  size_t len = *size / 2,
         curlen;
  for (wchar_t *cur = cpp_wcsnpbrk(str, chars, len);
       (cur && --len && (curlen = len - (cur - str)));
       cur = cpp_wcsnpbrk(cur, chars, curlen))
    ::memmove(cur, cur + 1, 2 * curlen);
  *size = len * 2;
}

void StringFilter(char *str, size_t *size, const char *remove, size_t removelen)
{
  size_t len = *size,
         curlen;
  for (char *cur = cpp_strnstr(str, remove, len);
       (cur && (len -= removelen) && (curlen = len - (cur - str)));
       cur = cpp_strnstr(cur, remove, curlen))
    ::memmove(cur, cur + removelen, curlen);
  *size = len;
}

void WideStringFilter(wchar_t *str, size_t *size, const wchar_t *remove, size_t removelen)
{
  size_t len = *size / 2,
         curlen;
  for (wchar_t *cur = cpp_wcsnstr(str, remove, len);
       (cur && (len -= removelen) && (curlen = len - (cur - str)));
       cur = cpp_wcsnstr(cur, remove, curlen))
    ::memmove(cur, cur + removelen, 2 * curlen);
  *size = len * 2;
}

void StringFilterBetween(char *str, size_t *size, const char *fr, size_t frlen, const char *to, size_t tolen)
{
  size_t len = *size,
         curlen;
  for (char *cur = cpp_strnstr(str, fr, len);
       cur;
       cur = cpp_strnstr(cur, fr, curlen)) {
    curlen = (len - frlen) - (cur - str);
    auto end = cpp_strnstr(cur + frlen, to, curlen);
    if (!end)
      break;
    curlen = len - (end - str) - tolen;
    ::memmove(cur, end + tolen, curlen);
    len -= tolen + (end - cur);
  }
  *size = len;
}

void WideStringFilterBetween(wchar_t *str, size_t *size, const wchar_t *fr, size_t frlen, const wchar_t *to, size_t tolen)
{
  size_t len = *size / 2,
         curlen;
  for (wchar_t *cur = cpp_wcsnstr(str, fr, len);
       cur;
       cur = cpp_wcsnstr(cur, fr, curlen)) {
    curlen = (len - frlen) - (cur - str);
    auto end = cpp_wcsnstr(cur + frlen, to, curlen);
    if (!end)
      break;
    curlen = len - (end - str) - tolen;
    ::memmove(cur, end + tolen, 2 * curlen);
    len -= tolen + (end - cur);
  }
  *size = len * 2;
}

void StringCharReplacer(char *str, size_t *size, const char *src, size_t srclen, char ch)
{
  size_t len = *size,
         curlen;
  for (char *cur = cpp_strnstr(str, src, len);
       cur && len;
       cur = cpp_strnstr(cur, src, curlen)) {
    *cur++ = ch;
    len -= srclen - 1;
    curlen = len - (cur - str);
    if (curlen == 0)
      break;
    ::memmove(cur, cur + srclen - 1, curlen);
  }
  *size = len;
}

void WideStringCharReplacer(wchar_t *str, size_t *size, const wchar_t *src, size_t srclen, wchar_t ch)
{
  size_t len = *size / 2,
         curlen;
  for (wchar_t *cur = cpp_wcsnstr(str, src, len);
       cur && len;
       cur = cpp_wcsnstr(cur, src, curlen)) {
    *cur++ = ch;
    len -= srclen - 1;
    curlen = len - (cur - str);
    if (curlen == 0)
      break;
    ::memmove(cur, cur + srclen, 2 * curlen);
  }
  *size = len * 2;
}

// NOTE: I assume srclen >= dstlen
void StringReplacer(char *str, size_t *size, const char *src, size_t srclen, const char *dst, size_t dstlen)
{
  size_t len = *size,
         curlen;
  for (char *cur = cpp_strnstr(str, src, len);
       cur && len;
       cur = cpp_strnstr(cur, src, curlen)) {
    ::memcpy(cur, dst, dstlen);
    cur += dstlen;
    len -= srclen - dstlen;
    curlen = len - (cur - str);
    if (curlen == 0)
      break;
    if (srclen > dstlen)
      ::memmove(cur, cur + srclen - dstlen, curlen);
  }
  *size = len;
}

void WideStringReplacer(wchar_t *str, size_t *size, const wchar_t *src, size_t srclen, const wchar_t *dst, size_t dstlen)
{
  size_t len = *size / 2,
         curlen;
  for (wchar_t *cur = cpp_wcsnstr(str, src, len);
       cur && len;
       cur = cpp_wcsnstr(cur, src, curlen)) {
    ::memcpy(cur, dst, 2 * dstlen);
    cur += dstlen;
    len -= srclen - dstlen;
    curlen = len - (cur - str);
    if (curlen == 0)
      break;
    if (srclen > dstlen)
      ::memmove(cur, cur + srclen - dstlen, 2 * curlen);
  }
  *size = len * 2;
}

bool NewLineCharFilter(LPVOID data, DWORD *size, HookParam *, BYTE)
{
  CharFilter(reinterpret_cast<LPSTR>(data), reinterpret_cast<size_t *>(size),
      '\n');
  return true;
}
bool NewLineWideCharFilter(LPVOID data, DWORD *size, HookParam *, BYTE)
{
  CharFilter(reinterpret_cast<LPSTR>(data), reinterpret_cast<size_t *>(size),
      L'\n');
  return true;
}
bool NewLineStringFilter(LPVOID data, DWORD *size, HookParam *, BYTE)
{
  StringFilter(reinterpret_cast<LPSTR>(data), reinterpret_cast<size_t *>(size),
      "\\n", 2);
  return true;
}
bool NewLineWideStringFilter(LPVOID data, DWORD *size, HookParam *, BYTE)
{
  WideStringFilter(reinterpret_cast<LPWSTR>(data), reinterpret_cast<size_t *>(size),
      L"\\n", 2);
  return true;
}
bool NewLineCharToSpaceFilter(LPVOID data, DWORD *size, HookParam *, BYTE)
{
  CharReplacer(reinterpret_cast<LPSTR>(data), reinterpret_cast<size_t *>(size), '\n', ' ');
  return true;
}
bool NewLineWideCharToSpaceFilter(LPVOID data, DWORD *size, HookParam *, BYTE)
{
  WideCharReplacer(reinterpret_cast<LPWSTR>(data), reinterpret_cast<size_t *>(size), L'\n', L' ');
  return true;
}

// Remove every characters <= 0x1f (i.e. before space ' ') except 0xa and 0xd.
bool IllegalCharsFilter(LPVOID data, DWORD *size, HookParam *, BYTE)
{
  CharsFilter(reinterpret_cast<LPSTR>(data), reinterpret_cast<size_t *>(size),
      "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0b\x0c\x0e\x0f\x10\x11\x12\x12\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f");
  return true;
}
bool IllegalWideCharsFilter(LPVOID data, DWORD *size, HookParam *, BYTE)
{
  WideCharsFilter(reinterpret_cast<LPWSTR>(data), reinterpret_cast<size_t *>(size),
      L"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0b\x0c\x0e\x0f\x10\x11\x12\x12\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f");
  return true;
}

} // unnamed namespace

namespace Engine {

/********************************************************************************************
KiriKiri hook:
  Usually there are xp3 files in the game folder but also exceptions.
  Find TVP(KIRIKIRI) in the version description is a much more precise way.

  KiriKiri1 correspond to AGTH KiriKiri hook, but this doesn't always work well.
  Find call to GetGlyphOutlineW and go to function header. EAX will point to a
  structure contains character (at 0x14, [EAX+0x14]) we want. To split names into
  different threads AGTH uses [EAX], seems that this value stands for font size.
  Since KiriKiri is compiled by BCC and BCC fastcall uses EAX to pass the first
  parameter. Here we choose EAX is reasonable.
  KiriKiri2 is a redundant hook to catch text when 1 doesn't work. When this happens,
  usually there is a single GetTextExtentPoint32W contains irregular repetitions which
  is out of the scope of KS or KF. This time we find a point and split them into clean
  text threads. First find call to GetTextExtentPoint32W and step out of this function.
  Usually there is a small loop. It is this small loop messed up the text. We can find
  one ADD EBX,2 in this loop. It's clear that EBX is a string pointer goes through the
  string. After the loop EBX will point to the end of the string. So EBX-2 is the last
  char and we insert hook here to extract it.
********************************************************************************************/
#if 0 // jichi 11/12/2013: not used
static void SpecialHookKiriKiri(DWORD esp_base, HookParam *, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  DWORD p1 =  *(DWORD *)(esp_base - 0x14),
        p2 =  *(DWORD *)(esp_base - 0x18);
  if ((p1>>16) == (p2>>16)) {
    if (DWORD p3 = *(DWORD *)p1) {
      p3 += 8;
      for (p2 = p3 + 2; *(WORD *)p2; p2 += 2);
      *len = p2 - p3;
      *data = p3;
      p1 = *(DWORD *)(esp_base - 0x20);
      p1 = *(DWORD *)(p1 + 0x74);
      *split = p1 | *(DWORD *)(esp_base + 0x48);
    } else
      *len = 0;
  } else
    *len=0;
}
#endif // 0

bool FindKiriKiriHook(DWORD fun, DWORD size, DWORD pt, DWORD flag) // jichi 10/20/2014: change return value to bool
{
  enum : DWORD {
    // jichi 10/20/2014: mov ebp,esp, sub esp,*
    kirikiri1_sig = 0xec8b55,

    // jichi 10/20/2014:
    // 00e01542   53               push ebx
    // 00e01543   56               push esi
    // 00e01544   57               push edi
    kirikiri2_sig = 0x575653
  };
  enum : DWORD { StartAddress = 0x1000 };
  enum : DWORD { StartRange = 0x6000, StopRange = 0x8000 }; // jichi 10/20/2014: ITH original pattern range

  // jichi 10/20/2014: The KiriKiri patterns exist in multiple places of the game.
  //enum : DWORD { StartRange = 0x8000, StopRange = 0x9000 }; // jichi 10/20/2014: change to a different range

  //WCHAR str[0x40];
  DWORD sig = flag ? kirikiri2_sig : kirikiri1_sig;
  DWORD t = 0;
  for (DWORD i = StartAddress; i < size - 4; i++)
    if (*(WORD *)(pt + i) == 0x15ff) { // jichi 10/20/2014: call dword ptr ds
      DWORD addr = *(DWORD *)(pt + i + 2);

      // jichi 10/20/2014: There are multiple function calls. The flag+1 one is selected.
      // i.e. KiriKiri1: The first call to GetGlyphOutlineW is selected
      //      KiriKiri2: The second call to GetTextExtentPoint32W is selected
      if (addr >= pt && addr <= pt + size - 4
          && *(DWORD *)addr == fun)
        t++;
      if (t == flag + 1)  // We find call to GetGlyphOutlineW or GetTextExtentPoint32W.
        //swprintf(str, L"CALL addr:0x%.8X",i+pt);
        //ConsoleOutput(str);
        for (DWORD j = i; j > i - StartAddress; j--)
          if (((*(DWORD *)(pt + j)) & 0xffffff) == sig) {
            if (flag)  { // We find the function entry. flag indicate 2 hooks.
              t = 0;  // KiriKiri2, we need to find call to this function.
              for (DWORD k = j + StartRange; k < j + StopRange; k++) // Empirical range.
                if (*(BYTE *)(pt + k) == 0xe8) {
                  if (k + 5 + *(DWORD *)(pt + k + 1) == j)
                    t++;
                  if (t == 2) {
                    //for (k+=pt+0x14; *(WORD*)(k)!=0xC483;k++);
                    //swprintf(str, L"Hook addr: 0x%.8X",pt+k);
                    //ConsoleOutput(str);
                    HookParam hp = {};
                    hp.address = pt + k + 0x14;
                    hp.offset = -0x14;
                    hp.index = -0x2;
                    hp.split = -0xc;
                    hp.length_offset = 1;
                    hp.type = USING_UNICODE|NO_CONTEXT|USING_SPLIT|DATA_INDIRECT;
                    ConsoleOutput("vnreng: INSERT KiriKiri2");
                    NewHook(hp, "KiriKiri2");
                    return true;
                  }
                }
            } else {
              //swprintf(str, L"Hook addr: 0x%.8X",pt+j);
              //ConsoleOutput(str);
              HookParam hp = {};
              hp.address = (DWORD)pt + j;
              hp.offset = -0x8;
              hp.index = 0x14;
              hp.split = -0x8;
              hp.length_offset = 1;
              hp.type = USING_UNICODE|DATA_INDIRECT|USING_SPLIT|SPLIT_INDIRECT;
              ConsoleOutput("vnreng: INSERT KiriKiri1");
              NewHook(hp, "KiriKiri1");
              return true;
            }
            return false;
          }
        //ConsoleOutput("vnreng:KiriKiri: FAILED to find function entry");
    }
  if (flag)
    ConsoleOutput("vnreng:KiriKiri2: failed");
  else
    ConsoleOutput("vnreng:KiriKiri1: failed");
  return false;
}

bool InsertKiriKiriHook() // 9/20/2014 jichi: change return type to bool
{
  bool k1 = FindKiriKiriHook((DWORD)GetGlyphOutlineW,      module_limit_ - module_base_, module_base_, 0), // KiriKiri1
       k2 = FindKiriKiriHook((DWORD)GetTextExtentPoint32W, module_limit_ - module_base_, module_base_, 1); // KiriKiri2
  //RegisterEngineType(ENGINE_KIRIKIRI);
  if (k1 && k2) {
    ConsoleOutput("vnreng:KiriKiri1: disable GDI hooks");
    DisableGDIHooks();
  }
  return k1 || k2;
}

/** 10/20/2014 jichi: KAGParser
 *  Sample game: [141128] Venus Blood -HYPNO- ヴィーナスブラッ�・ヒュプノ 体験版
 *
 *  drawText and drawGlyph seem to be the right function to look at.
 *  However, the latest source code does not match VenusBlood.
 *
 *  Debug method:
 *  Pre-compute: hexstr 視界のきかな�utf16, got: 96894c756e304d304b306a304430
 *  Use ollydbg to insert hardware break point before the scene is entered.
 *  It found several places either in game or KAGParser, and the last one is as follows.
 *  It tries to find "[" (0x5b) in the memory.
 *
 *  1. It cannot find character name.
 *  2. It will extract [r].
 *
 *  6e562270   75 0a            jnz short kagparse.6e56227c
 *  6e562272   c705 00000000 00>mov dword ptr ds:[0],0x0
 *  6e56227c   ffb424 24010000  push dword ptr ss:[esp+0x124]
 *  6e562283   ff9424 24010000  call dword ptr ss:[esp+0x124]
 *  6e56228a   8b8c24 20010000  mov ecx,dword ptr ss:[esp+0x120]
 *  6e562291   890d 14ed576e    mov dword ptr ds:[0x6e57ed14],ecx
 *  6e562297   68 3090576e      push kagparse.6e579030                   ; unicode "[r]"
 *  6e56229c   8d46 74          lea eax,dword ptr ds:[esi+0x74]
 *  6e56229f   50               push eax
 *  6e5622a0   ffd1             call ecx
 *  6e5622a2   8b4e 50          mov ecx,dword ptr ds:[esi+0x50]
 *  6e5622a5   8b46 54          mov eax,dword ptr ds:[esi+0x54]
 *  6e5622a8   66:833c48 5b     cmp word ptr ds:[eax+ecx*2],0x5b ; jichi: hook here
 *  6e5622ad   75 06            jnz short kagparse.6e5622b5
 *  6e5622af   8d41 01          lea eax,dword ptr ds:[ecx+0x1]
 *  6e5622b2   8946 50          mov dword ptr ds:[esi+0x50],eax
 *  6e5622b5   ff46 50          inc dword ptr ds:[esi+0x50]
 *  6e5622b8  ^e9 aebcffff      jmp kagparse.6e55df6b
 *  6e5622bd   8d8c24 88030000  lea ecx,dword ptr ss:[esp+0x388]
 *  6e5622c4   e8 b707ffff      call kagparse.6e552a80
 *  6e5622c9   84c0             test al,al
 *  6e5622cb   75 0f            jnz short kagparse.6e5622dc
 *  6e5622cd   8d8424 88030000  lea eax,dword ptr ss:[esp+0x388]
 *  6e5622d4   50               push eax
 *  6e5622d5   8bce             mov ecx,esi
 *  6e5622d7   e8 149bffff      call kagparse.6e55bdf0
 *  6e5622dc   8d8c24 80030000  lea ecx,dword ptr ss:[esp+0x380]
 *  6e5622e3   e8 9807ffff      call kagparse.6e552a80
 *  6e5622e8   84c0             test al,al
 *  6e5622ea   75 0f            jnz short kagparse.6e5622fb
 *  6e5622ec   8d8424 80030000  lea eax,dword ptr ss:[esp+0x380]
 *  6e5622f3   50               push eax
 *  6e5622f4   8bce             mov ecx,esi
 *  6e5622f6   e8 35a0ffff      call kagparse.6e55c330
 *  6e5622fb   8d8c24 c0030000  lea ecx,dword ptr ss:[esp+0x3c0]
 *  6e562302   c68424 c0040000 >mov byte ptr ss:[esp+0x4c0],0x3c
 *  6e56230a   e8 81edfeff      call kagparse.6e551090
 *  6e56230f   8d8c24 80030000  lea ecx,dword ptr ss:[esp+0x380]
 *  6e562316   c68424 c0040000 >mov byte ptr ss:[esp+0x4c0],0x3b
 *  6e56231e   e8 8deefeff      call kagparse.6e5511b0
 *  6e562323   8d8c24 88030000  lea ecx,dword ptr ss:[esp+0x388]
 *  6e56232a   e9 d7000000      jmp kagparse.6e562406
 *  6e56232f   66:837c24 20 00  cmp word ptr ss:[esp+0x20],0x0
 *  6e562335   75 10            jnz short kagparse.6e562347
 *  6e562337   ff46 4c          inc dword ptr ds:[esi+0x4c]
 *  6e56233a   c746 50 00000000 mov dword ptr ds:[esi+0x50],0x0
 *  6e562341   c646 5c 00       mov byte ptr ds:[esi+0x5c],0x0
 *
 *  Runtime regisers:
 *  EAX 09C1A626    text address
 *  ECX 00000000    0 or other offset
 *  EDX 025F1368    this value seems does not change. it is always pointed to 0
 *  EBX 0000300C
 *  ESP 0029EB7C
 *  EBP 0029F044
 *  ESI 04EE4150
 *  EDI 0029F020
 *
 *  とな�KAGParserEx.dll
 *  10013948   68 14830210      push _3.10028314                         ; UNICODE "[r]"
 *  1001394d   83c2 7c          add edx,0x7c
 *  10013950   52               push edx
 *  10013951   ffd0             call eax
 *  10013953   8b75 08          mov esi,dword ptr ss:[ebp+0x8]
 *  10013956   eb 02            jmp short _3.1001395a
 *  10013958   8bf2             mov esi,edx
 *  1001395a   8b46 58          mov eax,dword ptr ds:[esi+0x58]
 *  1001395d   8b4e 5c          mov ecx,dword ptr ds:[esi+0x5c]
 *  10013960   66:833c41 5b     cmp word ptr ds:[ecx+eax*2],0x5b    ; jichi: hook here
 *  10013965   75 06            jnz short _3.1001396d
 *  10013967   83c0 01          add eax,0x1
 *  1001396a   8946 58          mov dword ptr ds:[esi+0x58],eax
 *  1001396d   8346 58 01       add dword ptr ds:[esi+0x58],0x1
 *  10013971   807e 7a 00       cmp byte ptr ds:[esi+0x7a],0x0
 *  10013975  ^0f85 b5a7ffff    jnz _3.1000e130
 *  1001397b   8b45 08          mov eax,dword ptr ss:[ebp+0x8]
 *  1001397e   83b8 90000000 ff cmp dword ptr ds:[eax+0x90],-0x1
 *  10013985   0f84 68040000    je _3.10013df3
 *  1001398b   8bd8             mov ebx,eax
 *  1001398d  ^e9 a1a7ffff      jmp _3.1000e133
 *  10013992   8d7c24 78        lea edi,dword ptr ss:[esp+0x78]
 *  10013996   8d7424 54        lea esi,dword ptr ss:[esp+0x54]
 *  1001399a   e8 e16fffff      call _3.1000a980
 */

#if 0 // not used, as KiriKiriZ is sufficient, and most KiriKiriZ games use KAGParserEx instead of KAGParser.
namespace { // unnamed

bool KAGParserFilter(LPVOID data, DWORD *size, HookParam *, BYTE)
{
  WideStringFilter(reinterpret_cast<LPWSTR>(data), reinterpret_cast<size_t *>(size), L"[r]", 3);
  return true;
}

void SpecialHookKAGParser(DWORD esp_base, HookParam *, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  // 6e5622a8   66:833c48 5b     cmp word ptr ds:[eax+ecx*2],0x5b
  DWORD eax = regof(eax, esp_base),
        ecx = regof(ecx, esp_base);
  if (eax && !ecx) { // skip string when ecx is not zero
    *data = eax;
    *len = ::wcslen((LPCWSTR)eax) * 2; // 2 == sizeof(wchar_t)
    *split = FIXED_SPLIT_VALUE; // merge all threads
  }
}

void SpecialHookKAGParserEx(DWORD esp_base, HookParam *, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  // 10013960   66:833c41 5b     cmp word ptr ds:[ecx+eax*2],0x5b
  DWORD eax = regof(eax, esp_base),
        ecx = regof(ecx, esp_base);
  if (ecx && !eax) { // skip string when ecx is not zero
    *data = ecx;
    *len = ::wcslen((LPCWSTR)ecx) * 2; // 2 == sizeof(wchar_t)
    *split = FIXED_SPLIT_VALUE; // merge all threads
  }
}
} // unnamed namespace
bool InsertKAGParserHook()
{
  ULONG startAddress, stopAddress;
  if (!NtInspect::getModuleMemoryRange(L"KAGParser.dll", &startAddress, &stopAddress)) {
    ConsoleOutput("vnreng:KAGParser: failed to get memory range");
    return false;
  }
  const wchar_t *patternString = L"[r]";
  const size_t patternStringSize = ::wcslen(patternString) * 2;
  ULONG addr = MemDbg::findBytes(patternString, patternStringSize, startAddress, stopAddress);
  if (!addr) {
    ConsoleOutput("vnreng:KAGParser: [r] global string not found");
    return false;
  }
  // Find where it is used as function parameter
  addr = MemDbg::findPushAddress(addr, startAddress, stopAddress);
  if (!addr) {
    ConsoleOutput("vnreng:KAGParser: push address not found");
    return false;
  }

  const BYTE ins[] = {
    0x66,0x83,0x3c,0x48, 0x5b // 6e5622a8   66:833c48 5b   cmp word ptr ds:[eax+ecx*2],0x5b ; jichi: hook here
  };
  enum { range = 0x20 }; // 0x6e5622a8 - 0x6e562297 = 17
  addr = MemDbg::findBytes(ins, sizeof(ins), addr, addr + range);
  if (!addr) {
    ConsoleOutput("vnreng:KAGParser: instruction pattern not found");
    return false;
  }

  HookParam hp = {};
  hp.address = addr;
  hp.text_fun = SpecialHookKAGParser;
  hp.filter_fun = KAGParserFilter;
  hp.type = USING_UNICODE|FIXING_SPLIT|NO_CONTEXT; // Fix the split value to merge all threads
  ConsoleOutput("vnreng: INSERT KAGParser");
  NewHook(hp, "KAGParser");
  return true;
}
bool InsertKAGParserExHook()
{
  ULONG startAddress, stopAddress;
  if (!NtInspect::getModuleMemoryRange(L"KAGParserEx.dll", &startAddress, &stopAddress)) {
    ConsoleOutput("vnreng:KAGParserEx: failed to get memory range");
    return false;
  }
  const wchar_t *patternString = L"[r]";
  const size_t patternStringSize = ::wcslen(patternString) * 2;
  ULONG addr = MemDbg::findBytes(patternString, patternStringSize, startAddress, stopAddress);
  if (!addr) {
    ConsoleOutput("vnreng:KAGParserEx: [r] global string not found");
    return false;
  }
  // Find where it is used as function parameter
  addr = MemDbg::findPushAddress(addr, startAddress, stopAddress);
  if (!addr) {
    ConsoleOutput("vnreng:KAGParserEx: push address not found");
    return false;
  }

  const BYTE ins[] = {
    0x66,0x83,0x3c,0x41, 0x5b // 10013960   66:833c41 5b     cmp word ptr ds:[ecx+eax*2],0x5b    ; jichi: hook here
  };
  enum { range = 0x20 }; // 0x10013960 - 0x10013948 = 24
  addr = MemDbg::findBytes(ins, sizeof(ins), addr, addr + range);
  if (!addr) {
    ConsoleOutput("vnreng:KAGParserEx: instruction pattern not found");
    return false;
  }

  HookParam hp = {};
  hp.address = addr;
  hp.text_fun = SpecialHookKAGParserEx;
  hp.filter_fun = KAGParserFilter;
  hp.type = USING_UNICODE|FIXING_SPLIT|NO_CONTEXT; // Fix the split value to merge all threads
  ConsoleOutput("vnreng: INSERT KAGParserEx");
  NewHook(hp, "KAGParserEx");
  return true;
}
#endif // 0

/** 10/24/2014 jichi: New KiriKiri hook
 *  Sample game: [141128] Venus Blood -HYPNO- ヴィーナスブラッ�・ヒュプノ 体験版
 *
 *  This engine will hook to the caller of caller of the first GetGlyphOutlineW (totally three).
 *  The logic is quite similar to KiriKiri1 except it backtrack twice to get the function call.
 *
 *  1/31/2015: If the game no longer invoke GDI functions by default, one way to find the hook
 *  is to click the フォン�in the menu to force triggering GetGlyphOutlineW function.
 *
 *  KiriKiriZ:
 *  https://github.com/krkrz/krkrz
 *  http://krkrz.github.io
 *
 *  KiriKiri API: http://devdoc.kikyou.info/tvp/docs/kr2doc/contents/f_Layer.html
 *
 *  See: krkrz/src/core/visual/LayerIntf.cpp
 *  API: http://devdoc.kikyou.info/tvp/docs/kr2doc/contents/f_Layer_drawText.html
 *
 *  Debug method:
 *  Backtrack from GetGlyphOutlineW, and find the first function that is invoked more
 *  times than (cached) GetGlyphOutlineW.
 *
 *  - Find function calls to GetGlyphOutlineW (totally three)
 *
 *  - Find the caller of the first GetGlyphOutlineW
 *    Using MemDbg::findCallerAddressAfterInt3()
 *
 *  - Find the caller of the above caller
 *    Since the function address is dynamic, the function is found using KiriKiriZHook
 *
 *    00377c44   8b01             mov eax,dword ptr ds:[ecx]
 *    00377c46   ff75 10          push dword ptr ss:[ebp+0x10]
 *    00377c49   ff75 0c          push dword ptr ss:[ebp+0xc]
 *    00377c4c   53               push ebx
 *    00377c4d   ff50 1c          call dword ptr ds:[eax+0x1c] ; jichi: called here
 *    00377c50   8bf0             mov esi,eax
 *    00377c52   8975 e4          mov dword ptr ss:[ebp-0x1c],esi
 *    00377c55   ff46 04          inc dword ptr ds:[esi+0x4]
 *    00377c58   c745 fc 04000000 mov dword ptr ss:[ebp-0x4],0x4
 *
 *  Then, the UTF8 two-byte character is at [ecx]+0x14
 *    0017E950  16 00 00 00 00 02 00 00 00 00 00 00 98 D2 76 02
 *    0017E960  E0 8E 90 D9 42 7D 00 00 00 02 00 00 01 00 00 00
 *                          up: text here
 *    0017E970  01 00 01 FF 00 00 00 00 00 00 00 00 C8
 *
 *  1/30/2015:
 *  The hooked function in Venus Blood -HYPNO- is as follows.
 *  Since サノバウィッ� (150226), KiriKiriZ no longer invokes GetGlyphOutlineW.
 *  Try to extract instruction patterns from the following function instead.
 *
 *  011a7a3c   cc               int3
 *  011a7a3d   cc               int3
 *  011a7a3e   cc               int3
 *  011a7a3f   cc               int3
 *  011a7a40   55               push ebp
 *  011a7a41   8bec             mov ebp,esp
 *  011a7a43   6a ff            push -0x1
 *  011a7a45   68 dbaa3101      push .0131aadb
 *  011a7a4a   64:a1 00000000   mov eax,dword ptr fs:[0]
 *  011a7a50   50               push eax
 *  011a7a51   83ec 14          sub esp,0x14
 *  011a7a54   53               push ebx
 *  011a7a55   56               push esi
 *  011a7a56   57               push edi
 *  011a7a57   a1 00593d01      mov eax,dword ptr ds:[0x13d5900]
 *  011a7a5c   33c5             xor eax,ebp
 *  011a7a5e   50               push eax
 *  011a7a5f   8d45 f4          lea eax,dword ptr ss:[ebp-0xc]
 *  011a7a62   64:a3 00000000   mov dword ptr fs:[0],eax
 *  011a7a68   8965 f0          mov dword ptr ss:[ebp-0x10],esp
 *  011a7a6b   8bd9             mov ebx,ecx
 *  011a7a6d   803d 00113e01 00 cmp byte ptr ds:[0x13e1100],0x0
 *  011a7a74   75 17            jnz short .011a7a8d
 *  011a7a76   c745 e8 1cb83d01 mov dword ptr ss:[ebp-0x18],.013db81c
 *  011a7a7d   8d45 e8          lea eax,dword ptr ss:[ebp-0x18]
 *  011a7a80   50               push eax
 *  011a7a81   e8 4ae2f0ff      call .010b5cd0
 *  011a7a86   c605 00113e01 01 mov byte ptr ds:[0x13e1100],0x1
 *  011a7a8d   33c9             xor ecx,ecx
 *  011a7a8f   384b 21          cmp byte ptr ds:[ebx+0x21],cl
 *  011a7a92   0f95c1           setne cl
 *  011a7a95   33c0             xor eax,eax
 *  011a7a97   3843 20          cmp byte ptr ds:[ebx+0x20],al
 *  011a7a9a   0f95c0           setne al
 *  011a7a9d   33c8             xor ecx,eax
 *  011a7a9f   334b 10          xor ecx,dword ptr ds:[ebx+0x10]
 *  011a7aa2   0fb743 14        movzx eax,word ptr ds:[ebx+0x14]
 *  011a7aa6   33c8             xor ecx,eax
 *  011a7aa8   8b7b 1c          mov edi,dword ptr ds:[ebx+0x1c]
 *  011a7aab   33f9             xor edi,ecx
 *  011a7aad   337b 18          xor edi,dword ptr ds:[ebx+0x18]
 *  011a7ab0   897d e4          mov dword ptr ss:[ebp-0x1c],edi
 *  011a7ab3   57               push edi
 *  011a7ab4   53               push ebx
 *  011a7ab5   e8 06330000      call .011aadc0
 *  011a7aba   8bf0             mov esi,eax
 *  011a7abc   85f6             test esi,esi
 *  011a7abe   74 26            je short .011a7ae6
 *  011a7ac0   56               push esi
 *  011a7ac1   e8 ba330000      call .011aae80
 *  011a7ac6   8d46 2c          lea eax,dword ptr ds:[esi+0x2c]
 *  011a7ac9   85c0             test eax,eax
 *  011a7acb   74 19            je short .011a7ae6
 *  011a7acd   8b08             mov ecx,dword ptr ds:[eax]
 *  011a7acf   ff41 04          inc dword ptr ds:[ecx+0x4]
 *  011a7ad2   8b00             mov eax,dword ptr ds:[eax]
 *  011a7ad4   8b4d f4          mov ecx,dword ptr ss:[ebp-0xc]
 *  011a7ad7   64:890d 00000000 mov dword ptr fs:[0],ecx
 *  011a7ade   59               pop ecx
 *  011a7adf   5f               pop edi
 *  011a7ae0   5e               pop esi
 *  011a7ae1   5b               pop ebx
 *  011a7ae2   8be5             mov esp,ebp
 *  011a7ae4   5d               pop ebp
 *  011a7ae5   c3               retn
 *  011a7ae6   8b4d 08          mov ecx,dword ptr ss:[ebp+0x8]
 *  011a7ae9   85c9             test ecx,ecx
 *  011a7aeb   0f84 47010000    je .011a7c38
 *  011a7af1   0fb743 14        movzx eax,word ptr ds:[ebx+0x14]
 *  011a7af5   50               push eax
 *  011a7af6   e8 b5090300      call .011d84b0
 *  011a7afb   8bf0             mov esi,eax
 *  011a7afd   8975 ec          mov dword ptr ss:[ebp-0x14],esi
 *  011a7b00   85f6             test esi,esi
 *  011a7b02   0f84 30010000    je .011a7c38
 *  011a7b08   6a 34            push 0x34
 *  011a7b0a   e8 29621300      call .012ddd38
 *  011a7b0f   83c4 04          add esp,0x4
 *  011a7b12   8bf8             mov edi,eax
 *  011a7b14   897d e0          mov dword ptr ss:[ebp-0x20],edi
 *  011a7b17   c745 fc 00000000 mov dword ptr ss:[ebp-0x4],0x0
 *  011a7b1e   85ff             test edi,edi
 *  011a7b20   74 1d            je short .011a7b3f
 *  011a7b22   c747 2c 41000000 mov dword ptr ds:[edi+0x2c],0x41
 *  011a7b29   c647 32 00       mov byte ptr ds:[edi+0x32],0x0
 *  011a7b2d   c747 04 01000000 mov dword ptr ds:[edi+0x4],0x1
 *  011a7b34   c707 00000000    mov dword ptr ds:[edi],0x0
 *  011a7b3a   8945 e8          mov dword ptr ss:[ebp-0x18],eax
 *  011a7b3d   eb 05            jmp short .011a7b44
 *  011a7b3f   33ff             xor edi,edi
 *  011a7b41   897d e8          mov dword ptr ss:[ebp-0x18],edi
 *  011a7b44   c745 fc ffffffff mov dword ptr ss:[ebp-0x4],-0x1
 *  011a7b4b   0fb746 04        movzx eax,word ptr ds:[esi+0x4]
 *  011a7b4f   8947 1c          mov dword ptr ds:[edi+0x1c],eax
 *  011a7b52   0fb746 06        movzx eax,word ptr ds:[esi+0x6]
 *  011a7b56   8947 20          mov dword ptr ds:[edi+0x20],eax
 *  011a7b59   0fbf46 0c        movsx eax,word ptr ds:[esi+0xc]
 *  011a7b5d   8947 10          mov dword ptr ds:[edi+0x10],eax
 *  011a7b60   0fbf46 0e        movsx eax,word ptr ds:[esi+0xe]
 *  011a7b64   8947 14          mov dword ptr ds:[edi+0x14],eax
 *  011a7b67   0fbf46 08        movsx eax,word ptr ds:[esi+0x8]
 *  011a7b6b   0345 0c          add eax,dword ptr ss:[ebp+0xc]
 *  011a7b6e   8947 08          mov dword ptr ds:[edi+0x8],eax
 *  011a7b71   0fbf46 0a        movsx eax,word ptr ds:[esi+0xa]
 *  011a7b75   8b4d 10          mov ecx,dword ptr ss:[ebp+0x10]
 *  011a7b78   2bc8             sub ecx,eax
 *  011a7b7a   894f 0c          mov dword ptr ds:[edi+0xc],ecx
 *  011a7b7d   0fb643 20        movzx eax,byte ptr ds:[ebx+0x20]
 *  011a7b81   8847 30          mov byte ptr ds:[edi+0x30],al
 *  011a7b84   c647 32 00       mov byte ptr ds:[edi+0x32],0x0
 *  011a7b88   0fb643 21        movzx eax,byte ptr ds:[ebx+0x21]
 *  011a7b8c   8847 31          mov byte ptr ds:[edi+0x31],al
 *  011a7b8f   8b43 1c          mov eax,dword ptr ds:[ebx+0x1c]
 *  011a7b92   8947 28          mov dword ptr ds:[edi+0x28],eax
 *  011a7b95   8b43 18          mov eax,dword ptr ds:[ebx+0x18]
 *  011a7b98   8947 24          mov dword ptr ds:[edi+0x24],eax
 *  011a7b9b   c745 fc 01000000 mov dword ptr ss:[ebp-0x4],0x1
 *  011a7ba2   837f 1c 00       cmp dword ptr ds:[edi+0x1c],0x0
 *  011a7ba6   74 64            je short .011a7c0c
 *  011a7ba8   8b47 20          mov eax,dword ptr ds:[edi+0x20]
 *  011a7bab   85c0             test eax,eax
 *  011a7bad   74 5d            je short .011a7c0c
 *  011a7baf   0fb776 04        movzx esi,word ptr ds:[esi+0x4]
 *  011a7bb3   4e               dec esi
 *  011a7bb4   83e6 fc          and esi,0xfffffffc
 *  011a7bb7   83c6 04          add esi,0x4
 *  011a7bba   8977 18          mov dword ptr ds:[edi+0x18],esi
 *  011a7bbd   0fafc6           imul eax,esi
 *  011a7bc0   50               push eax
 *  011a7bc1   8bcf             mov ecx,edi
 *  011a7bc3   e8 b8f6ffff      call .011a7280
 *  011a7bc8   56               push esi
 *  011a7bc9   ff37             push dword ptr ds:[edi]
 *  011a7bcb   ff75 ec          push dword ptr ss:[ebp-0x14]
 *  011a7bce   8b4d 08          mov ecx,dword ptr ss:[ebp+0x8]
 *  011a7bd1   e8 3a090300      call .011d8510
 *  011a7bd6   807b 21 00       cmp byte ptr ds:[ebx+0x21],0x0
 *  011a7bda   74 0d            je short .011a7be9
 *  011a7bdc   ff77 28          push dword ptr ds:[edi+0x28]
 *  011a7bdf   ff77 24          push dword ptr ds:[edi+0x24]
 *  011a7be2   8bcf             mov ecx,edi
 *  011a7be4   e8 d70affff      call .011986c0
 *  011a7be9   897d ec          mov dword ptr ss:[ebp-0x14],edi
 *  011a7bec   ff47 04          inc dword ptr ds:[edi+0x4]
 *  011a7bef   c645 fc 02       mov byte ptr ss:[ebp-0x4],0x2
 *  011a7bf3   8d45 ec          lea eax,dword ptr ss:[ebp-0x14]
 *  011a7bf6   50               push eax
 *  011a7bf7   ff75 e4          push dword ptr ss:[ebp-0x1c]
 *  011a7bfa   53               push ebx
 *  011a7bfb   e8 50280000      call .011aa450
 *  011a7c00   c645 fc 01       mov byte ptr ss:[ebp-0x4],0x1
 *  011a7c04   8d4d ec          lea ecx,dword ptr ss:[ebp-0x14]
 *  011a7c07   e8 84280000      call .011aa490
 *  011a7c0c   c745 fc ffffffff mov dword ptr ss:[ebp-0x4],-0x1
 *  011a7c13   8bc7             mov eax,edi
 *  011a7c15   8b4d f4          mov ecx,dword ptr ss:[ebp-0xc]
 *  011a7c18   64:890d 00000000 mov dword ptr fs:[0],ecx
 *  011a7c1f   59               pop ecx
 *  011a7c20   5f               pop edi
 *  011a7c21   5e               pop esi
 *  011a7c22   5b               pop ebx
 *  011a7c23   8be5             mov esp,ebp
 *  011a7c25   5d               pop ebp
 *  011a7c26   c3               retn
 *  011a7c27   8b4d e8          mov ecx,dword ptr ss:[ebp-0x18]
 *  011a7c2a   e8 81f6ffff      call .011a72b0
 *  011a7c2f   6a 00            push 0x0
 *  011a7c31   6a 00            push 0x0
 *  011a7c33   e8 93cb1300      call .012e47cb
 *  011a7c38   a1 dc8a3d01      mov eax,dword ptr ds:[0x13d8adc]
 *  011a7c3d   8b0c85 88b93f01  mov ecx,dword ptr ds:[eax*4+0x13fb988]
 *  011a7c44   8b01             mov eax,dword ptr ds:[ecx]
 *  011a7c46   ff75 10          push dword ptr ss:[ebp+0x10]
 *  011a7c49   ff75 0c          push dword ptr ss:[ebp+0xc]
 *  011a7c4c   53               push ebx
 *  011a7c4d   ff50 1c          call dword ptr ds:[eax+0x1c]
 *  011a7c50   8bf0             mov esi,eax
 *  011a7c52   8975 e4          mov dword ptr ss:[ebp-0x1c],esi
 *  011a7c55   ff46 04          inc dword ptr ds:[esi+0x4]
 *  011a7c58   c745 fc 04000000 mov dword ptr ss:[ebp-0x4],0x4
 *  011a7c5f   8d45 e4          lea eax,dword ptr ss:[ebp-0x1c]
 *  011a7c62   50               push eax
 *  011a7c63   57               push edi
 *  011a7c64   53               push ebx
 *  011a7c65   e8 a62c0000      call .011aa910
 *  011a7c6a   a1 388b3f01      mov eax,dword ptr ds:[0x13f8b38]
 *  011a7c6f   8b0d 448b3f01    mov ecx,dword ptr ds:[0x13f8b44]
 *  011a7c75   3bc1             cmp eax,ecx
 *  011a7c77   76 08            jbe short .011a7c81
 *  011a7c79   2bc1             sub eax,ecx
 *  011a7c7b   50               push eax
 *  011a7c7c   e8 1f2e0000      call .011aaaa0
 *  011a7c81   c745 fc ffffffff mov dword ptr ss:[ebp-0x4],-0x1
 *  011a7c88   8b46 04          mov eax,dword ptr ds:[esi+0x4]
 *  011a7c8b   83f8 01          cmp eax,0x1
 *  011a7c8e   75 2c            jnz short .011a7cbc
 *  011a7c90   8b06             mov eax,dword ptr ds:[esi]
 *  011a7c92   85c0             test eax,eax
 *  011a7c94   74 09            je short .011a7c9f
 *  011a7c96   50               push eax
 *  011a7c97   e8 3b621300      call .012dded7
 *  011a7c9c   83c4 04          add esp,0x4
 *  011a7c9f   56               push esi
 *  011a7ca0   e8 335e1300      call .012ddad8
 *  011a7ca5   83c4 04          add esp,0x4
 *  011a7ca8   8bc6             mov eax,esi
 *  011a7caa   8b4d f4          mov ecx,dword ptr ss:[ebp-0xc]
 *  011a7cad   64:890d 00000000 mov dword ptr fs:[0],ecx
 *  011a7cb4   59               pop ecx
 *  011a7cb5   5f               pop edi
 *  011a7cb6   5e               pop esi
 *  011a7cb7   5b               pop ebx
 *  011a7cb8   8be5             mov esp,ebp
 *  011a7cba   5d               pop ebp
 *  011a7cbb   c3               retn
 *  011a7cbc   48               dec eax
 *  011a7cbd   8946 04          mov dword ptr ds:[esi+0x4],eax
 *  011a7cc0   8bc6             mov eax,esi
 *  011a7cc2   8b4d f4          mov ecx,dword ptr ss:[ebp-0xc]
 *  011a7cc5   64:890d 00000000 mov dword ptr fs:[0],ecx
 *  011a7ccc   59               pop ecx
 *  011a7ccd   5f               pop edi
 *  011a7cce   5e               pop esi
 *  011a7ccf   5b               pop ebx
 *  011a7cd0   8be5             mov esp,ebp
 *  011a7cd2   5d               pop ebp
 *  011a7cd3   c3               retn
 *  011a7cd4   cc               int3
 *  011a7cd5   cc               int3
 *  011a7cd6   cc               int3
 *  011a7cd7   cc               int3
 *  011a7cd8   cc               int3
 *
 *  Here's the hooked function in サノバウィッ� (150226).
 *  I randomly picked a pattern from VBH:
 *
 *    011a7a95   33c0             xor eax,eax
 *    011a7a97   3843 20          cmp byte ptr ds:[ebx+0x20],al
 *    011a7a9a   0f95c0           setne al
 *    011a7a9d   33c8             xor ecx,eax
 *    011a7a9f   334b 10          xor ecx,dword ptr ds:[ebx+0x10]
 *    011a7aa2   0fb743 14        movzx eax,word ptr ds:[ebx+0x14]
 *
 *  i.e: 33c03843200f95c033c8334b100fb74314
 *
 *  The new hooked function in サノバウィッ� is as follows.
 *
 *  012280dc   cc               int3
 *  012280dd   cc               int3
 *  012280de   cc               int3
 *  012280df   cc               int3
 *  012280e0   55               push ebp
 *  012280e1   8bec             mov ebp,esp
 *  012280e3   6a ff            push -0x1
 *  012280e5   68 3b813d01      push .013d813b
 *  012280ea   64:a1 00000000   mov eax,dword ptr fs:[0]
 *  012280f0   50               push eax
 *  012280f1   83ec 14          sub esp,0x14
 *  012280f4   53               push ebx
 *  012280f5   56               push esi
 *  012280f6   57               push edi
 *  012280f7   a1 00694901      mov eax,dword ptr ds:[0x1496900]
 *  012280fc   33c5             xor eax,ebp
 *  012280fe   50               push eax
 *  012280ff   8d45 f4          lea eax,dword ptr ss:[ebp-0xc]
 *  01228102   64:a3 00000000   mov dword ptr fs:[0],eax
 *  01228108   8965 f0          mov dword ptr ss:[ebp-0x10],esp
 *  0122810b   8bd9             mov ebx,ecx
 *  0122810d   803d e82d4a01 00 cmp byte ptr ds:[0x14a2de8],0x0
 *  01228114   75 17            jnz short .0122812d
 *  01228116   c745 e8 d8d44901 mov dword ptr ss:[ebp-0x18],.0149d4d8
 *  0122811d   8d45 e8          lea eax,dword ptr ss:[ebp-0x18]
 *  01228120   50               push eax
 *  01228121   e8 aadbf0ff      call .01135cd0
 *  01228126   c605 e82d4a01 01 mov byte ptr ds:[0x14a2de8],0x1
 *  0122812d   33c9             xor ecx,ecx
 *  0122812f   384b 21          cmp byte ptr ds:[ebx+0x21],cl
 *  01228132   0f95c1           setne cl
 *  01228135   33c0             xor eax,eax
 *  01228137   3843 20          cmp byte ptr ds:[ebx+0x20],al
 *  0122813a   0f95c0           setne al
 *  0122813d   33c8             xor ecx,eax
 *  0122813f   334b 10          xor ecx,dword ptr ds:[ebx+0x10]
 *  01228142   0fb743 14        movzx eax,word ptr ds:[ebx+0x14]
 *  01228146   33c8             xor ecx,eax
 *  01228148   8b7b 1c          mov edi,dword ptr ds:[ebx+0x1c]
 *  0122814b   33f9             xor edi,ecx
 *  0122814d   337b 18          xor edi,dword ptr ds:[ebx+0x18]
 *  01228150   897d e4          mov dword ptr ss:[ebp-0x1c],edi
 *  01228153   57               push edi
 *  01228154   53               push ebx
 *  01228155   e8 06330000      call .0122b460
 *  0122815a   8bf0             mov esi,eax
 *  0122815c   85f6             test esi,esi
 *  0122815e   74 26            je short .01228186
 *  01228160   56               push esi
 *  01228161   e8 ba330000      call .0122b520
 *  01228166   8d46 2c          lea eax,dword ptr ds:[esi+0x2c]
 *  01228169   85c0             test eax,eax
 *  0122816b   74 19            je short .01228186
 *  0122816d   8b08             mov ecx,dword ptr ds:[eax]
 *  0122816f   ff41 04          inc dword ptr ds:[ecx+0x4]
 *  01228172   8b00             mov eax,dword ptr ds:[eax]
 *  01228174   8b4d f4          mov ecx,dword ptr ss:[ebp-0xc]
 *  01228177   64:890d 00000000 mov dword ptr fs:[0],ecx
 *  0122817e   59               pop ecx
 *  0122817f   5f               pop edi
 *  01228180   5e               pop esi
 *  01228181   5b               pop ebx
 *  01228182   8be5             mov esp,ebp
 *  01228184   5d               pop ebp
 *  01228185   c3               retn
 *  01228186   8b4d 08          mov ecx,dword ptr ss:[ebp+0x8]
 *  01228189   85c9             test ecx,ecx
 *  0122818b   0f84 47010000    je .012282d8
 *  01228191   0fb743 14        movzx eax,word ptr ds:[ebx+0x14]
 *  01228195   50               push eax
 *  01228196   e8 950f0300      call .01259130
 *  0122819b   8bf0             mov esi,eax
 *  0122819d   8975 ec          mov dword ptr ss:[ebp-0x14],esi
 *  012281a0   85f6             test esi,esi
 *  012281a2   0f84 30010000    je .012282d8
 *  012281a8   6a 34            push 0x34
 *  012281aa   e8 297c1300      call .0135fdd8
 *  012281af   83c4 04          add esp,0x4
 *  012281b2   8bf8             mov edi,eax
 *  012281b4   897d e0          mov dword ptr ss:[ebp-0x20],edi
 *  012281b7   c745 fc 00000000 mov dword ptr ss:[ebp-0x4],0x0
 *  012281be   85ff             test edi,edi
 *  012281c0   74 1d            je short .012281df
 *  012281c2   c747 2c 41000000 mov dword ptr ds:[edi+0x2c],0x41
 *  012281c9   c647 32 00       mov byte ptr ds:[edi+0x32],0x0
 *  012281cd   c747 04 01000000 mov dword ptr ds:[edi+0x4],0x1
 *  012281d4   c707 00000000    mov dword ptr ds:[edi],0x0
 *  012281da   8945 e8          mov dword ptr ss:[ebp-0x18],eax
 *  012281dd   eb 05            jmp short .012281e4
 *  012281df   33ff             xor edi,edi
 *  012281e1   897d e8          mov dword ptr ss:[ebp-0x18],edi
 *  012281e4   c745 fc ffffffff mov dword ptr ss:[ebp-0x4],-0x1
 *  012281eb   0fb746 04        movzx eax,word ptr ds:[esi+0x4]
 *  012281ef   8947 1c          mov dword ptr ds:[edi+0x1c],eax
 *  012281f2   0fb746 06        movzx eax,word ptr ds:[esi+0x6]
 *  012281f6   8947 20          mov dword ptr ds:[edi+0x20],eax
 *  012281f9   0fbf46 0c        movsx eax,word ptr ds:[esi+0xc]
 *  012281fd   8947 10          mov dword ptr ds:[edi+0x10],eax
 *  01228200   0fbf46 0e        movsx eax,word ptr ds:[esi+0xe]
 *  01228204   8947 14          mov dword ptr ds:[edi+0x14],eax
 *  01228207   0fbf46 08        movsx eax,word ptr ds:[esi+0x8]
 *  0122820b   0345 0c          add eax,dword ptr ss:[ebp+0xc]
 *  0122820e   8947 08          mov dword ptr ds:[edi+0x8],eax
 *  01228211   0fbf46 0a        movsx eax,word ptr ds:[esi+0xa]
 *  01228215   8b4d 10          mov ecx,dword ptr ss:[ebp+0x10]
 *  01228218   2bc8             sub ecx,eax
 *  0122821a   894f 0c          mov dword ptr ds:[edi+0xc],ecx
 *  0122821d   0fb643 20        movzx eax,byte ptr ds:[ebx+0x20]
 *  01228221   8847 30          mov byte ptr ds:[edi+0x30],al
 *  01228224   c647 32 00       mov byte ptr ds:[edi+0x32],0x0
 *  01228228   0fb643 21        movzx eax,byte ptr ds:[ebx+0x21]
 *  0122822c   8847 31          mov byte ptr ds:[edi+0x31],al
 *  0122822f   8b43 1c          mov eax,dword ptr ds:[ebx+0x1c]
 *  01228232   8947 28          mov dword ptr ds:[edi+0x28],eax
 *  01228235   8b43 18          mov eax,dword ptr ds:[ebx+0x18]
 *  01228238   8947 24          mov dword ptr ds:[edi+0x24],eax
 *  0122823b   c745 fc 01000000 mov dword ptr ss:[ebp-0x4],0x1
 *  01228242   837f 1c 00       cmp dword ptr ds:[edi+0x1c],0x0
 *  01228246   74 64            je short .012282ac
 *  01228248   8b47 20          mov eax,dword ptr ds:[edi+0x20]
 *  0122824b   85c0             test eax,eax
 *  0122824d   74 5d            je short .012282ac
 *  0122824f   0fb776 04        movzx esi,word ptr ds:[esi+0x4]
 *  01228253   4e               dec esi
 *  01228254   83e6 fc          and esi,0xfffffffc
 *  01228257   83c6 04          add esi,0x4
 *  0122825a   8977 18          mov dword ptr ds:[edi+0x18],esi
 *  0122825d   0fafc6           imul eax,esi
 *  01228260   50               push eax
 *  01228261   8bcf             mov ecx,edi
 *  01228263   e8 a8f6ffff      call .01227910
 *  01228268   56               push esi
 *  01228269   ff37             push dword ptr ds:[edi]
 *  0122826b   ff75 ec          push dword ptr ss:[ebp-0x14]
 *  0122826e   8b4d 08          mov ecx,dword ptr ss:[ebp+0x8]
 *  01228271   e8 1a0f0300      call .01259190
 *  01228276   807b 21 00       cmp byte ptr ds:[ebx+0x21],0x0
 *  0122827a   74 0d            je short .01228289
 *  0122827c   ff77 28          push dword ptr ds:[edi+0x28]
 *  0122827f   ff77 24          push dword ptr ds:[edi+0x24]
 *  01228282   8bcf             mov ecx,edi
 *  01228284   e8 870affff      call .01218d10
 *  01228289   897d ec          mov dword ptr ss:[ebp-0x14],edi
 *  0122828c   ff47 04          inc dword ptr ds:[edi+0x4]
 *  0122828f   c645 fc 02       mov byte ptr ss:[ebp-0x4],0x2
 *  01228293   8d45 ec          lea eax,dword ptr ss:[ebp-0x14]
 *  01228296   50               push eax
 *  01228297   ff75 e4          push dword ptr ss:[ebp-0x1c]
 *  0122829a   53               push ebx
 *  0122829b   e8 50280000      call .0122aaf0
 *  012282a0   c645 fc 01       mov byte ptr ss:[ebp-0x4],0x1
 *  012282a4   8d4d ec          lea ecx,dword ptr ss:[ebp-0x14]
 *  012282a7   e8 84280000      call .0122ab30
 *  012282ac   c745 fc ffffffff mov dword ptr ss:[ebp-0x4],-0x1
 *  012282b3   8bc7             mov eax,edi
 *  012282b5   8b4d f4          mov ecx,dword ptr ss:[ebp-0xc]
 *  012282b8   64:890d 00000000 mov dword ptr fs:[0],ecx
 *  012282bf   59               pop ecx
 *  012282c0   5f               pop edi
 *  012282c1   5e               pop esi
 *  012282c2   5b               pop ebx
 *  012282c3   8be5             mov esp,ebp
 *  012282c5   5d               pop ebp
 *  012282c6   c3               retn
 *  012282c7   8b4d e8          mov ecx,dword ptr ss:[ebp-0x18]
 *  012282ca   e8 71f6ffff      call .01227940
 *  012282cf   6a 00            push 0x0
 *  012282d1   6a 00            push 0x0
 *  012282d3   e8 83eb1300      call .01366e5b
 *  012282d8   a1 e89a4901      mov eax,dword ptr ds:[0x1499ae8]
 *  012282dd   8b0c85 f0d64b01  mov ecx,dword ptr ds:[eax*4+0x14bd6f0]
 *  012282e4   8b01             mov eax,dword ptr ds:[ecx]
 *  012282e6   ff75 10          push dword ptr ss:[ebp+0x10]
 *  012282e9   ff75 0c          push dword ptr ss:[ebp+0xc]
 *  012282ec   53               push ebx
 *  012282ed   ff50 1c          call dword ptr ds:[eax+0x1c]
 *  012282f0   8bf0             mov esi,eax
 *  012282f2   8975 e4          mov dword ptr ss:[ebp-0x1c],esi
 *  012282f5   ff46 04          inc dword ptr ds:[esi+0x4]
 *  012282f8   c745 fc 04000000 mov dword ptr ss:[ebp-0x4],0x4
 *  012282ff   8d45 e4          lea eax,dword ptr ss:[ebp-0x1c]
 *  01228302   50               push eax
 *  01228303   57               push edi
 *  01228304   53               push ebx
 *  01228305   e8 a62c0000      call .0122afb0
 *  0122830a   a1 a0a84b01      mov eax,dword ptr ds:[0x14ba8a0]
 *  0122830f   8b0d aca84b01    mov ecx,dword ptr ds:[0x14ba8ac]
 *  01228315   3bc1             cmp eax,ecx
 *  01228317   76 08            jbe short .01228321
 *  01228319   2bc1             sub eax,ecx
 *  0122831b   50               push eax
 *  0122831c   e8 1f2e0000      call .0122b140
 *  01228321   c745 fc ffffffff mov dword ptr ss:[ebp-0x4],-0x1
 *  01228328   8b46 04          mov eax,dword ptr ds:[esi+0x4]
 *  0122832b   83f8 01          cmp eax,0x1
 *  0122832e   75 2c            jnz short .0122835c
 *  01228330   8b06             mov eax,dword ptr ds:[esi]
 *  01228332   85c0             test eax,eax
 *  01228334   74 09            je short .0122833f
 *  01228336   50               push eax
 *  01228337   e8 3b7c1300      call .0135ff77
 *  0122833c   83c4 04          add esp,0x4
 *  0122833f   56               push esi
 *  01228340   e8 33781300      call .0135fb78
 *  01228345   83c4 04          add esp,0x4
 *  01228348   8bc6             mov eax,esi
 *  0122834a   8b4d f4          mov ecx,dword ptr ss:[ebp-0xc]
 *  0122834d   64:890d 00000000 mov dword ptr fs:[0],ecx
 *  01228354   59               pop ecx
 *  01228355   5f               pop edi
 *  01228356   5e               pop esi
 *  01228357   5b               pop ebx
 *  01228358   8be5             mov esp,ebp
 *  0122835a   5d               pop ebp
 *  0122835b   c3               retn
 *  0122835c   48               dec eax
 *  0122835d   8946 04          mov dword ptr ds:[esi+0x4],eax
 *  01228360   8bc6             mov eax,esi
 *  01228362   8b4d f4          mov ecx,dword ptr ss:[ebp-0xc]
 *  01228365   64:890d 00000000 mov dword ptr fs:[0],ecx
 *  0122836c   59               pop ecx
 *  0122836d   5f               pop edi
 *  0122836e   5e               pop esi
 *  0122836f   5b               pop ebx
 *  01228370   8be5             mov esp,ebp
 *  01228372   5d               pop ebp
 *  01228373   c3               retn
 *  01228374   cc               int3
 *  01228375   cc               int3
 *  01228376   cc               int3
 *  01228377   cc               int3
 *  01228378   cc               int3
 */

namespace { // unnamed

// Skip individual L'\n' which might cause repetition.
//bool NewLineWideCharSkipper(LPVOID data, DWORD *size, HookParam *)
//{
//  LPCWSTR text = (LPCWSTR)data;
//  if (*size == 2 && *text == L'\n')
//    return false;
//  return true;
//}
//

void NewKiriKiriZHook(DWORD addr)
{
  HookParam hp = {};
  hp.address = addr;
  hp.offset = pusha_ecx_off - 4;
  hp.split = hp.offset;    // the same logic but diff value as KiriKiri1, use [ecx] as split
  hp.index = 0x14;        // the same as KiriKiri1
  hp.length_offset = 1; // the same as KiriKiri1
  hp.type = USING_UNICODE|DATA_INDIRECT|USING_SPLIT|SPLIT_INDIRECT;
  //hp.filter_fun = NewLineWideCharFilter;
  ConsoleOutput("vnreng: INSERT KiriKiriZ");
  NewHook(hp, "KiriKiriZ");

  ConsoleOutput("vnreng:KiriKiriZ: disable GDI hooks");
  DisableGDIHooks();
}

bool KiriKiriZHook1(DWORD esp_base, HookParam *)
{
  DWORD addr = retof(esp_base); // retaddr
  addr = MemDbg::findEnclosingAlignedFunction(addr, 0x400); // range is around 0x377c50 - 0x377a40 = 0x210
  if (!addr) {
    ConsoleOutput("vnreng:KiriKiriZ: failed to find enclosing function");
    return false; // stop looking
  }
  NewKiriKiriZHook(addr);
  ConsoleOutput("vnreng: KiriKiriZ1 inserted");
  return false; // stop looking
}

bool InsertKiriKiriZHook1()
{
  ULONG startAddress, stopAddress;
  if (!NtInspect::getProcessMemoryRange(&startAddress, &stopAddress)) { // need accurate stopAddress
    ConsoleOutput("vnreng:KiriKiriZ1: failed to get memory range");
    return false;
  }

  ULONG addr = MemDbg::findCallerAddressAfterInt3((DWORD)::GetGlyphOutlineW, startAddress, stopAddress);
  if (!addr) {
    ConsoleOutput("vnreng:KiriKiriZ1: could not find caller of GetGlyphOutlineW");
    return false;
  }

  HookParam hp = {};
  hp.address = addr;
  hp.type = HOOK_EMPTY;
  hp.hook_fun = KiriKiriZHook1;
  ConsoleOutput("vnreng: INSERT KiriKiriZ1 empty hook");
  NewHook(hp, "KiriKiriZ Hook");
  return true;
}


// jichi 1/30/2015: Add KiriKiriZ2 for サノバウィッ�
// It inserts to the same location as the old KiriKiriZ, but use a different way to find it.
bool InsertKiriKiriZHook2()
{
  const BYTE bytes[] = {
    0x38,0x4b, 0x21,     // 0122812f   384b 21          cmp byte ptr ds:[ebx+0x21],cl
    0x0f,0x95,0xc1,      // 01228132   0f95c1           setne cl
    0x33,0xc0,           // 01228135   33c0             xor eax,eax
    0x38,0x43, 0x20,     // 01228137   3843 20          cmp byte ptr ds:[ebx+0x20],al
    0x0f,0x95,0xc0,      // 0122813a   0f95c0           setne al
    0x33,0xc8,           // 0122813d   33c8             xor ecx,eax
    0x33,0x4b, 0x10,     // 0122813f   334b 10          xor ecx,dword ptr ds:[ebx+0x10]
    0x0f,0xb7,0x43, 0x14 // 01228142   0fb743 14        movzx eax,word ptr ds:[ebx+0x14]
  };
  ULONG range = min(module_limit_ - module_base_, MAX_REL_ADDR);
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_base_ + range);
  //GROWL_DWORD(addr);
  if (!addr) {
    ConsoleOutput("vnreng:KiriKiriZ2: pattern not found");
    return false;
  }

  // 012280e0   55               push ebp
  // 012280e1   8bec             mov ebp,esp
  addr = MemDbg::findEnclosingAlignedFunction(addr, 0x100); // 0x0122812f-0x012280e0 = 0x4F
  enum : BYTE { push_ebp = 0x55 };  // 011d4c80  /$ 55             push ebp
  if (!addr || *(BYTE *)addr != push_ebp) {
    ConsoleOutput("vnreng:KiriKiriZ2: pattern found but the function offset is invalid");
    return false;
  }

  NewKiriKiriZHook(addr);
  ConsoleOutput("vnreng: KiriKiriZ2 inserted");
  return true;
}

} // unnamed namespace

// jichi 1/30/2015: Do KiriKiriZ2 first, which might insert to the same location as KiriKiri1.
bool InsertKiriKiriZHook()
{ return InsertKiriKiriZHook2() || InsertKiriKiriZHook1(); }

/********************************************************************************************
BGI hook:
  Usually game folder contains BGI.*. After first run BGI.gdb appears.

  BGI engine has font caching issue so the strategy is simple.
  First find call to TextOutA or TextOutW then reverse to function entry point,
  until full text is caught.
  After 2 tries we will get to the right place. Use ESP value to split text since
  it's likely to be different for different calls.
********************************************************************************************/
namespace { // unnamed
#if 0 // jichi 12/28/2013: dynamic BGI is not used
static bool FindBGIHook(DWORD fun, DWORD size, DWORD pt, WORD sig)
{
  if (!fun) {
    ConsoleOutput("vnreng:BGI: cannot find BGI hook");
    //swprintf(str, L"Can't find BGI hook: %.8X.",fun);
    //ConsoleOutput(str);
    return false;
  }
  //WCHAR str[0x40];
  //i=FindCallBoth(fun,size,pt);

  //swprintf(str, L"CALL addr: 0x%.8X",pt+i);
  //ConsoleOutput(str);
  for (DWORD i = fun, j = fun; j > i - 0x100; j--)
    if ((*(WORD *)(pt + j)) == sig) { // Fun entry 1.
      //swprintf(str, L"Entry 1: 0x%.8X",pt+j);
      //ConsoleOutput(str);
      for (DWORD k = i + 0x100; k < i+0x800; k++)
        if (*(BYTE *)(pt + k) == 0xe8)
          if (k + 5 + *(DWORD *)(pt + k + 1) == j) { // Find call to fun1.
            //swprintf(str, L"CALL to entry 1: 0x%.8X",pt+k);
            //ConsoleOutput(str);
            for (DWORD l = k; l > k - 0x100;l--)
              if ((*(WORD *)(pt + l)) == 0xec83) { // Fun entry 2.
                //swprintf(str, L"Entry 2(final): 0x%.8X",pt+l);
                //ConsoleOutput(str);
                HookParam hp = {};
                hp.address = (DWORD)pt + l;
                hp.offset = 0x8;
                hp.split = -0x18;
                hp.length_offset = 1;
                hp.type = BIG_ENDIAN|USING_SPLIT;
                ConsoleOutput("vnreng:INSERT DynamicBGI");
                NewHook(hp, "BGI");
                return true;
              }
          }
    }
  ConsoleOutput("vnreng:DynamicBGI: failed");
  return false;
}
bool InsertBGIDynamicHook(LPVOID addr, DWORD frame, DWORD stack)
{
  if (addr != TextOutA && addr != TextOutW)  {
    //ConsoleOutput("vnreng:DynamicBGI: failed");
    return false;
  }

  DWORD i = *(DWORD *)(stack + 4) - module_base_;
  return FindBGIHook(i, module_limit_ - module_base_, module_base_, 0xec83);
}
#endif // 0

/** jichi 5/12/2014
 *  Sample game:  FORTUNE ARTERIAL, case 2 at 0x41ebd0
 *
 *  sub_41EBD0 proc near, seems to take 5 parameters
 *
 *  0041ebd0  /$ 83ec 28        sub esp,0x28 ; jichi: hook here, beginning of the function
 *  0041ebd3  |. 55             push ebp
 *  0041ebd4  |. 8b6c24 38      mov ebp,dword ptr ss:[esp+0x38]
 *  0041ebd8  |. 81fd 00ff0000  cmp ebp,0xff00
 *  0041ebde  |. 0f82 e1000000  jb bgi.0041ecc5
 *  0041ebe4  |. 81fd ffff0000  cmp ebp,0xffff
 *  0041ebea  |. 0f87 d5000000  ja bgi.0041ecc5
 *  0041ebf0  |. a1 54634900    mov eax,dword ptr ds:[0x496354]
 *  0041ebf5  |. 8bd5           mov edx,ebp
 *  0041ebf7  |. 81e2 ff000000  and edx,0xff
 *  0041ebfd  |. 53             push ebx
 *  0041ebfe  |. 4a             dec edx
 *  0041ebff  |. 33db           xor ebx,ebx
 *  0041ec01  |. 3bd0           cmp edx,eax
 *  0041ec03  |. 56             push esi
 *  0041ec04  |. 0f8d 8a000000  jge bgi.0041ec94
 *  0041ec0a  |. 57             push edi
 *  0041ec0b  |. b9 06000000    mov ecx,0x6
 *  0041ec10  |. be 5c634900    mov esi,bgi.0049635c
 *  0041ec15  |. 8d7c24 20      lea edi,dword ptr ss:[esp+0x20]
 *  0041ec19  |. f3:a5          rep movs dword ptr es:[edi],dword ptr ds>
 *  0041ec1b  |. 8b0d 58634900  mov ecx,dword ptr ds:[0x496358]
 *  0041ec21  |. 8b7424 3c      mov esi,dword ptr ss:[esp+0x3c]
 *  0041ec25  |. 8bc1           mov eax,ecx
 *  0041ec27  |. 5f             pop edi
 *  0041ec28  |. 0fafc2         imul eax,edx
 *  0041ec2b  |. 8b56 08        mov edx,dword ptr ds:[esi+0x8]
 *  0041ec2e  |. 894424 0c      mov dword ptr ss:[esp+0xc],eax
 *  0041ec32  |. 3bca           cmp ecx,edx
 *  0041ec34  |. 7e 02          jle short bgi.0041ec38
 *  0041ec36  |. 8bca           mov ecx,edx
 *  0041ec38  |> 8d4401 ff      lea eax,dword ptr ds:[ecx+eax-0x1]
 *  0041ec3c  |. 8b4c24 28      mov ecx,dword ptr ss:[esp+0x28]
 *  0041ec40  |. 894424 14      mov dword ptr ss:[esp+0x14],eax
 *  0041ec44  |. 8b46 0c        mov eax,dword ptr ds:[esi+0xc]
 *  0041ec47  |. 3bc8           cmp ecx,eax
 *  0041ec49  |. 895c24 10      mov dword ptr ss:[esp+0x10],ebx
 *  0041ec4d  |. 77 02          ja short bgi.0041ec51
 *  0041ec4f  |. 8bc1           mov eax,ecx
 *  0041ec51  |> 8d4c24 0c      lea ecx,dword ptr ss:[esp+0xc]
 *  0041ec55  |. 8d5424 1c      lea edx,dword ptr ss:[esp+0x1c]
 *  0041ec59  |. 48             dec eax
 *  0041ec5a  |. 51             push ecx
 *  0041ec5b  |. 52             push edx
 *  0041ec5c  |. 894424 20      mov dword ptr ss:[esp+0x20],eax
 *  0041ec60  |. e8 7b62feff    call bgi.00404ee0
 *  0041ec65  |. 8b4424 34      mov eax,dword ptr ss:[esp+0x34]
 *  0041ec69  |. 83c4 08        add esp,0x8
 *  0041ec6c  |. 83f8 03        cmp eax,0x3
 *  0041ec6f  |. 75 15          jnz short bgi.0041ec86
 *  0041ec71  |. 8b4424 48      mov eax,dword ptr ss:[esp+0x48]
 *  0041ec75  |. 8d4c24 1c      lea ecx,dword ptr ss:[esp+0x1c]
 *  0041ec79  |. 50             push eax
 *  0041ec7a  |. 51             push ecx
 *  0041ec7b  |. 56             push esi
 *  0041ec7c  |. e8 1fa0feff    call bgi.00408ca0
 */
bool InsertBGI1Hook()
{
  union {
    DWORD i;
    DWORD *id;
    BYTE *ib;
  };
  HookParam hp = {};
  for (i = module_base_ + 0x1000; i < module_limit_; i++) {
    if (ib[0] == 0x3d) {
      i++;
      if (id[0] == 0xffff) { //cmp eax,0xffff
        hp.address = SafeFindEntryAligned(i, 0x40);
        if (hp.address) {
          hp.offset = 0xc;
          hp.split = -0x18;
          hp.type = BIG_ENDIAN|USING_SPLIT;
          hp.length_offset = 1;
          ConsoleOutput("vnreng:INSERT BGI#1");
          NewHook(hp, "BGI");
          //RegisterEngineType(ENGINE_BGI);
          return true;
        }
      }
    }
    if (ib[0] == 0x81 && ((ib[1] & 0xf8) == 0xf8)) {
      i += 2;
      if (id[0] == 0xffff) { //cmp reg,0xffff
        hp.address = SafeFindEntryAligned(i, 0x40);
        if (hp.address) {
          hp.offset = 0xc;
          hp.split = -0x18;
          hp.type = BIG_ENDIAN|USING_SPLIT;
          hp.length_offset = 1;
          ConsoleOutput("vnreng: INSERT BGI#2");
          NewHook(hp, "BGI");
          //RegisterEngineType(ENGINE_BGI);
          return true;
        }
      }
    }
  }
  //ConsoleOutput("Unknown BGI engine.");

  //ConsoleOutput("Probably BGI. Wait for text.");
  //SwitchTrigger(true);
  //trigger_fun_=InsertBGIDynamicHook;
  ConsoleOutput("vnreng:BGI: failed");
  return false;
}

/**
 *  jichi 2/5/2014: Add an alternative BGI hook
 *
 *  Issue: This hook cannot extract character name for コトバの消えた日
 *
 *  See: http://tieba.baidu.com/p/2845113296
 *  世界と世界の真ん中で
 *  - /HSN4@349E0:sekachu.exe // Disabled BGI3, floating split char
 *  - /HS-1C:-4@68E56 // Not used, cannot detect character name
 *  - /HSC@34C80:sekachu.exe  // BGI2, extract both scenario and character names
 *
 *  [Lump of Sugar] 世界と世界の真ん中で
 *  /HSC@34C80:sekachu.exe
 *  - addr: 216192 = 0x34c80
 *  - module: 3599131534
 *  - off: 12 = 0xc
 *  - type: 65 = 0x41
 *
 *  base: 0x11a0000
 *  hook_addr = base + addr = 0x11d4c80
 *
 *  011d4c7e     cc             int3
 *  011d4c7f     cc             int3
 *  011d4c80  /$ 55             push ebp    ; jichi: hook here
 *  011d4c81  |. 8bec           mov ebp,esp
 *  011d4c83  |. 6a ff          push -0x1
 *  011d4c85  |. 68 e6592601    push sekachu.012659e6
 *  011d4c8a  |. 64:a1 00000000 mov eax,dword ptr fs:[0]
 *  011d4c90  |. 50             push eax
 *  011d4c91  |. 81ec 300d0000  sub esp,0xd30
 *  011d4c97  |. a1 d8c82801    mov eax,dword ptr ds:[0x128c8d8]
 *  011d4c9c  |. 33c5           xor eax,ebp
 *  011d4c9e  |. 8945 f0        mov dword ptr ss:[ebp-0x10],eax
 *  011d4ca1  |. 53             push ebx
 *  011d4ca2  |. 56             push esi
 *  011d4ca3  |. 57             push edi
 *  011d4ca4  |. 50             push eax
 *  011d4ca5  |. 8d45 f4        lea eax,dword ptr ss:[ebp-0xc]
 *  011d4ca8  |. 64:a3 00000000 mov dword ptr fs:[0],eax
 *  011d4cae  |. 8b4d 0c        mov ecx,dword ptr ss:[ebp+0xc]
 *  011d4cb1  |. 8b55 18        mov edx,dword ptr ss:[ebp+0x18]
 *  011d4cb4  |. 8b45 08        mov eax,dword ptr ss:[ebp+0x8]
 *  011d4cb7  |. 8b5d 10        mov ebx,dword ptr ss:[ebp+0x10]
 *  011d4cba  |. 8b7d 38        mov edi,dword ptr ss:[ebp+0x38]
 *  011d4cbd  |. 898d d8f3ffff  mov dword ptr ss:[ebp-0xc28],ecx
 *  011d4cc3  |. 8b4d 28        mov ecx,dword ptr ss:[ebp+0x28]
 *  011d4cc6  |. 8995 9cf3ffff  mov dword ptr ss:[ebp-0xc64],edx
 *  011d4ccc  |. 51             push ecx
 *  011d4ccd  |. 8b0d 305c2901  mov ecx,dword ptr ds:[0x1295c30]
 *  011d4cd3  |. 8985 e0f3ffff  mov dword ptr ss:[ebp-0xc20],eax
 *  011d4cd9  |. 8b45 1c        mov eax,dword ptr ss:[ebp+0x1c]
 *  011d4cdc  |. 8d95 4cf4ffff  lea edx,dword ptr ss:[ebp-0xbb4]
 *  011d4ce2  |. 52             push edx
 *  011d4ce3  |. 899d 40f4ffff  mov dword ptr ss:[ebp-0xbc0],ebx
 *  011d4ce9  |. 8985 1cf4ffff  mov dword ptr ss:[ebp-0xbe4],eax
 *  011d4cef  |. 89bd f0f3ffff  mov dword ptr ss:[ebp-0xc10],edi
 *  011d4cf5  |. e8 862efdff    call sekachu.011a7b80
 *  011d4cfa  |. 33c9           xor ecx,ecx
 *  011d4cfc  |. 8985 60f3ffff  mov dword ptr ss:[ebp-0xca0],eax
 *  011d4d02  |. 3bc1           cmp eax,ecx
 *  011d4d04  |. 0f84 0f1c0000  je sekachu.011d6919
 *  011d4d0a  |. e8 31f6ffff    call sekachu.011d4340
 *  011d4d0f  |. e8 6cf8ffff    call sekachu.011d4580
 *  011d4d14  |. 8985 64f3ffff  mov dword ptr ss:[ebp-0xc9c],eax
 *  011d4d1a  |. 8a03           mov al,byte ptr ds:[ebx]
 *  011d4d1c  |. 898d 90f3ffff  mov dword ptr ss:[ebp-0xc70],ecx
 *  011d4d22  |. 898d 14f4ffff  mov dword ptr ss:[ebp-0xbec],ecx
 *  011d4d28  |. 898d 38f4ffff  mov dword ptr ss:[ebp-0xbc8],ecx
 *  011d4d2e  |. 8d71 01        lea esi,dword ptr ds:[ecx+0x1]
 *  011d4d31  |. 3c 20          cmp al,0x20                 ; jichi: pattern starts
 *  011d4d33  |. 7d 75          jge short sekachu.011d4daa
 *  011d4d35  |. 0fbec0         movsx eax,al
 *  011d4d38  |. 83c0 fe        add eax,-0x2                             ;  switch (cases 2..8)
 *  011d4d3b  |. 83f8 06        cmp eax,0x6
 *  011d4d3e  |. 77 6a          ja short sekachu.011d4daa
 *  011d4d40  |. ff2485 38691d0>jmp dword ptr ds:[eax*4+0x11d6938]
 *
 *  蒼の彼方 体験版 (8/6/2014)
 *  01312cce     cc             int3    ; jichi: reladdr = 0x32cd0
 *  01312ccf     cc             int3
 *  01312cd0   $ 55             push ebp
 *  01312cd1   . 8bec           mov ebp,esp
 *  01312cd3   . 83e4 f8        and esp,0xfffffff8
 *  01312cd6   . 6a ff          push -0x1
 *  01312cd8   . 68 86583a01    push 蒼の彼方.013a5886
 *  01312cdd   . 64:a1 00000000 mov eax,dword ptr fs:[0]
 *  01312ce3   . 50             push eax
 *  01312ce4   . 81ec 38090000  sub esp,0x938
 *  01312cea   . a1 24673c01    mov eax,dword ptr ds:[0x13c6724]
 *  01312cef   . 33c4           xor eax,esp
 *  01312cf1   . 898424 3009000>mov dword ptr ss:[esp+0x930],eax
 *  01312cf8   . 53             push ebx
 *  01312cf9   . 56             push esi
 *  01312cfa   . 57             push edi
 *  01312cfb   . a1 24673c01    mov eax,dword ptr ds:[0x13c6724]
 *  01312d00   . 33c4           xor eax,esp
 *  01312d02   . 50             push eax
 *  01312d03   . 8d8424 4809000>lea eax,dword ptr ss:[esp+0x948]
 *  01312d0a   . 64:a3 00000000 mov dword ptr fs:[0],eax
 *  01312d10   . 8b45 08        mov eax,dword ptr ss:[ebp+0x8]
 *  01312d13   . 8b7d 0c        mov edi,dword ptr ss:[ebp+0xc]
 *  01312d16   . 8b5d 30        mov ebx,dword ptr ss:[ebp+0x30]
 *  01312d19   . 898424 8800000>mov dword ptr ss:[esp+0x88],eax
 *  01312d20   . 8b45 14        mov eax,dword ptr ss:[ebp+0x14]
 *  01312d23   . 898c24 8c00000>mov dword ptr ss:[esp+0x8c],ecx
 *  01312d2a   . 8b0d a8734a01  mov ecx,dword ptr ds:[0x14a73a8]
 *  01312d30   . 894424 4c      mov dword ptr ss:[esp+0x4c],eax
 *  01312d34   . 899424 bc00000>mov dword ptr ss:[esp+0xbc],edx
 *  01312d3b   . 8b55 20        mov edx,dword ptr ss:[ebp+0x20]
 *  01312d3e   . 51             push ecx                                 ; /arg1 => 00000000
 *  01312d3f   . 8d8424 0c02000>lea eax,dword ptr ss:[esp+0x20c]         ; |
 *  01312d46   . 897c24 34      mov dword ptr ss:[esp+0x34],edi          ; |
 *  01312d4a   . 899c24 8800000>mov dword ptr ss:[esp+0x88],ebx          ; |
 *  01312d51   . e8 ca59fdff    call 蒼の彼方.012e8720                       ; \蒼の彼方.012e8720
 *  01312d56   . 33c9           xor ecx,ecx
 *  01312d58   . 898424 f400000>mov dword ptr ss:[esp+0xf4],eax
 *  01312d5f   . 3bc1           cmp eax,ecx
 *  01312d61   . 0f84 391b0000  je 蒼の彼方.013148a0
 *  01312d67   . e8 54280000    call 蒼の彼方.013155c0
 *  01312d6c   . e8 7f2a0000    call 蒼の彼方.013157f0
 *  01312d71   . 898424 f800000>mov dword ptr ss:[esp+0xf8],eax
 *  01312d78   . 8a07           mov al,byte ptr ds:[edi]
 *  01312d7a   . 898c24 c400000>mov dword ptr ss:[esp+0xc4],ecx
 *  01312d81   . 894c24 2c      mov dword ptr ss:[esp+0x2c],ecx
 *  01312d85   . 894c24 1c      mov dword ptr ss:[esp+0x1c],ecx
 *  01312d89   . b9 01000000    mov ecx,0x1
 *  01312d8e   . 3c 20          cmp al,0x20     ; jichi: pattern starts
 *  01312d90   . 7d 58          jge short 蒼の彼方.01312dea
 *  01312d92   . 0fbec0         movsx eax,al
 *  01312d95   . 83c0 fe        add eax,-0x2                             ;  switch (cases 2..8)
 *  01312d98   . 83f8 06        cmp eax,0x6
 *  01312d9b   . 77 4d          ja short 蒼の彼方.01312dea
 *  01312d9d   . ff2485 c448310>jmp dword ptr ds:[eax*4+0x13148c4]
 *  01312da4   > 898c24 c400000>mov dword ptr ss:[esp+0xc4],ecx          ;  case 2 of switch 01312d95
 *  01312dab   . 03f9           add edi,ecx
 *  01312dad   . eb 37          jmp short 蒼の彼方.01312de6
 *  01312daf   > 894c24 2c      mov dword ptr ss:[esp+0x2c],ecx          ;  case 3 of switch 01312d95
 *  01312db3   . 03f9           add edi,ecx
 *  01312db5   . eb 2f          jmp short 蒼の彼方.01312de6
 *  01312db7   > ba e0103b01    mov edx,蒼の彼方.013b10e0                    ;  case 4 of switch 01312d95
 *  01312dbc   . eb 1a          jmp short 蒼の彼方.01312dd8
 *  01312dbe   > ba e4103b01    mov edx,蒼の彼方.013b10e4                    ;  case 5 of switch 01312d95
 *  01312dc3   . eb 13          jmp short 蒼の彼方.01312dd8
 *  01312dc5   > ba e8103b01    mov edx,蒼の彼方.013b10e8                    ;  case 6 of switch 01312d95
 *  01312dca   . eb 0c          jmp short 蒼の彼方.01312dd8
 *  01312dcc   > ba ec103b01    mov edx,蒼の彼方.013b10ec                    ;  case 7 of switch 01312d95
 *  01312dd1   . eb 05          jmp short 蒼の彼方.01312dd8
 *  01312dd3   > ba f0103b01    mov edx,蒼の彼方.013b10f0                    ;  case 8 of switch 01312d95
 *  01312dd8   > 8d7424 14      lea esi,dword ptr ss:[esp+0x14]
 *  01312ddc   . 894c24 1c      mov dword ptr ss:[esp+0x1c],ecx
 *  01312de0   . e8 1b8dffff    call 蒼の彼方.0130bb00
 *  01312de5   . 47             inc edi
 *  01312de6   > 897c24 30      mov dword ptr ss:[esp+0x30],edi
 *  01312dea   > 8d8424 0802000>lea eax,dword ptr ss:[esp+0x208]         ;  default case of switch 01312d95
 *  01312df1   . e8 ba1b0000    call 蒼の彼方.013149b0
 *  01312df6   . 837d 10 00     cmp dword ptr ss:[ebp+0x10],0x0
 *  01312dfa   . 8bb424 2802000>mov esi,dword ptr ss:[esp+0x228]
 *  01312e01   . 894424 5c      mov dword ptr ss:[esp+0x5c],eax
 *  01312e05   . 74 12          je short 蒼の彼方.01312e19
 *  01312e07   . 56             push esi                                 ; /arg1
 *  01312e08   . e8 c31b0000    call 蒼の彼方.013149d0                       ; \蒼の彼方.013149d0
 *  01312e0d   . 83c4 04        add esp,0x4
 *  01312e10   . 898424 c000000>mov dword ptr ss:[esp+0xc0],eax
 *  01312e17   . eb 0b          jmp short 蒼の彼方.01312e24
 *  01312e19   > c78424 c000000>mov dword ptr ss:[esp+0xc0],0x0
 *  01312e24   > 8b4b 04        mov ecx,dword ptr ds:[ebx+0x4]
 *  01312e27   . 0fafce         imul ecx,esi
 *  01312e2a   . b8 1f85eb51    mov eax,0x51eb851f
 *  01312e2f   . f7e9           imul ecx
 *  01312e31   . c1fa 05        sar edx,0x5
 *  01312e34   . 8bca           mov ecx,edx
 *  01312e36   . c1e9 1f        shr ecx,0x1f
 *  01312e39   . 03ca           add ecx,edx
 *  01312e3b   . 894c24 70      mov dword ptr ss:[esp+0x70],ecx
 *  01312e3f   . 85c9           test ecx,ecx
 *  01312e41   . 7f 09          jg short 蒼の彼方.01312e4c
 *  01312e43   . b9 01000000    mov ecx,0x1
 *  01312e48   . 894c24 70      mov dword ptr ss:[esp+0x70],ecx
 *  01312e4c   > 8b53 08        mov edx,dword ptr ds:[ebx+0x8]
 *  01312e4f   . 0fafd6         imul edx,esi
 *  01312e52   . b8 1f85eb51    mov eax,0x51eb851f
 *  01312e57   . f7ea           imul edx
 *  01312e59   . c1fa 05        sar edx,0x5
 *  01312e5c   . 8bc2           mov eax,edx
 *  01312e5e   . c1e8 1f        shr eax,0x1f
 *  01312e61   . 03c2           add eax,edx
 *  01312e63   . 894424 78      mov dword ptr ss:[esp+0x78],eax
 *  01312e67   . 85c0           test eax,eax
 *  01312e69   . 7f 09          jg short 蒼の彼方.01312e74
 *  01312e6b   . b8 01000000    mov eax,0x1
 *  01312e70   . 894424 78      mov dword ptr ss:[esp+0x78],eax
 *  01312e74   > 33d2           xor edx,edx
 *  01312e76   . 895424 64      mov dword ptr ss:[esp+0x64],edx
 *  01312e7a   . 895424 6c      mov dword ptr ss:[esp+0x6c],edx
 *  01312e7e   . 8b13           mov edx,dword ptr ds:[ebx]
 *  01312e80   . 4a             dec edx                                  ;  switch (cases 1..2)
 *  01312e81   . 74 0e          je short 蒼の彼方.01312e91
 *  01312e83   . 4a             dec edx
 *  01312e84   . 75 13          jnz short 蒼の彼方.01312e99
 *  01312e86   . 8d1409         lea edx,dword ptr ds:[ecx+ecx]           ;  case 2 of switch 01312e80
 *  01312e89   . 895424 64      mov dword ptr ss:[esp+0x64],edx
 *  01312e8d   . 03c0           add eax,eax
 *  01312e8f   . eb 04          jmp short 蒼の彼方.01312e95
 *  01312e91   > 894c24 64      mov dword ptr ss:[esp+0x64],ecx          ;  case 1 of switch 01312e80
 *  01312e95   > 894424 6c      mov dword ptr ss:[esp+0x6c],eax
 *  01312e99   > 8b9c24 3802000>mov ebx,dword ptr ss:[esp+0x238]         ;  default case of switch 01312e80
 *  01312ea0   . 8bc3           mov eax,ebx
 *  01312ea2   . e8 d98bffff    call 蒼の彼方.0130ba80
 *  01312ea7   . 8bc8           mov ecx,eax
 *  01312ea9   . 8bc3           mov eax,ebx
 *  01312eab   . e8 e08bffff    call 蒼の彼方.0130ba90
 *  01312eb0   . 6a 01          push 0x1                                 ; /arg1 = 00000001
 *  01312eb2   . 8bd0           mov edx,eax                              ; |
 *  01312eb4   . 8db424 1c01000>lea esi,dword ptr ss:[esp+0x11c]         ; |
 *  01312ebb   . e8 3056fdff    call 蒼の彼方.012e84f0                       ; \蒼の彼方.012e84f0
 *  01312ec0   . 8bc7           mov eax,edi
 *  01312ec2   . 83c4 04        add esp,0x4
 *  01312ec5   . 8d70 01        lea esi,dword ptr ds:[eax+0x1]
 *  01312ec8   > 8a08           mov cl,byte ptr ds:[eax]
 *  01312eca   . 40             inc eax
 *  01312ecb   . 84c9           test cl,cl
 *  01312ecd   .^75 f9          jnz short 蒼の彼方.01312ec8
 *  01312ecf   . 2bc6           sub eax,esi
 *  01312ed1   . 40             inc eax
 *  01312ed2   . 50             push eax
 *  01312ed3   . e8 e74c0600    call 蒼の彼方.01377bbf
 *  01312ed8   . 33f6           xor esi,esi
 *  01312eda   . 83c4 04        add esp,0x4
 *
 *  1/1/2016
 *  コドモノアソビ trial
 *
 *  00A64259   CC               INT3
 *  00A6425A   CC               INT3
 *  00A6425B   CC               INT3
 *  00A6425C   CC               INT3
 *  00A6425D   CC               INT3
 *  00A6425E   CC               INT3
 *  00A6425F   CC               INT3
 *  00A64260   55               PUSH EBP
 *  00A64261   8BEC             MOV EBP,ESP
 *  00A64263   83E4 F8          AND ESP,0xFFFFFFF8
 *  00A64266   6A FF            PUSH -0x1
 *  00A64268   68 D610B000      PUSH .00B010D6
 *  00A6426D   64:A1 00000000   MOV EAX,DWORD PTR FS:[0]
 *  00A64273   50               PUSH EAX
 *  00A64274   81EC 40090000    SUB ESP,0x940
 *  00A6427A   A1 2417B200      MOV EAX,DWORD PTR DS:[0xB21724]
 *  00A6427F   33C4             XOR EAX,ESP
 *  00A64281   898424 38090000  MOV DWORD PTR SS:[ESP+0x938],EAX
 *  00A64288   53               PUSH EBX
 *  00A64289   56               PUSH ESI
 *  00A6428A   57               PUSH EDI
 *  00A6428B   A1 2417B200      MOV EAX,DWORD PTR DS:[0xB21724]
 *  00A64290   33C4             XOR EAX,ESP
 *  00A64292   50               PUSH EAX
 *  00A64293   8D8424 50090000  LEA EAX,DWORD PTR SS:[ESP+0x950]
 *  00A6429A   64:A3 00000000   MOV DWORD PTR FS:[0],EAX
 *  00A642A0   8B45 08          MOV EAX,DWORD PTR SS:[EBP+0x8]
 *  00A642A3   8B7D 0C          MOV EDI,DWORD PTR SS:[EBP+0xC]
 *  00A642A6   8B5D 30          MOV EBX,DWORD PTR SS:[EBP+0x30]
 *  00A642A9   894424 50        MOV DWORD PTR SS:[ESP+0x50],EAX
 *  00A642AD   8B45 14          MOV EAX,DWORD PTR SS:[EBP+0x14]
 *  00A642B0   894C24 74        MOV DWORD PTR SS:[ESP+0x74],ECX
 *  00A642B4   8B0D A024B800    MOV ECX,DWORD PTR DS:[0xB824A0]
 *  00A642BA   894424 4C        MOV DWORD PTR SS:[ESP+0x4C],EAX
 *  00A642BE   899424 B8000000  MOV DWORD PTR SS:[ESP+0xB8],EDX
 *  00A642C5   8B55 20          MOV EDX,DWORD PTR SS:[EBP+0x20]
 *  00A642C8   51               PUSH ECX
 *  00A642C9   8D8424 14020000  LEA EAX,DWORD PTR SS:[ESP+0x214]
 *  00A642D0   897C24 2C        MOV DWORD PTR SS:[ESP+0x2C],EDI
 *  00A642D4   899C24 88000000  MOV DWORD PTR SS:[ESP+0x88],EBX
 *  00A642DB   E8 504CFDFF      CALL .00A38F30
 *  00A642E0   33C9             XOR ECX,ECX
 *  00A642E2   898424 F8000000  MOV DWORD PTR SS:[ESP+0xF8],EAX
 *  00A642E9   3BC1             CMP EAX,ECX
 *  00A642EB   0F84 391C0000    JE .00A65F2A
 *  00A642F1   E8 FA2A0000      CALL .00A66DF0
 *  00A642F6   E8 252D0000      CALL .00A67020
 *  00A642FB   898424 FC000000  MOV DWORD PTR SS:[ESP+0xFC],EAX
 *  00A64302   8A07             MOV AL,BYTE PTR DS:[EDI]
 *  00A64304   898C24 CC000000  MOV DWORD PTR SS:[ESP+0xCC],ECX
 *  00A6430B   894C24 30        MOV DWORD PTR SS:[ESP+0x30],ECX
 *  00A6430F   894C24 1C        MOV DWORD PTR SS:[ESP+0x1C],ECX
 *  00A64313   B9 01000000      MOV ECX,0x1
 *  00A64318   3C 20            CMP AL,0x20  ; jichi: pattern found here
 *  00A6431A   7D 58            JGE SHORT .00A64374
 *  00A6431C   0FBEC0           MOVSX EAX,AL
 *  00A6431F   83C0 FE          ADD EAX,-0x2
 *  00A64322   83F8 06          CMP EAX,0x6
 *  00A64325   77 4D            JA SHORT .00A64374
 *  00A64327   FF2485 505FA600  JMP DWORD PTR DS:[EAX*4+0xA65F50]
 *  00A6432E   898C24 CC000000  MOV DWORD PTR SS:[ESP+0xCC],ECX
 *  00A64335   03F9             ADD EDI,ECX
 *  00A64337   EB 37            JMP SHORT .00A64370
 *  00A64339   894C24 30        MOV DWORD PTR SS:[ESP+0x30],ECX
 *  00A6433D   03F9             ADD EDI,ECX
 *  00A6433F   EB 2F            JMP SHORT .00A64370
 *  00A64341   BA E0C1B000      MOV EDX,.00B0C1E0
 *  00A64346   EB 1A            JMP SHORT .00A64362
 *  00A64348   BA E4C1B000      MOV EDX,.00B0C1E4
 *  00A6434D   EB 13            JMP SHORT .00A64362
 *  00A6434F   BA E8C1B000      MOV EDX,.00B0C1E8
 *  00A64354   EB 0C            JMP SHORT .00A64362
 *  00A64356   BA ECC1B000      MOV EDX,.00B0C1EC
 *  00A6435B   EB 05            JMP SHORT .00A64362
 *  00A6435D   BA F0C1B000      MOV EDX,.00B0C1F0
 *  00A64362   8D7424 14        LEA ESI,DWORD PTR SS:[ESP+0x14]
 *  00A64366   894C24 1C        MOV DWORD PTR SS:[ESP+0x1C],ECX
 *  00A6436A   E8 A196FFFF      CALL .00A5DA10
 *  00A6436F   47               INC EDI
 *  00A64370   897C24 28        MOV DWORD PTR SS:[ESP+0x28],EDI
 *  00A64374   8D8424 10020000  LEA EAX,DWORD PTR SS:[ESP+0x210]
 *  00A6437B   E8 C01C0000      CALL .00A66040
 *  00A64380   837D 10 00       CMP DWORD PTR SS:[EBP+0x10],0x0
 *  00A64384   8BB424 30020000  MOV ESI,DWORD PTR SS:[ESP+0x230]
 *  00A6438B   894424 60        MOV DWORD PTR SS:[ESP+0x60],EAX
 *  00A6438F   74 12            JE SHORT .00A643A3
 *  00A64391   56               PUSH ESI
 *  00A64392   E8 C91C0000      CALL .00A66060
 *  00A64397   83C4 04          ADD ESP,0x4
 *  00A6439A   898424 C4000000  MOV DWORD PTR SS:[ESP+0xC4],EAX
 *  00A643A1   EB 0B            JMP SHORT .00A643AE
 *  00A643A3   C78424 C4000000 >MOV DWORD PTR SS:[ESP+0xC4],0x0
 *  00A643AE   8B4B 04          MOV ECX,DWORD PTR DS:[EBX+0x4]
 *  00A643B1   0FAFCE           IMUL ECX,ESI
 *  00A643B4   B8 1F85EB51      MOV EAX,0x51EB851F
 *  00A643B9   F7E9             IMUL ECX
 *  00A643BB   C1FA 05          SAR EDX,0x5
 *  00A643BE   8BCA             MOV ECX,EDX
 *  00A643C0   C1E9 1F          SHR ECX,0x1F
 *  00A643C3   03CA             ADD ECX,EDX
 *  00A643C5   898C24 94000000  MOV DWORD PTR SS:[ESP+0x94],ECX
 *  00A643CC   85C9             TEST ECX,ECX
 *  00A643D0   B9 01000000      MOV ECX,0x1
 *  ...
 */
//static inline size_t _bgistrlen(LPCSTR text)
//{
//  size_t r = ::strlen(text);
//  if (r >=2 && *(WORD *)(text + r - 2) == 0xa581) // remove trailing ▼ = \x81\xa5
//    r -= 2;
//  return r;
//}
//
//static void SpecialHookBGI2(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
//{
//  LPCSTR text = (LPCSTR)*(DWORD *)(esp_base + hp->offset);
//  if (text) {
//    *data = (DWORD)text;
//    *len = _bgistrlen(text);
//  }
//}

bool InsertBGI2Hook()
{
  const BYTE bytes[] = {
    // The following code does not exist in newer BGI games after BGI 1.633.0.0 (tayutama2_trial_EX)
    //0x3c, 0x20,      // 011d4d31  |. 3c 20          cmp al,0x20
    //0x7d, XX,        // 011d4d33  |. 7d 75          jge short sekachu.011d4daa ; jichi: 0x75 or 0x58
    0x0f,0xbe,0xc0,  // 011d4d35  |. 0fbec0         movsx eax,al
    0x83,0xc0, 0xfe, // 011d4d38  |. 83c0 fe        add eax,-0x2               ;  switch (cases 2..8)
    0x83,0xf8//, 0x06  // 011d4d3b  |. 83f8 06        cmp eax,0x6
    // The following code does not exist in newer BGI games after 蒼の彼方
    //0x77, 0x6a     // 011d4d3e  |. 77 6a          ja short sekachu.011d4daa
  };

  ULONG range = min(module_limit_ - module_base_, MAX_REL_ADDR);
  ULONG addr = MemDbg::matchBytes(bytes, sizeof(bytes), module_base_, module_base_ + range);
  //GROWL_DWORD(reladdr);
  if (!addr) {
    ConsoleOutput("vnreng:BGI2: pattern not found");
    return false;
  }

  DWORD funaddr = MemDbg::findEnclosingAlignedFunction(addr, 0x100); // range is around 177 ~ 190

  enum : BYTE { push_ebp = 0x55 };  // 011d4c80  /$ 55             push ebp
  if (!funaddr || *(BYTE *)funaddr != push_ebp) {
    ConsoleOutput("vnreng:BGI2: pattern found but the function offset is invalid");
    return false;
  }

  HookParam hp = {};
  switch (funaddr - addr) {
  // for old BGI2 game, text is arg3
  case 0x34c80 - 0x34d31:
    hp.offset = 4 * 3;
    break;
  // for new BGI2 game since 蒼の彼方 (2014/08), text is in arg2
  case 0x01312cd0 - 0x01312D92:
  // For newer BGI2 game since コドモノアソビ (2015/11)
  case 0x00A64260 - 0x00A6431C:
  // For latest BGI2 game since タユタマ2(2016/05) by @mireado
  case 0x00E95290 - 0x00E95349:
  // For latest BGI2 game since 千の刃濤、桃花染の皇姫 体験版  by @mireado
  case 0x00AF5640 - 0x00AF56FF:
  // For latest BGI2 game since by BGI 1.633.0.0 @mireado
  case 0x00D8A660 - 0x00D8A73A:
    hp.offset = 4 * 2;
    break;
  default:
    ConsoleOutput("vnreng:BGI2: function found bug unrecognized hook offset");
    return false;
  }
  hp.address = funaddr;

  // jichi 5/12/2014: Using split could distinguish name and choices. But the signature might become unstable
  hp.type = USING_STRING|USING_SPLIT;
  hp.split = 4 * 8; // pseudo arg8
  //hp.split = -0x18;

  //GROWL_DWORD2(hp.address, module_base_);

  ConsoleOutput("vnreng: INSERT BGI2");
  NewHook(hp, "BGI2");

  // Disable TextOutA, which is cached and hence missing characters.
  ConsoleOutput("vnreng:BGI2: disable GDI hooks");
  DisableGDIHooks();
  return true;
}

#if 0
/**
 *  jichi 1/31/2014: Add a new BGI hook
 *  See: http://www.hongfire.com/forum/showthread.php/36807-AGTH-text-extraction-tool-for-games-translation/page702
 *  See: http://www.hongfire.com/forum/showthread.php/36807-AGTH-text-extraction-tool-for-games-translation/page716
 *
 *  Issue: This hook has floating split char
 *
 *  [ぷちけろ] コトバの消えた日 �忁�で裸にする純�調教~体験版
 *  /HS-1C:-4@68E56:BGI.exe
 *  - addr: 429654 (0x68e56)
 *  - module: 3927275266 (0xea157702)
 *  - off: 4294967264 = 0xffffffe0 = -0x20
 *  - split: 4294967288 = 0xfffffff8 = -0x8
 *  - type: 81 = 0x51
 *
 *  00e88e3d     cc             int3
 *  00e88e3e     cc             int3
 *  00e88e3f     cc             int3
 *  00e88e40  /. 55             push ebp
 *  00e88e41  |. 8bec           mov ebp,esp
 *  00e88e43  |. 56             push esi
 *  00e88e44  |. 57             push edi
 *  00e88e45  |. 8b7d 08        mov edi,dword ptr ss:[ebp+0x8]
 *  00e88e48  |. 57             push edi
 *  00e88e49  |. e8 c28a0100    call bgi.00ea1910
 *  00e88e4e  |. 57             push edi                                 ; |arg1
 *  00e88e4f  |. 8bf0           mov esi,eax                              ; |
 *  00e88e51  |. e8 ba8a0100    call bgi.00ea1910                        ; \bgi.00ea1910
 *  00e88e56  |. 83c4 08        add esp,0x8 ; jichi: hook here
 *  00e88e59  |. 2bc6           sub eax,esi
 *  00e88e5b  |. eb 03          jmp short bgi.00e88e60
 *  00e88e5d  |  8d49 00        lea ecx,dword ptr ds:[ecx]
 *  00e88e60  |> 8a0e           /mov cl,byte ptr ds:[esi]
 *  00e88e62  |. 880c30         |mov byte ptr ds:[eax+esi],cl
 *  00e88e65  |. 46             |inc esi
 *  00e88e66  |. 84c9           |test cl,cl
 *  00e88e68  |.^75 f6          \jnz short bgi.00e88e60
 *  00e88e6a  |. 5f             pop edi
 *  00e88e6b  |. 33c0           xor eax,eax
 *  00e88e6d  |. 5e             pop esi
 *  00e88e6e  |. 5d             pop ebp
 *  00e88e6f  \. c3             retn
 */
bool InsertBGI3Hook()
{
  const BYTE bytes[] = {
    0x83,0xc4, 0x08,// 00e88e56  |. 83c4 08        add esp,0x8 ; hook here
    0x2b,0xc6,      // 00e88e59  |. 2bc6           sub eax,esi
    0xeb, 0x03,     // 00e88e5b  |. eb 03          jmp short bgi.00e88e60
    0x8d,0x49, 0x00,// 00e88e5d  |  8d49 00        lea ecx,dword ptr ds:[ecx]
    0x8a,0x0e,      // 00e88e60  |> 8a0e           /mov cl,byte ptr ds:[esi]
    0x88,0x0c,0x30, // 00e88e62  |. 880c30         |mov byte ptr ds:[eax+esi],cl
    0x46,           // 00e88e65  |. 46             |inc esi
    0x84,0xc9,      // 00e88e66  |. 84c9           |test cl,cl
    0x75, 0xf6      // 00e88e68  |.^75 f6          \jnz short bgi.00e88e60
    //0x5f,           // 00e88e6a  |. 5f             pop edi
    //0x33,0xc0,      // 00e88e6b  |. 33c0           xor eax,eax
    //0x5e,           // 00e88e6d  |. 5e             pop esi
    //0x5d,           // 00e88e6e  |. 5d             pop ebp
    //0xc3            // 00e88e6f  \. c3             retn
  };
  //enum { addr_offset = 0 };
  ULONG range = min(module_limit_ - module_base_, MAX_REL_ADDR);
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_base_ + range);
  //reladdr = 0x68e56;
  if (!addr) {
    ConsoleOutput("vnreng:BGI3: pattern not found");
    return false;
  }

  HookParam hp = {};
  hp.type = USING_STRING|USING_SPLIT;
  hp.offset = -0x20;
  hp.split = -0x8;
  hp.address = addr;

  //GROWL_DWORD2(hp.address, module_base_);

  ConsoleOutput("vnreng: INSERT BGI3");
  NewHook(hp, "BGI3");
  return true;
}
#endif // 0
} // unnamed

// jichi 5/12/2014: BGI1 and BGI2 game can co-exist, such as 世界と世界の真ん中で
// BGI1 can exist in both old and new games
// BGI2 only exist in new games
// Insert BGI2 first.
bool InsertBGIHook()
{ return InsertBGI2Hook() ||  InsertBGI1Hook(); }

/********************************************************************************************
Reallive hook:
  Process name is reallive.exe or reallive*.exe.

  Technique to find Reallive hook is quite different from 2 above.
  Usually Reallive engine has a font caching issue. This time we wait
  until the first call to GetGlyphOutlineA. Reallive engine usually set
  up stack frames so we can just refer to EBP to find function entry.

********************************************************************************************/
/** jichi 5/13/2015
 *  RealLive does not work for 水着少女と媚薬アイス from 裸足少女
 *  012da80f   cc               int3
 *  012da810   55               push ebp    ; jichi: change to hook here
 *  012da811   8bec             mov ebp,esp
 *  012da813   83ec 10          sub esp,0x10 ; jichi: hook here by default
 *  012da816   53               push ebx
 *  012da817   56               push esi
 *  012da818   57               push edi
 *  012da819   8b7d 18          mov edi,dword ptr ss:[ebp+0x18]
 *  012da81c   81ff 5c810000    cmp edi,0x815c
 *  012da822   75 0a            jnz short reallive.012da82e
 *  012da824   c745 18 9f840000 mov dword ptr ss:[ebp+0x18],0x849f
 *  012da82b   8b7d 18          mov edi,dword ptr ss:[ebp+0x18]
 *  012da82e   b8 9041e301      mov eax,reallive.01e34190
 *  012da833   b9 18a49001      mov ecx,reallive.0190a418
 *  012da838   e8 a38d0000      call reallive.012e35e0
 *  012da83d   85c0             test eax,eax
 *  012da83f   74 14            je short reallive.012da855
 *  012da841   e8 6addffff      call reallive.012d85b0
 *  012da846   ba 9041e301      mov edx,reallive.01e34190
 *  012da84b   b8 18a49001      mov eax,reallive.0190a418
 *  012da850   e8 ab7c0000      call reallive.012e2500
 *  012da855   8d45 f0          lea eax,dword ptr ss:[ebp-0x10]
 *  012da858   50               push eax
 *  012da859   8d4d f4          lea ecx,dword ptr ss:[ebp-0xc]
 *  012da85c   51               push ecx
 *  012da85d   8d55 fc          lea edx,dword ptr ss:[ebp-0x4]
 *  012da860   52               push edx
 *  012da861   8d45 f8          lea eax,dword ptr ss:[ebp-0x8]
 *  012da864   50               push eax
 *  012da865   8bc7             mov eax,edi
 *  012da867   e8 54dfffff      call reallive.012d87c0
 *  012da86c   8bf0             mov esi,eax
 *  012da86e   83c4 10          add esp,0x10
 *  012da871   85f6             test esi,esi
 *  012da873   75 4b            jnz short reallive.012da8c0
 *  012da875   8d4d f4          lea ecx,dword ptr ss:[ebp-0xc]
 *  012da878   51               push ecx
 *  012da879   57               push edi
 *  012da87a   8d4d f0          lea ecx,dword ptr ss:[ebp-0x10]
 *  012da87d   e8 cef0ffff      call reallive.012d9950
 *  012da882   8bf0             mov esi,eax
 *  012da884   83c4 08          add esp,0x8
 *  012da887   85f6             test esi,esi
 */
static bool InsertRealliveDynamicHook(LPVOID addr, DWORD frame, DWORD stack)
{
  if (addr != ::GetGlyphOutlineA)
    return false;
  // jichi 5/13/2015: Find the enclosing caller of GetGlyphOutlineA
  if (DWORD i = frame) {
    i = *(DWORD *)(i + 4);
    for (DWORD j = i; j > i - 0x100; j--)
      if (*(WORD *)j == 0xec83) { // jichi 7/26/2014: function starts
        // 012da80f   cc               int3
        // 012da810   55               push ebp    ; jichi: change to hook here
        // 012da811   8bec             mov ebp,esp
        // 012da813   83ec 10          sub esp,0x10 ; jichi: hook here by default
        if (*(DWORD *)(j-3) == 0x83ec8b55)
          j -= 3;

        HookParam hp = {};
        hp.address = j;
        hp.offset = 0x14;
        hp.split = pusha_esp_off - 4; // -0x18
        hp.length_offset = 1;
        hp.type = BIG_ENDIAN|USING_SPLIT;
        //GROWL_DWORD(hp.address);
        NewHook(hp, "RealLive");
        //RegisterEngineType(ENGINE_REALLIVE);
        ConsoleOutput("vnreng:RealLive: disable GDI hooks");
        DisableGDIHooks();
        return true;
      }
  }
  return true; // jichi 12/25/2013: return true
}
void InsertRealliveHook()
{
  //ConsoleOutput("Probably Reallive. Wait for text.");
  ConsoleOutput("vnreng: TRIGGER Reallive");
  trigger_fun_ = InsertRealliveDynamicHook;
  SwitchTrigger(true);
}

namespace { // unnamed

/**
 *  jichi 8/17/2013:  SiglusEngine from siglusengine.exe
 *  The old hook does not work for new games.
 *  The new hook cannot recognize character names.
 *  Insert old first. As the pattern could also be found in the old engine.
 */

/** jichi 10/25/2014: new SiglusEngine3 that can extract character name
 *
 *  Sample game: リア兂�ラスメイト孕ませ催� -- /HW-4@F67DC:SiglusEngine.exe
 *  The character is in [edx+ecx*2]. Text in edx, and offset in ecx.
 *
 *  002667be   cc               int3
 *  002667bf   cc               int3
 *  002667c0   55               push ebp ; jichi: hook here
 *  002667c1   8bec             mov ebp,esp
 *  002667c3   8bd1             mov edx,ecx
 *  002667c5   8b4d 0c          mov ecx,dword ptr ss:[ebp+0xc]
 *  002667c8   83f9 01          cmp ecx,0x1
 *  002667cb   75 17            jnz short .002667e4
 *  002667cd   837a 14 08       cmp dword ptr ds:[edx+0x14],0x8
 *  002667d1   72 02            jb short .002667d5
 *  002667d3   8b12             mov edx,dword ptr ds:[edx]
 *  002667d5   8b4d 08          mov ecx,dword ptr ss:[ebp+0x8]
 *  002667d8   66:8b45 10       mov ax,word ptr ss:[ebp+0x10]
 *  002667dc   66:89044a        mov word ptr ds:[edx+ecx*2],ax  ; jichi: wchar_t is in ax
 *  002667e0   5d               pop ebp
 *  002667e1   c2 0c00          retn 0xc
 *  002667e4   837a 14 08       cmp dword ptr ds:[edx+0x14],0x8
 *  002667e8   72 02            jb short .002667ec
 *  002667ea   8b12             mov edx,dword ptr ds:[edx]
 *  002667ec   8b45 08          mov eax,dword ptr ss:[ebp+0x8]
 *  002667ef   57               push edi
 *  002667f0   8d3c42           lea edi,dword ptr ds:[edx+eax*2]
 *  002667f3   85c9             test ecx,ecx
 *  002667f5   74 16            je short .0026680d
 *  002667f7   8b45 10          mov eax,dword ptr ss:[ebp+0x10]
 *  002667fa   0fb7d0           movzx edx,ax
 *  002667fd   8bc2             mov eax,edx
 *  002667ff   c1e2 10          shl edx,0x10
 *  00266802   0bc2             or eax,edx
 *  00266804   d1e9             shr ecx,1
 *  00266806   f3:ab            rep stos dword ptr es:[edi]
 *  00266808   13c9             adc ecx,ecx
 *  0026680a   66:f3:ab         rep stos word ptr es:[edi]
 *  0026680d   5f               pop edi
 *  0026680e   5d               pop ebp
 *  0026680f   c2 0c00          retn 0xc
 *  00266812   cc               int3
 *  00266813   cc               int3
 *
 *  Stack when enter function call:
 *  04cee270   00266870  return to .00266870 from .002667c0
 *  04cee274   00000002  jichi: arg1, ecx
 *  04cee278   00000001  jichi: arg2, always 1
 *  04cee27c   000050ac  jichi: arg3, wchar_t
 *  04cee280   04cee4fc  jichi: text address
 *  04cee284   0ead055c  arg5
 *  04cee288   0ead0568  arg6, last text when arg6 = arg5 = 2
 *  04cee28c  /04cee2c0
 *  04cee290  |00266969  return to .00266969 from .00266820
 *  04cee294  |00000001
 *  04cee298  |000050ac
 *  04cee29c  |e1466fb2
 *  04cee2a0  |072f45f0
 *
 *  Target address (edx) is at [[ecx]] when enter function.
 */

// jichi: 8/17/2013: Change return type to bool
bool InsertSiglus3Hook()
{
  const BYTE bytes[] = {
    0x8b,0x12,             // 002667d3   8b12             mov edx,dword ptr ds:[edx]
    0x8b,0x4d, 0x08,       // 002667d5   8b4d 08          mov ecx,dword ptr ss:[ebp+0x8]
    0x66,0x8b,0x45, 0x10,  // 002667d8   66:8b45 10       mov ax,word ptr ss:[ebp+0x10]
    0x66,0x89,0x04,0x4a    // 002667dc   66:89044a        mov word ptr ds:[edx+ecx*2],ax ; jichi: wchar_t in ax
                           // 002667e0   5d               pop ebp
                           // 002667e1   c2 0c00          retn 0xc
  };
  enum { addr_offset = sizeof(bytes) - 4 };
  ULONG range = max(module_limit_ - module_base_, MAX_REL_ADDR);
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_base_ + range);
  if (!addr) {
    //ConsoleOutput("Unknown SiglusEngine");
    ConsoleOutput("vnreng:Siglus3: pattern not found");
    return false;
  }

  //addr = MemDbg::findEnclosingAlignedFunction(addr, 50); // 0x002667dc - 0x002667c0 = 28
  //if (!addr) {
  //  ConsoleOutput("vnreng:Siglus3: enclosing function not found");
  //  return false;
  //}

  HookParam hp = {};
  hp.address = addr + addr_offset;
  hp.offset = pusha_eax_off - 4;
  hp.type = USING_UNICODE;
  hp.length_offset = 1;
  //hp.text_fun = SpecialHookSiglus3;

  ConsoleOutput("vnreng: INSERT Siglus3");
  NewHook(hp, "SiglusEngine3");

  ConsoleOutput("vnreng:Siglus3: disable GDI hooks");
  DisableGDIHooks();
  return true;
}

/** SiglusEngine4 5/23/2015
 *  Sample game: AngleBeats trial
 *  Alternative ATcode from EGDB:
 *  UNIKOFILTER(30),FORCEFONT(5),HOOK(SiglusEngine.exe!0x0018CF39,TRANS(EAX,UNICODE,SMSTR,ADDNULL),RETNPOS(SOURCE))
 *  Text address is [eax]
 *
 *  0042CEFD   CC               INT3
 *  0042CEFE   CC               INT3
 *  0042CEFF   CC               INT3
 *  0042CF00   55               PUSH EBP
 *  0042CF01   8BEC             MOV EBP,ESP
 *  0042CF03   51               PUSH ECX
 *  0042CF04   A1 005E8A00      MOV EAX,DWORD PTR DS:[0x8A5E00]
 *  0042CF09   53               PUSH EBX
 *  0042CF0A   56               PUSH ESI
 *  0042CF0B   57               PUSH EDI
 *  0042CF0C   8B40 10          MOV EAX,DWORD PTR DS:[EAX+0x10]
 *  0042CF0F   8BF9             MOV EDI,ECX
 *  0042CF11   33C9             XOR ECX,ECX
 *  0042CF13   C745 FC 00000000 MOV DWORD PTR SS:[EBP-0x4],0x0
 *  0042CF1A   6A FF            PUSH -0x1
 *  0042CF1C   51               PUSH ECX
 *  0042CF1D   83E8 18          SUB EAX,0x18
 *  0042CF20   C747 14 07000000 MOV DWORD PTR DS:[EDI+0x14],0x7
 *  0042CF27   C747 10 00000000 MOV DWORD PTR DS:[EDI+0x10],0x0
 *  0042CF2E   66:890F          MOV WORD PTR DS:[EDI],CX
 *  0042CF31   8BCF             MOV ECX,EDI
 *  0042CF33   50               PUSH EAX
 *  0042CF34   E8 E725F6FF      CALL .0038F520
 *  0042CF39   8B1D 005E8A00    MOV EBX,DWORD PTR DS:[0x8A5E00] ; jichi: ATcode hooked here, text sometimes in eax sometimes address in eax, size in [eax+0x16]
 *  0042CF3F   8B73 10          MOV ESI,DWORD PTR DS:[EBX+0x10]
 *  0042CF42   837E FC 08       CMP DWORD PTR DS:[ESI-0x4],0x8
 *  0042CF46   72 0B            JB SHORT .0042CF53
 *  0042CF48   FF76 E8          PUSH DWORD PTR DS:[ESI-0x18]
 *  0042CF4B   E8 EA131300      CALL .0055E33A
 *  0042CF50   83C4 04          ADD ESP,0x4
 *  0042CF53   33C0             XOR EAX,EAX
 *  0042CF55   C746 FC 07000000 MOV DWORD PTR DS:[ESI-0x4],0x7
 *  0042CF5C   C746 F8 00000000 MOV DWORD PTR DS:[ESI-0x8],0x0
 *  0042CF63   66:8946 E8       MOV WORD PTR DS:[ESI-0x18],AX
 *  0042CF67   8BC7             MOV EAX,EDI
 *  0042CF69   8343 10 E8       ADD DWORD PTR DS:[EBX+0x10],-0x18
 *  0042CF6D   5F               POP EDI
 *  0042CF6E   5E               POP ESI
 *  0042CF6F   5B               POP EBX
 *  0042CF70   8BE5             MOV ESP,EBP
 *  0042CF72   5D               POP EBP
 *  0042CF73   C3               RETN
 *  0042CF74   CC               INT3
 *  0042CF75   CC               INT3
 *  0042CF76   CC               INT3
 *  0042CF77   CC               INT3
 */
bool Siglus4Filter(LPVOID data, DWORD *size, HookParam *, BYTE)
{
  auto text = reinterpret_cast<LPWSTR>(data);
  auto len = reinterpret_cast<size_t *>(size);
  // Remove "NNLI"
  //if (*len > 2 && ::all_ascii(text))
  //  return false;
  //if (*len == 2 && *text == L'N')
  //  return false;
  WideStringFilter(text, len, L"NLI", 3);
  // Replace 『�(300e, 300f) with 「�(300c,300d)
  //WideCharReplacer(text, len, 0x300e, 0x300c);
  //WideCharReplacer(text, len, 0x300f, 0x300d);
  return true;
}
void SpecialHookSiglus4(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  //static uint64_t lastTextHash_;
  DWORD eax = regof(eax, esp_base); // text
  if (!eax || !*(const BYTE *)eax) // empty data
    return;
  DWORD size = *(DWORD *)(eax + 0x10);
  if (!size)
    return;
  if (size < 8)
    *data = eax;
  else
    *data = *(DWORD *)eax;

  // Skip all ascii characters
  if (all_ascii((LPCWSTR)*data))
    return;

  // Avoid duplication
  //LPCWSTR text = (LPCWSTR)*data;
  //auto hash = hashstr(text);
  //if (hash == lastTextHash_)
  //  return;
  //lastTextHash_ = hash;

  *len = size * 2; // UTF-16
  DWORD s0 = retof(esp_base); // use stack[0] as split
  if (s0 <= 0xff) // scenario text
    *split = FIXED_SPLIT_VALUE;
  else if (::IsBadReadPtr((LPCVOID)s0, 4))
    *split = s0;
  else {
    *split = *(DWORD *)s0; // This value is runtime dependent
    if (*split == 0x54)
      *split = FIXED_SPLIT_VALUE * 2;
  }
  *split += argof(1, esp_base); // plus stack[1] as split
}
bool InsertSiglus4Hook()
{
  const BYTE bytes[] = {
    0xc7,0x47, 0x14, 0x07,0x00,0x00,0x00,   // 0042cf20   c747 14 07000000 mov dword ptr ds:[edi+0x14],0x7
    0xc7,0x47, 0x10, 0x00,0x00,0x00,0x00,   // 0042cf27   c747 10 00000000 mov dword ptr ds:[edi+0x10],0x0
    0x66,0x89,0x0f,                         // 0042cf2e   66:890f          mov word ptr ds:[edi],cx
    0x8b,0xcf,                              // 0042cf31   8bcf             mov ecx,edi
    0x50,                                   // 0042cf33   50               push eax
    0xe8 //XX4                              // 0042cf34   e8 e725f6ff      call .0038f520
    // hook here
  };
  enum { addr_offset = sizeof(bytes) + 4 }; // +4 for the call address
  ULONG range = max(module_limit_ - module_base_, MAX_REL_ADDR);
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_base_ + range);
  //ULONG addr = module_base_ + 0x0018cf39;
  if (!addr) {
    //ConsoleOutput("Unknown SiglusEngine");
    ConsoleOutput("vnreng:Siglus4: pattern not found");
    return false;
  }

  //addr = MemDbg::findEnclosingAlignedFunction(addr, 50); // 0x002667dc - 0x002667c0 = 28
  //if (!addr) {
  //  ConsoleOutput("vnreng:Siglus3: enclosing function not found");
  //  return false;
  //}

  HookParam hp = {};
  hp.address = addr + addr_offset;
  hp.type = NO_CONTEXT;
  hp.text_fun = SpecialHookSiglus4;
  hp.filter_fun = Siglus4Filter;
  //hp.offset = pusha_eax_off - 4;
  //hp.type = USING_UNICODE|DATA_INDIRECT|USING_SPLIT|NO_CONTEXT;
  //hp.type = USING_UNICODE|USING_SPLIT|NO_CONTEXT;
  //hp.split = pusha_edx_off - 4;

  ConsoleOutput("vnreng: INSERT Siglus4");
  NewHook(hp, "SiglusEngine4");

  ConsoleOutput("vnreng:Siglus4: disable GDI hooks");
  DisableGDIHooks();
  return true;
}

#if 0 // not all text can be extracted
/** jichi: 6/16/2015 Siglus4Engine for Frill games
 *  Sample game: 冺�少女
 *
 *  This function is found by tracking where the text length is modified
 *
 *  Base address: 0x070000
 *
 *  0020F51B   CC               INT3
 *  0020F51C   CC               INT3
 *  0020F51D   CC               INT3
 *  0020F51E   CC               INT3
 *  0020F51F   CC               INT3
 *  0020F520   55               PUSH EBP	; jichi: memory address in [arg1+0x4], text length in arg1
 *  0020F521   8BEC             MOV EBP,ESP
 *  0020F523   6A FF            PUSH -0x1
 *  0020F525   68 889B5900      PUSH .00599B88
 *  0020F52A   64:A1 00000000   MOV EAX,DWORD PTR FS:[0]
 *  0020F530   50               PUSH EAX
 *  0020F531   83EC 1C          SUB ESP,0x1C
 *  0020F534   53               PUSH EBX
 *  0020F535   56               PUSH ESI
 *  0020F536   57               PUSH EDI
 *  0020F537   A1 E0946500      MOV EAX,DWORD PTR DS:[0x6594E0]
 *  0020F53C   33C5             XOR EAX,EBP
 *  0020F53E   50               PUSH EAX
 *  0020F53F   8D45 F4          LEA EAX,DWORD PTR SS:[EBP-0xC]
 *  0020F542   64:A3 00000000   MOV DWORD PTR FS:[0],EAX
 *  0020F548   8BD1             MOV EDX,ECX
 *  0020F54A   8955 F0          MOV DWORD PTR SS:[EBP-0x10],EDX
 *  0020F54D   8B45 0C          MOV EAX,DWORD PTR SS:[EBP+0xC]
 *  0020F550   8B5D 10          MOV EBX,DWORD PTR SS:[EBP+0x10]
 *  0020F553   3BC3             CMP EAX,EBX
 *  0020F555   0F8D DF000000    JGE .0020F63A
 *  0020F55B   8B75 08          MOV ESI,DWORD PTR SS:[EBP+0x8]
 *  0020F55E   8D0C40           LEA ECX,DWORD PTR DS:[EAX+EAX*2]
 *  0020F561   C1E1 03          SHL ECX,0x3
 *  0020F564   2BD8             SUB EBX,EAX
 *  0020F566   894D 0C          MOV DWORD PTR SS:[EBP+0xC],ECX
 *  0020F569   8DA424 00000000  LEA ESP,DWORD PTR SS:[ESP]
 *  0020F570   8B82 A4000000    MOV EAX,DWORD PTR DS:[EDX+0xA4]
 *  0020F576   03C1             ADD EAX,ECX
 *  0020F578   C745 EC 07000000 MOV DWORD PTR SS:[EBP-0x14],0x7
 *  0020F57F   33C9             XOR ECX,ECX
 *  0020F581   C745 E8 00000000 MOV DWORD PTR SS:[EBP-0x18],0x0
 *  0020F588   6A FF            PUSH -0x1
 *  0020F58A   51               PUSH ECX
 *  0020F58B   66:894D D8       MOV WORD PTR SS:[EBP-0x28],CX
 *  0020F58F   8D4D D8          LEA ECX,DWORD PTR SS:[EBP-0x28]
 *  0020F592   50               PUSH EAX
 *  0020F593   E8 68EFF4FF      CALL .0015E500
 *  0020F598   C745 FC 00000000 MOV DWORD PTR SS:[EBP-0x4],0x0
 *  0020F59F   8BCE             MOV ECX,ESI
 *  0020F5A1   8B46 0C          MOV EAX,DWORD PTR DS:[ESI+0xC]
 *  0020F5A4   8B7D E8          MOV EDI,DWORD PTR SS:[EBP-0x18]
 *  0020F5A7   83C0 04          ADD EAX,0x4
 *  0020F5AA   50               PUSH EAX
 *  0020F5AB   E8 209DF5FF      CALL .001692D0
 *  0020F5B0   8B0E             MOV ECX,DWORD PTR DS:[ESI]
 *  0020F5B2   8D55 D8          LEA EDX,DWORD PTR SS:[EBP-0x28]
 *  0020F5B5   33C0             XOR EAX,EAX
 *  0020F5B7   3B4E 04          CMP ECX,DWORD PTR DS:[ESI+0x4]
 *  0020F5BA   0F44C8           CMOVE ECX,EAX
 *  0020F5BD   8B46 0C          MOV EAX,DWORD PTR DS:[ESI+0xC]
 *  0020F5C0   893C01           MOV DWORD PTR DS:[ECX+EAX],EDI	; jichi: text length modified here
 *  0020F5C3   8B45 E8          MOV EAX,DWORD PTR SS:[EBP-0x18]
 *  0020F5C6   8346 0C 04       ADD DWORD PTR DS:[ESI+0xC],0x4
 *  0020F5CA   8B4D D8          MOV ECX,DWORD PTR SS:[EBP-0x28]
 *  0020F5CD   8D3C00           LEA EDI,DWORD PTR DS:[EAX+EAX]
 *  0020F5D0   8B45 EC          MOV EAX,DWORD PTR SS:[EBP-0x14]
 *  0020F5D3   83F8 08          CMP EAX,0x8
 *  0020F5D6   0F43D1           CMOVNB EDX,ECX
 *  0020F5D9   8955 10          MOV DWORD PTR SS:[EBP+0x10],EDX
 *  0020F5DC   85FF             TEST EDI,EDI
 *  0020F5DE   7E 32            JLE SHORT .0020F612
 *  0020F5E0   8B46 0C          MOV EAX,DWORD PTR DS:[ESI+0xC]
 *  0020F5E3   8BCE             MOV ECX,ESI
 *  0020F5E5   03C7             ADD EAX,EDI
 *  0020F5E7   50               PUSH EAX
 *  0020F5E8   E8 E39CF5FF      CALL .001692D0
 *  0020F5ED   8B0E             MOV ECX,DWORD PTR DS:[ESI]
 *  0020F5EF   33C0             XOR EAX,EAX
 *  0020F5F1   3B4E 04          CMP ECX,DWORD PTR DS:[ESI+0x4]
 *  0020F5F4   57               PUSH EDI
 *  0020F5F5   FF75 10          PUSH DWORD PTR SS:[EBP+0x10]
 *  0020F5F8   0F44C8           CMOVE ECX,EAX
 *  0020F5FB   8B46 0C          MOV EAX,DWORD PTR DS:[ESI+0xC]
 *  0020F5FE   03C1             ADD EAX,ECX
 *  0020F600   50               PUSH EAX
 *  0020F601   E8 EA1B1200      CALL .003311F0
 *  0020F606   8B45 EC          MOV EAX,DWORD PTR SS:[EBP-0x14]
 *  0020F609   83C4 0C          ADD ESP,0xC
 *  0020F60C   017E 0C          ADD DWORD PTR DS:[ESI+0xC],EDI
 *  0020F60F   8B4D D8          MOV ECX,DWORD PTR SS:[EBP-0x28]
 *  0020F612   C745 FC FFFFFFFF MOV DWORD PTR SS:[EBP-0x4],-0x1
 *  0020F619   83F8 08          CMP EAX,0x8
 *  0020F61C   72 09            JB SHORT .0020F627
 *  0020F61E   51               PUSH ECX
 *  0020F61F   E8 A6DC1100      CALL .0032D2CA
 *  0020F624   83C4 04          ADD ESP,0x4
 *  0020F627   8B4D 0C          MOV ECX,DWORD PTR SS:[EBP+0xC]
 *  0020F62A   8B55 F0          MOV EDX,DWORD PTR SS:[EBP-0x10]
 *  0020F62D   83C1 18          ADD ECX,0x18
 *  0020F630   894D 0C          MOV DWORD PTR SS:[EBP+0xC],ECX
 *  0020F633   4B               DEC EBX
 *  0020F634  ^0F85 36FFFFFF    JNZ .0020F570
 *  0020F63A   8B4D F4          MOV ECX,DWORD PTR SS:[EBP-0xC]
 *  0020F63D   64:890D 00000000 MOV DWORD PTR FS:[0],ECX
 *  0020F644   59               POP ECX
 *  0020F645   5F               POP EDI
 *  0020F646   5E               POP ESI
 *  0020F647   5B               POP EBX
 *  0020F648   8BE5             MOV ESP,EBP
 *  0020F64A   5D               POP EBP
 *  0020F64B   C2 0C00          RETN 0xC
 *  0020F64E   CC               INT3
 *  0020F64F   CC               INT3
 */
void SpecialHookSiglus4(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  static uint64_t lastTextHash_;
  DWORD arg1 = argof(1, esp_base); // arg1
  DWORD addr = *(DWORD *)(arg1 + 4);
  int size = *(DWORD *)addr;
  if (size <= 0 || size > VNR_TEXT_CAPACITY)
    return;
  auto text = LPWSTR(addr + 4);
  if (!text || ::IsBadWritePtr(text, size * 2) || !*text || ::wcslen(text) != size || lastTextHash_ == hashstr(text)) //  || text[size+1], skip if text's size + 1 is not empty
    return;
  lastTextHash_ = hashstr(text); // skip last repetition
  *len = size * 2;
  *data = (DWORD)text;
  *split = argof(3, esp_base); // arg3
}
bool InsertSiglus4Hook()
{
  ULONG startAddress, stopAddress;
  if (!NtInspect::getProcessMemoryRange(&startAddress, &stopAddress)) { // need accurate stopAddress
    ConsoleOutput("vnreng:Siglus4: failed to get memory range");
    return false;
  }
  const BYTE bytes[] = {
    0x8b,0x75, 0x08, // 0020f55b   8b75 08          mov esi,dword ptr ss:[ebp+0x8]
    0x8d,0x0c,0x40,  // 0020f55e   8d0c40           lea ecx,dword ptr ds:[eax+eax*2]
    0xc1,0xe1, 0x03, // 0020f561   c1e1 03          shl ecx,0x3
    0x2b,0xd8,       // 0020f564   2bd8             sub ebx,eax
    0x89,0x4d, 0x0c  // 0020f566   894d 0c          mov dword ptr ss:[ebp+0xc],ecx

    // The following pattern is not unique, there are at least four matches
    //                        // 0020f5b7   3b4e 04     cmp ecx,dword ptr ds:[esi+0x4]
    //                        // 0020f5ba   0f44c8      cmove ecx,eax
    //0x8b,0x46, 0x0c,        // 0020f5bd   8b46 0c     mov eax,dword ptr ds:[esi+0xc]
    //0x89,0x3c,0x01,         // 0020f5c0   893c01      mov dword ptr ds:[ecx+eax],edi	; jichi: text length modified here
    //0x8b,0x45, 0xe8,        // 0020f5c3   8b45 e8     mov eax,dword ptr ss:[ebp-0x18]
    //0x83,0x46, 0x0c, 0x04,  // 0020f5c6   8346 0c 04  add dword ptr ds:[esi+0xc],0x4
    //0x8b,0x4d, 0xd8,        // 0020f5ca   8b4d d8     mov ecx,dword ptr ss:[ebp-0x28]
    //0x8d,0x3c,0x00          // 0020f5cd   8d3c00      lea edi,dword ptr ds:[eax+eax]
    //                        // 0020f5d0   8b45 ec     mov eax,dword ptr ss:[ebp-0x14]
  };
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress);
  if (!addr) {
    //ConsoleOutput("Unknown SiglusEngine");
    ConsoleOutput("vnreng:Siglus4: pattern not found");
    return false;
  }
  addr = MemDbg::findEnclosingAlignedFunction(addr, 0x100); // 0x0020f55b - 0x0020F520 = 59
  if (!addr) {
    ConsoleOutput("vnreng:Siglus4: enclosing function not found");
    return false;
  }

  //addr += 0x0020f64b - 0x0020f520; // hook to ret instead

  HookParam hp = {};
  hp.address = addr;
  //hp.type = USING_UNICODE;
  hp.type = NO_CONTEXT;
  hp.text_fun = SpecialHookSiglus4;
  hp.filter_fun = Siglus4Filter; // remove NLI from the game

  //GROWL_DWORD(addr);

  ConsoleOutput("vnreng: INSERT Siglus4");
  NewHook(hp, "SiglusEngine4");

  ConsoleOutput("vnreng:Siglus4: disable GDI hooks");
  DisableGDIHooks();
  return true;
}
#endif // 0


/**
 *  jichi 8/16/2013: Insert new siglus hook
 *  See (CaoNiMaGeBi): http://tieba.baidu.com/p/2531786952
 *  Issue: floating text
 *  Example:
 *  0153588b9534fdffff8b43583bd7
 *  0153 58          add dword ptr ds:[ebx+58],edx
 *  8b95 34fdffff    mov edx,dword ptr ss:[ebp-2cc]
 *  8b43 58          mov eax,dword ptr ds:[ebx+58]
 *  3bd7             cmp edx,edi    ; hook here
 *
 *  /HW-1C@D9DB2:SiglusEngine.exe
 *  - addr: 892338 (0xd9db2)
 *  - text_fun: 0x0
 *  - function: 0
 *  - hook_len: 0
 *  - ind: 0
 *  - length_offset: 1
 *  - module: 356004490 (0x1538328a)
 *  - off: 4294967264 (0xffffffe0L, 0x-20)
 *  - recover_len: 0
 *  - split: 0
 *  - split_ind: 0
 *  - type: 66   (0x42)
 *
 *  10/19/2014: There are currently two patterns to find the function to render scenario text.
 *  In the future, if both of them do not work again, try the following pattern instead.
 *  It is used to infer SiglusEngine2's logic in vnragent.
 *
 *  01140f8d   56               push esi
 *  01140f8e   8d8b 0c010000    lea ecx,dword ptr ds:[ebx+0x10c]
 *  01140f94   e8 67acfcff      call .0110bc00
 *  01140f99   837f 14 08       cmp dword ptr ds:[edi+0x14],0x8
 *  01140f9d   72 04            jb short .01140fa3
 *  01140f9f   8b37             mov esi,dword ptr ds:[edi]
 *  01140fa1   eb 02            jmp short .01140fa5
 *
 *  Type1 (聖娼女):
 *
 *  013aac6c   cc               int3
 *  013aac6d   cc               int3
 *  013aac6e   cc               int3
 *  013aac6f   cc               int3
 *  013aac70   55               push ebp    ; jichi: vnragent hooked here
 *  013aac71   8bec             mov ebp,esp
 *  013aac73   6a ff            push -0x1
 *  013aac75   68 d8306101      push .016130d8
 *  013aac7a   64:a1 00000000   mov eax,dword ptr fs:[0]
 *  013aac80   50               push eax
 *  013aac81   81ec dc020000    sub esp,0x2dc
 *  013aac87   a1 90f46a01      mov eax,dword ptr ds:[0x16af490]
 *  013aac8c   33c5             xor eax,ebp
 *  013aac8e   8945 f0          mov dword ptr ss:[ebp-0x10],eax
 *  013aac91   53               push ebx
 *  013aac92   56               push esi
 *  013aac93   57               push edi
 *  013aac94   50               push eax
 *  013aac95   8d45 f4          lea eax,dword ptr ss:[ebp-0xc]
 *  013aac98   64:a3 00000000   mov dword ptr fs:[0],eax
 *  013aac9e   8b45 0c          mov eax,dword ptr ss:[ebp+0xc]
 *  013aaca1   8b5d 08          mov ebx,dword ptr ss:[ebp+0x8]
 *  013aaca4   8bf9             mov edi,ecx
 *  013aaca6   8b77 10          mov esi,dword ptr ds:[edi+0x10]
 *  013aaca9   89bd 20fdffff    mov dword ptr ss:[ebp-0x2e0],edi
 *  013aacaf   8985 18fdffff    mov dword ptr ss:[ebp-0x2e8],eax
 *  013aacb5   85f6             test esi,esi
 *  013aacb7   0f84 77040000    je .013ab134
 *  013aacbd   8b93 18010000    mov edx,dword ptr ds:[ebx+0x118]
 *  013aacc3   2b93 14010000    sub edx,dword ptr ds:[ebx+0x114]
 *  013aacc9   8d8b 14010000    lea ecx,dword ptr ds:[ebx+0x114]
 *  013aaccf   b8 67666666      mov eax,0x66666667
 *  013aacd4   f7ea             imul edx
 *  013aacd6   c1fa 08          sar edx,0x8
 *  013aacd9   8bc2             mov eax,edx
 *  013aacdb   c1e8 1f          shr eax,0x1f
 *  013aacde   03c2             add eax,edx
 *  013aace0   03c6             add eax,esi
 *  013aace2   50               push eax
 *  013aace3   e8 5896fcff      call .01374340
 *  013aace8   837f 14 08       cmp dword ptr ds:[edi+0x14],0x8
 *  013aacec   72 04            jb short .013aacf2
 *  013aacee   8b07             mov eax,dword ptr ds:[edi]
 *  013aacf0   eb 02            jmp short .013aacf4
 *  013aacf2   8bc7             mov eax,edi
 *  013aacf4   8985 24fdffff    mov dword ptr ss:[ebp-0x2dc],eax
 *  013aacfa   8b57 14          mov edx,dword ptr ds:[edi+0x14]
 *  013aacfd   83fa 08          cmp edx,0x8
 *  013aad00   72 04            jb short .013aad06
 *  013aad02   8b0f             mov ecx,dword ptr ds:[edi]
 *  013aad04   eb 02            jmp short .013aad08
 *  013aad06   8bcf             mov ecx,edi
 *  013aad08   8b47 10          mov eax,dword ptr ds:[edi+0x10]
 *  013aad0b   8bb5 24fdffff    mov esi,dword ptr ss:[ebp-0x2dc]
 *  013aad11   03c0             add eax,eax
 *  013aad13   03c8             add ecx,eax
 *  013aad15   3bf1             cmp esi,ecx
 *  013aad17   0f84 17040000    je .013ab134
 *  013aad1d   c785 34fdffff 00>mov dword ptr ss:[ebp-0x2cc],0x0
 *  013aad27   c785 2cfdffff ff>mov dword ptr ss:[ebp-0x2d4],-0x1
 *  013aad31   89b5 1cfdffff    mov dword ptr ss:[ebp-0x2e4],esi
 *  013aad37   83fa 08          cmp edx,0x8
 *  013aad3a   72 04            jb short .013aad40
 *  013aad3c   8b0f             mov ecx,dword ptr ds:[edi]
 *  013aad3e   eb 02            jmp short .013aad42
 *  013aad40   8bcf             mov ecx,edi
 *  013aad42   03c1             add eax,ecx
 *  013aad44   8d8d 2cfdffff    lea ecx,dword ptr ss:[ebp-0x2d4]
 *  013aad4a   51               push ecx
 *  013aad4b   8d95 34fdffff    lea edx,dword ptr ss:[ebp-0x2cc]
 *  013aad51   52               push edx
 *  013aad52   50               push eax
 *  013aad53   8d85 24fdffff    lea eax,dword ptr ss:[ebp-0x2dc]
 *  013aad59   50               push eax
 *  013aad5a   e8 b183faff      call .01353110
 *  013aad5f   8bb5 2cfdffff    mov esi,dword ptr ss:[ebp-0x2d4]
 *  013aad65   83c4 10          add esp,0x10
 *  013aad68   83fe 0a          cmp esi,0xa
 *  013aad6b   75 09            jnz short .013aad76
 *  013aad6d   8bcb             mov ecx,ebx
 *  013aad6f   e8 ac050000      call .013ab320
 *  013aad74  ^eb 84            jmp short .013aacfa
 *  013aad76   83fe 07          cmp esi,0x7
 *  013aad79   75 2a            jnz short .013aada5
 *  013aad7b   33c9             xor ecx,ecx
 *  013aad7d   33c0             xor eax,eax
 *  013aad7f   66:898b ec000000 mov word ptr ds:[ebx+0xec],cx
 *  013aad86   8bcb             mov ecx,ebx
 *  013aad88   8983 e8000000    mov dword ptr ds:[ebx+0xe8],eax
 *  013aad8e   8983 f0000000    mov dword ptr ds:[ebx+0xf0],eax
 *  013aad94   e8 87050000      call .013ab320
 *  013aad99   c683 f9000000 01 mov byte ptr ds:[ebx+0xf9],0x1
 *  013aada0  ^e9 55ffffff      jmp .013aacfa
 *  013aada5   8b85 34fdffff    mov eax,dword ptr ss:[ebp-0x2cc]
 *  013aadab   85c0             test eax,eax
 *  013aadad   75 37            jnz short .013aade6
 *  013aadaf   85f6             test esi,esi
 *  013aadb1  ^0f84 43ffffff    je .013aacfa
 *  013aadb7   85c0             test eax,eax
 *  013aadb9   75 2b            jnz short .013aade6
 *  013aadbb   f605 c0be9f05 01 test byte ptr ds:[0x59fbec0],0x1
 *  013aadc2   75 0c            jnz short .013aadd0
 *  013aadc4   830d c0be9f05 01 or dword ptr ds:[0x59fbec0],0x1
 *  013aadcb   e8 f02a0b00      call .0145d8c0
 *  013aadd0   0fb7d6           movzx edx,si
 *  013aadd3   80ba c0be9e05 01 cmp byte ptr ds:[edx+0x59ebec0],0x1
 *  013aadda   75 0a            jnz short .013aade6
 *  013aaddc   8b43 68          mov eax,dword ptr ds:[ebx+0x68]
 *  013aaddf   99               cdq
 *  013aade0   2bc2             sub eax,edx
 *  013aade2   d1f8             sar eax,1
 *  013aade4   eb 03            jmp short .013aade9
 *  013aade6   8b43 68          mov eax,dword ptr ds:[ebx+0x68]
 *  013aade9   8b8b a0000000    mov ecx,dword ptr ds:[ebx+0xa0]
 *  013aadef   8b53 18          mov edx,dword ptr ds:[ebx+0x18]
 *  013aadf2   8985 30fdffff    mov dword ptr ss:[ebp-0x2d0],eax
 *  013aadf8   0343 58          add eax,dword ptr ds:[ebx+0x58]
 *  013aadfb   03d1             add edx,ecx
 *  013aadfd   3bc2             cmp eax,edx
 *  013aadff   7f 0f            jg short .013aae10
 *  013aae01   3bc1             cmp eax,ecx
 *  013aae03   7e 30            jle short .013aae35
 *  013aae05   8bc6             mov eax,esi
 *  013aae07   e8 94faffff      call .013aa8a0
 *  013aae0c   84c0             test al,al
 *  013aae0e   75 25            jnz short .013aae35
 *  013aae10   8bcb             mov ecx,ebx
 *  013aae12   e8 09050000      call .013ab320
 *  013aae17   83bd 34fdffff 00 cmp dword ptr ss:[ebp-0x2cc],0x0
 *  013aae1e   75 15            jnz short .013aae35
 *  013aae20   83fe 20          cmp esi,0x20
 *  013aae23  ^0f84 d1feffff    je .013aacfa
 *  013aae29   81fe 00300000    cmp esi,0x3000
 *  013aae2f  ^0f84 c5feffff    je .013aacfa
 *  013aae35   8b43 5c          mov eax,dword ptr ds:[ebx+0x5c]
 *  013aae38   3b83 a4000000    cmp eax,dword ptr ds:[ebx+0xa4]
 *  013aae3e   0f8d 7e020000    jge .013ab0c2
 *  013aae44   8d8d 38fdffff    lea ecx,dword ptr ss:[ebp-0x2c8]
 *  013aae4a   51               push ecx
 *  013aae4b   e8 30e4ffff      call .013a9280
 *  013aae50   c745 fc 01000000 mov dword ptr ss:[ebp-0x4],0x1
 *  013aae57   8b43 74          mov eax,dword ptr ds:[ebx+0x74]
 *  013aae5a   8b0d 88b26c01    mov ecx,dword ptr ds:[0x16cb288]
 *  013aae60   83f8 ff          cmp eax,-0x1
 *  013aae63   74 04            je short .013aae69
 *  013aae65   8bd0             mov edx,eax
 *  013aae67   eb 19            jmp short .013aae82
 *  013aae69   80b9 60010000 00 cmp byte ptr ds:[ecx+0x160],0x0
 *  013aae70   74 0d            je short .013aae7f
 *  013aae72   8b83 e0000000    mov eax,dword ptr ds:[ebx+0xe0]
 *  013aae78   8bd0             mov edx,eax
 *  013aae7a   83f8 ff          cmp eax,-0x1
 *  013aae7d   75 03            jnz short .013aae82
 *  013aae7f   8b53 24          mov edx,dword ptr ds:[ebx+0x24]
 *  013aae82   8b43 78          mov eax,dword ptr ds:[ebx+0x78]
 *  013aae85   83f8 ff          cmp eax,-0x1
 *  013aae88   75 17            jnz short .013aaea1
 *  013aae8a   80b9 60010000 00 cmp byte ptr ds:[ecx+0x160],0x0
 *  013aae91   74 0b            je short .013aae9e
 *  013aae93   8b83 e4000000    mov eax,dword ptr ds:[ebx+0xe4]
 *  013aae99   83f8 ff          cmp eax,-0x1
 *  013aae9c   75 03            jnz short .013aaea1
 *  013aae9e   8b43 28          mov eax,dword ptr ds:[ebx+0x28]
 *  013aaea1   8b4b 60          mov ecx,dword ptr ds:[ebx+0x60]
 *  013aaea4   8bb5 34fdffff    mov esi,dword ptr ss:[ebp-0x2cc]
 *  013aaeaa   034b 58          add ecx,dword ptr ds:[ebx+0x58]
 *  013aaead   8b7b 68          mov edi,dword ptr ds:[ebx+0x68]
 *  013aaeb0   8985 28fdffff    mov dword ptr ss:[ebp-0x2d8],eax
 *  013aaeb6   8b43 5c          mov eax,dword ptr ds:[ebx+0x5c]
 *  013aaeb9   0343 64          add eax,dword ptr ds:[ebx+0x64]
 *  013aaebc   83fe 01          cmp esi,0x1
 *  013aaebf   75 02            jnz short .013aaec3
 *  013aaec1   33d2             xor edx,edx
 *  013aaec3   80bb fa000000 00 cmp byte ptr ds:[ebx+0xfa],0x0
 *  013aaeca   89b5 38fdffff    mov dword ptr ss:[ebp-0x2c8],esi
 *  013aaed0   8bb5 2cfdffff    mov esi,dword ptr ss:[ebp-0x2d4]
 *  013aaed6   8995 44fdffff    mov dword ptr ss:[ebp-0x2bc],edx
 *  013aaedc   8b95 28fdffff    mov edx,dword ptr ss:[ebp-0x2d8]
 *  013aaee2   89b5 3cfdffff    mov dword ptr ss:[ebp-0x2c4],esi
 *  013aaee8   89bd 40fdffff    mov dword ptr ss:[ebp-0x2c0],edi
 *  013aaeee   8995 48fdffff    mov dword ptr ss:[ebp-0x2b8],edx
 *  013aaef4   898d 4cfdffff    mov dword ptr ss:[ebp-0x2b4],ecx
 *  013aaefa   8985 50fdffff    mov dword ptr ss:[ebp-0x2b0],eax
 *  013aaf00   74 19            je short .013aaf1b
 *  013aaf02   8b43 58          mov eax,dword ptr ds:[ebx+0x58]
 *  013aaf05   8b4b 5c          mov ecx,dword ptr ds:[ebx+0x5c]
 *  013aaf08   8983 fc000000    mov dword ptr ds:[ebx+0xfc],eax
 *  013aaf0e   898b 00010000    mov dword ptr ds:[ebx+0x100],ecx
 *  013aaf14   c683 fa000000 00 mov byte ptr ds:[ebx+0xfa],0x0
 *  013aaf1b   8b53 6c          mov edx,dword ptr ds:[ebx+0x6c]
 *  013aaf1e   0395 30fdffff    add edx,dword ptr ss:[ebp-0x2d0]
 *  013aaf24   33ff             xor edi,edi
 *  013aaf26   0153 58          add dword ptr ds:[ebx+0x58],edx
 *  013aaf29   8b95 34fdffff    mov edx,dword ptr ss:[ebp-0x2cc]
 *  013aaf2f   8b43 58          mov eax,dword ptr ds:[ebx+0x58]
 *  013aaf32   3bd7             cmp edx,edi             ; jichi: hook here
 *  013aaf34   75 4b            jnz short .013aaf81
 *  013aaf36   81fe 0c300000    cmp esi,0x300c  ; jichi 10/18/2014: searched here found the new siglus function
 *  013aaf3c   74 10            je short .013aaf4e
 *  013aaf3e   81fe 0e300000    cmp esi,0x300e
 *  013aaf44   74 08            je short .013aaf4e
 *  013aaf46   81fe 08ff0000    cmp esi,0xff08
 *  013aaf4c   75 33            jnz short .013aaf81
 *  013aaf4e   80bb f9000000 00 cmp byte ptr ds:[ebx+0xf9],0x0
 *  013aaf55   74 19            je short .013aaf70
 *  013aaf57   8983 e8000000    mov dword ptr ds:[ebx+0xe8],eax
 *  013aaf5d   66:89b3 ec000000 mov word ptr ds:[ebx+0xec],si
 *  013aaf64   c783 f0000000 01>mov dword ptr ds:[ebx+0xf0],0x1
 *  013aaf6e   eb 11            jmp short .013aaf81
 *  013aaf70   0fb783 ec000000  movzx eax,word ptr ds:[ebx+0xec]
 *  013aaf77   3bf0             cmp esi,eax
 *  013aaf79   75 06            jnz short .013aaf81
 *  013aaf7b   ff83 f0000000    inc dword ptr ds:[ebx+0xf0]
 *  013aaf81   8b8b f0000000    mov ecx,dword ptr ds:[ebx+0xf0]
 *  013aaf87   3bcf             cmp ecx,edi
 *  013aaf89   7e 71            jle short .013aaffc
 *  013aaf8b   3bd7             cmp edx,edi
 *  013aaf8d   75 50            jnz short .013aafdf
 *  013aaf8f   0fb783 ec000000  movzx eax,word ptr ds:[ebx+0xec]
 *  013aaf96   ba 0c300000      mov edx,0x300c
 *  013aaf9b   66:3bc2          cmp ax,dx
 *  013aaf9e   75 0f            jnz short .013aafaf
 *  013aafa0   81fe 0d300000    cmp esi,0x300d
 *  013aafa6   75 07            jnz short .013aafaf
 *  013aafa8   49               dec ecx
 *  013aafa9   898b f0000000    mov dword ptr ds:[ebx+0xf0],ecx
 *  013aafaf   b9 0e300000      mov ecx,0x300e
 *  013aafb4   66:3bc1          cmp ax,cx
 *  013aafb7   75 0e            jnz short .013aafc7
 *  013aafb9   81fe 0f300000    cmp esi,0x300f
 *  013aafbf   75 06            jnz short .013aafc7
 *  013aafc1   ff8b f0000000    dec dword ptr ds:[ebx+0xf0]
 *  013aafc7   ba 08ff0000      mov edx,0xff08
 *  013aafcc   66:3bc2          cmp ax,dx
 *  013aafcf   75 0e            jnz short .013aafdf
 *  013aafd1   81fe 09ff0000    cmp esi,0xff09
 *  013aafd7   75 06            jnz short .013aafdf
 *  013aafd9   ff8b f0000000    dec dword ptr ds:[ebx+0xf0]
 *  013aafdf   39bb f0000000    cmp dword ptr ds:[ebx+0xf0],edi
 *  013aafe5   75 15            jnz short .013aaffc
 *  013aafe7   33c0             xor eax,eax
 *  013aafe9   89bb e8000000    mov dword ptr ds:[ebx+0xe8],edi
 *  013aafef   66:8983 ec000000 mov word ptr ds:[ebx+0xec],ax
 *  013aaff6   89bb f0000000    mov dword ptr ds:[ebx+0xf0],edi
 *  013aaffc   8d8d 38fdffff    lea ecx,dword ptr ss:[ebp-0x2c8]
 *  013ab002   8dbb 14010000    lea edi,dword ptr ds:[ebx+0x114]
 *  013ab008   e8 b390fcff      call .013740c0
 *  013ab00d   33ff             xor edi,edi
 *  013ab00f   39bd 34fdffff    cmp dword ptr ss:[ebp-0x2cc],edi
 *  013ab015   75 0e            jnz short .013ab025
 *  013ab017   56               push esi
 *  013ab018   8d83 a8000000    lea eax,dword ptr ds:[ebx+0xa8]
 *  013ab01e   e8 5d080000      call .013ab880
 *  013ab023   eb 65            jmp short .013ab08a
 *  013ab025   8b85 1cfdffff    mov eax,dword ptr ss:[ebp-0x2e4]
 *  013ab02b   33c9             xor ecx,ecx
 *  013ab02d   66:894d d4       mov word ptr ss:[ebp-0x2c],cx
 *  013ab031   8b8d 24fdffff    mov ecx,dword ptr ss:[ebp-0x2dc]
 *  013ab037   c745 e8 07000000 mov dword ptr ss:[ebp-0x18],0x7
 *  013ab03e   897d e4          mov dword ptr ss:[ebp-0x1c],edi
 *  013ab041   3bc1             cmp eax,ecx
 *  013ab043   74 0d            je short .013ab052
 *  013ab045   2bc8             sub ecx,eax
 *  013ab047   d1f9             sar ecx,1
 *  013ab049   51               push ecx
 *  013ab04a   8d75 d4          lea esi,dword ptr ss:[ebp-0x2c]
 *  013ab04d   e8 de72f2ff      call .012d2330
 *  013ab052   6a ff            push -0x1
 *  013ab054   57               push edi
 *  013ab055   8d55 d4          lea edx,dword ptr ss:[ebp-0x2c]
 *  013ab058   52               push edx
 *  013ab059   8db3 a8000000    lea esi,dword ptr ds:[ebx+0xa8]
 *  013ab05f   c645 fc 02       mov byte ptr ss:[ebp-0x4],0x2
 *  013ab063   e8 3879f2ff      call .012d29a0
 *  013ab068   837d e8 08       cmp dword ptr ss:[ebp-0x18],0x8
 *  013ab06c   72 0c            jb short .013ab07a
 *  013ab06e   8b45 d4          mov eax,dword ptr ss:[ebp-0x2c]
 *  013ab071   50               push eax
 *  013ab072   e8 5fbe1900      call .01546ed6
 *  013ab077   83c4 04          add esp,0x4
 *  013ab07a   33c9             xor ecx,ecx
 *  013ab07c   c745 e8 07000000 mov dword ptr ss:[ebp-0x18],0x7
 *  013ab083   897d e4          mov dword ptr ss:[ebp-0x1c],edi
 *  013ab086   66:894d d4       mov word ptr ss:[ebp-0x2c],cx
 *  013ab08a   8bbd 20fdffff    mov edi,dword ptr ss:[ebp-0x2e0]
 *  013ab090   c683 f9000000 00 mov byte ptr ds:[ebx+0xf9],0x0
 *  013ab097   8d95 88feffff    lea edx,dword ptr ss:[ebp-0x178]
 *  013ab09d   52               push edx
 *  013ab09e   c745 fc 03000000 mov dword ptr ss:[ebp-0x4],0x3
 *  013ab0a5   e8 d6c70800      call .01437880
 *  013ab0aa   8d85 58fdffff    lea eax,dword ptr ss:[ebp-0x2a8]
 *  013ab0b0   50               push eax
 *  013ab0b1   c745 fc ffffffff mov dword ptr ss:[ebp-0x4],-0x1
 *  013ab0b8   e8 c3c70800      call .01437880
 *  013ab0bd  ^e9 38fcffff      jmp .013aacfa
 *  013ab0c2   8b9d 18fdffff    mov ebx,dword ptr ss:[ebp-0x2e8]
 *  013ab0c8   85db             test ebx,ebx
 *  013ab0ca   74 68            je short .013ab134
 *  013ab0cc   837f 14 08       cmp dword ptr ds:[edi+0x14],0x8
 *  013ab0d0   72 04            jb short .013ab0d6
 *  013ab0d2   8b07             mov eax,dword ptr ds:[edi]
 *  013ab0d4   eb 02            jmp short .013ab0d8
 *  013ab0d6   8bc7             mov eax,edi
 *  013ab0d8   8b4f 10          mov ecx,dword ptr ds:[edi+0x10]
 *  013ab0db   8d0448           lea eax,dword ptr ds:[eax+ecx*2]
 *  013ab0de   8b8d 1cfdffff    mov ecx,dword ptr ss:[ebp-0x2e4]
 *  013ab0e4   33d2             xor edx,edx
 *  013ab0e6   c745 cc 07000000 mov dword ptr ss:[ebp-0x34],0x7
 *  013ab0ed   c745 c8 00000000 mov dword ptr ss:[ebp-0x38],0x0
 *  013ab0f4   66:8955 b8       mov word ptr ss:[ebp-0x48],dx
 *  013ab0f8   3bc8             cmp ecx,eax
 *  013ab0fa   74 0f            je short .013ab10b
 *  013ab0fc   2bc1             sub eax,ecx
 *  013ab0fe   d1f8             sar eax,1
 *  013ab100   50               push eax
 *  013ab101   8bc1             mov eax,ecx
 *  013ab103   8d75 b8          lea esi,dword ptr ss:[ebp-0x48]
 *  013ab106   e8 2572f2ff      call .012d2330
 *  013ab10b   6a 00            push 0x0
 *  013ab10d   8d45 b8          lea eax,dword ptr ss:[ebp-0x48]
 *  013ab110   50               push eax
 *  013ab111   83c8 ff          or eax,0xffffffff
 *  013ab114   8bcb             mov ecx,ebx
 *  013ab116   c745 fc 00000000 mov dword ptr ss:[ebp-0x4],0x0
 *  013ab11d   e8 2e6ef2ff      call .012d1f50
 *  013ab122   837d cc 08       cmp dword ptr ss:[ebp-0x34],0x8
 *  013ab126   72 0c            jb short .013ab134
 *  013ab128   8b4d b8          mov ecx,dword ptr ss:[ebp-0x48]
 *  013ab12b   51               push ecx
 *  013ab12c   e8 a5bd1900      call .01546ed6
 *  013ab131   83c4 04          add esp,0x4
 *  013ab134   8b4d f4          mov ecx,dword ptr ss:[ebp-0xc]
 *  013ab137   64:890d 00000000 mov dword ptr fs:[0],ecx
 *  013ab13e   59               pop ecx
 *  013ab13f   5f               pop edi
 *  013ab140   5e               pop esi
 *  013ab141   5b               pop ebx
 *  013ab142   8b4d f0          mov ecx,dword ptr ss:[ebp-0x10]
 *  013ab145   33cd             xor ecx,ebp
 *  013ab147   e8 6ab30e00      call .014964b6
 *  013ab14c   8be5             mov esp,ebp
 *  013ab14e   5d               pop ebp
 *  013ab14f   c2 0800          retn 0x8
 *  013ab152   cc               int3
 *  013ab153   cc               int3
 *  013ab154   cc               int3
 *
 *  10/18/2014 Type2: リア兂�ラスメイト孕ませ催�
 *
 *  01140edb   cc               int3
 *  01140edc   cc               int3
 *  01140edd   cc               int3
 *  01140ede   cc               int3
 *  01140edf   cc               int3
 *  01140ee0   55               push ebp
 *  01140ee1   8bec             mov ebp,esp
 *  01140ee3   6a ff            push -0x1
 *  01140ee5   68 c6514a01      push .014a51c6
 *  01140eea   64:a1 00000000   mov eax,dword ptr fs:[0]
 *  01140ef0   50               push eax
 *  01140ef1   81ec dc020000    sub esp,0x2dc
 *  01140ef7   a1 10745501      mov eax,dword ptr ds:[0x1557410]
 *  01140efc   33c5             xor eax,ebp
 *  01140efe   8945 f0          mov dword ptr ss:[ebp-0x10],eax
 *  01140f01   53               push ebx
 *  01140f02   56               push esi
 *  01140f03   57               push edi
 *  01140f04   50               push eax
 *  01140f05   8d45 f4          lea eax,dword ptr ss:[ebp-0xc]
 *  01140f08   64:a3 00000000   mov dword ptr fs:[0],eax
 *  01140f0e   8bd9             mov ebx,ecx
 *  01140f10   8b7d 08          mov edi,dword ptr ss:[ebp+0x8]
 *  01140f13   837f 10 00       cmp dword ptr ds:[edi+0x10],0x0
 *  01140f17   8b45 0c          mov eax,dword ptr ss:[ebp+0xc]
 *  01140f1a   8985 1cfdffff    mov dword ptr ss:[ebp-0x2e4],eax
 *  01140f20   8d47 10          lea eax,dword ptr ds:[edi+0x10]
 *  01140f23   89bd 38fdffff    mov dword ptr ss:[ebp-0x2c8],edi
 *  01140f29   8985 20fdffff    mov dword ptr ss:[ebp-0x2e0],eax
 *  01140f2f   0f84 2a050000    je .0114145f
 *  01140f35   8b8b 10010000    mov ecx,dword ptr ds:[ebx+0x110]
 *  01140f3b   b8 67666666      mov eax,0x66666667
 *  01140f40   2b8b 0c010000    sub ecx,dword ptr ds:[ebx+0x10c]
 *  01140f46   f7e9             imul ecx
 *  01140f48   8b85 20fdffff    mov eax,dword ptr ss:[ebp-0x2e0]
 *  01140f4e   8b8b 14010000    mov ecx,dword ptr ds:[ebx+0x114]
 *  01140f54   2b8b 0c010000    sub ecx,dword ptr ds:[ebx+0x10c]
 *  01140f5a   c1fa 08          sar edx,0x8
 *  01140f5d   8bf2             mov esi,edx
 *  01140f5f   c1ee 1f          shr esi,0x1f
 *  01140f62   03f2             add esi,edx
 *  01140f64   0330             add esi,dword ptr ds:[eax]
 *  01140f66   b8 67666666      mov eax,0x66666667
 *  01140f6b   f7e9             imul ecx
 *  01140f6d   c1fa 08          sar edx,0x8
 *  01140f70   8bc2             mov eax,edx
 *  01140f72   c1e8 1f          shr eax,0x1f
 *  01140f75   03c2             add eax,edx
 *  01140f77   3bc6             cmp eax,esi
 *  01140f79   73 1e            jnb short .01140f99
 *  01140f7b   81fe 66666600    cmp esi,0x666666                         ; unicode "s the data.
 *  01140f81   76 0a            jbe short .01140f8d
 *  01140f83   68 c00f4f01      push .014f0fc0                           ; ascii "vector<t> too long"
 *  01140f88   e8 b1a30e00      call .0122b33e
 *  01140f8d   56               push esi
 *  01140f8e   8d8b 0c010000    lea ecx,dword ptr ds:[ebx+0x10c]
 *  01140f94   e8 67acfcff      call .0110bc00
 *  01140f99   837f 14 08       cmp dword ptr ds:[edi+0x14],0x8
 *  01140f9d   72 04            jb short .01140fa3
 *  01140f9f   8b37             mov esi,dword ptr ds:[edi]
 *  01140fa1   eb 02            jmp short .01140fa5
 *  01140fa3   8bf7             mov esi,edi
 *  01140fa5   89b5 34fdffff    mov dword ptr ss:[ebp-0x2cc],esi
 *  01140fab   eb 03            jmp short .01140fb0
 *  01140fad   8d49 00          lea ecx,dword ptr ds:[ecx]
 *  01140fb0   8b57 14          mov edx,dword ptr ds:[edi+0x14]
 *  01140fb3   83fa 08          cmp edx,0x8
 *  01140fb6   72 04            jb short .01140fbc
 *  01140fb8   8b07             mov eax,dword ptr ds:[edi]
 *  01140fba   eb 02            jmp short .01140fbe
 *  01140fbc   8bc7             mov eax,edi
 *  01140fbe   8b8d 20fdffff    mov ecx,dword ptr ss:[ebp-0x2e0]
 *  01140fc4   8b09             mov ecx,dword ptr ds:[ecx]
 *  01140fc6   03c9             add ecx,ecx
 *  01140fc8   03c1             add eax,ecx
 *  01140fca   3bf0             cmp esi,eax
 *  01140fcc   0f84 8d040000    je .0114145f
 *  01140fd2   8b85 38fdffff    mov eax,dword ptr ss:[ebp-0x2c8]
 *  01140fd8   8bfe             mov edi,esi
 *  01140fda   c785 3cfdffff 00>mov dword ptr ss:[ebp-0x2c4],0x0
 *  01140fe4   c785 2cfdffff ff>mov dword ptr ss:[ebp-0x2d4],-0x1
 *  01140fee   83fa 08          cmp edx,0x8
 *  01140ff1   72 02            jb short .01140ff5
 *  01140ff3   8b00             mov eax,dword ptr ds:[eax]
 *  01140ff5   03c1             add eax,ecx
 *  01140ff7   8d95 3cfdffff    lea edx,dword ptr ss:[ebp-0x2c4]
 *  01140ffd   8d8d 2cfdffff    lea ecx,dword ptr ss:[ebp-0x2d4]
 *  01141003   51               push ecx
 *  01141004   50               push eax
 *  01141005   8d8d 34fdffff    lea ecx,dword ptr ss:[ebp-0x2cc]
 *  0114100b   e8 e033fbff      call .010f43f0
 *  01141010   8bb5 2cfdffff    mov esi,dword ptr ss:[ebp-0x2d4]
 *  01141016   83c4 08          add esp,0x8
 *  01141019   83fe 0a          cmp esi,0xa
 *  0114101c   75 18            jnz short .01141036
 *  0114101e   8bcb             mov ecx,ebx
 *  01141020   e8 2b060000      call .01141650
 *  01141025   8bb5 34fdffff    mov esi,dword ptr ss:[ebp-0x2cc]
 *  0114102b   8bbd 38fdffff    mov edi,dword ptr ss:[ebp-0x2c8]
 *  01141031  ^e9 7affffff      jmp .01140fb0
 *  01141036   83fe 07          cmp esi,0x7
 *  01141039   75 38            jnz short .01141073
 *  0114103b   33c0             xor eax,eax
 *  0114103d   c783 e0000000 00>mov dword ptr ds:[ebx+0xe0],0x0
 *  01141047   8bcb             mov ecx,ebx
 *  01141049   66:8983 e4000000 mov word ptr ds:[ebx+0xe4],ax
 *  01141050   8983 e8000000    mov dword ptr ds:[ebx+0xe8],eax
 *  01141056   e8 f5050000      call .01141650
 *  0114105b   8bb5 34fdffff    mov esi,dword ptr ss:[ebp-0x2cc]
 *  01141061   8bbd 38fdffff    mov edi,dword ptr ss:[ebp-0x2c8]
 *  01141067   c683 f1000000 01 mov byte ptr ds:[ebx+0xf1],0x1
 *  0114106e  ^e9 3dffffff      jmp .01140fb0
 *  01141073   8b85 3cfdffff    mov eax,dword ptr ss:[ebp-0x2c4]
 *  01141079   85c0             test eax,eax
 *  0114107b   75 36            jnz short .011410b3
 *  0114107d   85f6             test esi,esi
 *  0114107f   74 7f            je short .01141100
 *  01141081   85c0             test eax,eax
 *  01141083   75 2e            jnz short .011410b3
 *  01141085   a1 00358905      mov eax,dword ptr ds:[0x5893500]
 *  0114108a   a8 01            test al,0x1
 *  0114108c   75 0d            jnz short .0114109b
 *  0114108e   83c8 01          or eax,0x1
 *  01141091   a3 00358905      mov dword ptr ds:[0x5893500],eax
 *  01141096   e8 65160b00      call .011f2700
 *  0114109b   0fb7c6           movzx eax,si
 *  0114109e   80b8 10358905 01 cmp byte ptr ds:[eax+0x5893510],0x1
 *  011410a5   75 0c            jnz short .011410b3
 *  011410a7   8b43 68          mov eax,dword ptr ds:[ebx+0x68]
 *  011410aa   99               cdq
 *  011410ab   2bc2             sub eax,edx
 *  011410ad   8bc8             mov ecx,eax
 *  011410af   d1f9             sar ecx,1
 *  011410b1   eb 03            jmp short .011410b6
 *  011410b3   8b4b 68          mov ecx,dword ptr ds:[ebx+0x68]
 *  011410b6   8b43 18          mov eax,dword ptr ds:[ebx+0x18]
 *  011410b9   8b93 a0000000    mov edx,dword ptr ds:[ebx+0xa0]
 *  011410bf   03c2             add eax,edx
 *  011410c1   898d 28fdffff    mov dword ptr ss:[ebp-0x2d8],ecx
 *  011410c7   034b 58          add ecx,dword ptr ds:[ebx+0x58]
 *  011410ca   3bc8             cmp ecx,eax
 *  011410cc   7f 0f            jg short .011410dd
 *  011410ce   3bca             cmp ecx,edx
 *  011410d0   7e 3f            jle short .01141111
 *  011410d2   8bce             mov ecx,esi
 *  011410d4   e8 37faffff      call .01140b10
 *  011410d9   84c0             test al,al
 *  011410db   75 34            jnz short .01141111
 *  011410dd   8bcb             mov ecx,ebx
 *  011410df   e8 6c050000      call .01141650
 *  011410e4   83bd 3cfdffff 00 cmp dword ptr ss:[ebp-0x2c4],0x0
 *  011410eb   75 24            jnz short .01141111
 *  011410ed   83fe 20          cmp esi,0x20
 *  011410f0   74 0e            je short .01141100
 *  011410f2   81fe 00300000    cmp esi,0x3000
 *  011410f8   75 17            jnz short .01141111
 *  011410fa   8d9b 00000000    lea ebx,dword ptr ds:[ebx]
 *  01141100   8bb5 34fdffff    mov esi,dword ptr ss:[ebp-0x2cc]
 *  01141106   8bbd 38fdffff    mov edi,dword ptr ss:[ebp-0x2c8]
 *  0114110c  ^e9 9ffeffff      jmp .01140fb0
 *  01141111   8b43 5c          mov eax,dword ptr ds:[ebx+0x5c]
 *  01141114   3b83 a4000000    cmp eax,dword ptr ds:[ebx+0xa4]
 *  0114111a   0f8d cb020000    jge .011413eb
 *  01141120   8d8d 40fdffff    lea ecx,dword ptr ss:[ebp-0x2c0]
 *  01141126   e8 d5e3ffff      call .0113f500
 *  0114112b   c745 fc 01000000 mov dword ptr ss:[ebp-0x4],0x1
 *  01141132   8b4b 74          mov ecx,dword ptr ds:[ebx+0x74]
 *  01141135   8b15 98285701    mov edx,dword ptr ds:[0x1572898]
 *  0114113b   898d 30fdffff    mov dword ptr ss:[ebp-0x2d0],ecx
 *  01141141   83f9 ff          cmp ecx,-0x1
 *  01141144   75 23            jnz short .01141169
 *  01141146   80ba 58010000 00 cmp byte ptr ds:[edx+0x158],0x0
 *  0114114d   74 11            je short .01141160
 *  0114114f   8b8b d8000000    mov ecx,dword ptr ds:[ebx+0xd8]
 *  01141155   898d 30fdffff    mov dword ptr ss:[ebp-0x2d0],ecx
 *  0114115b   83f9 ff          cmp ecx,-0x1
 *  0114115e   75 09            jnz short .01141169
 *  01141160   8b43 24          mov eax,dword ptr ds:[ebx+0x24]
 *  01141163   8985 30fdffff    mov dword ptr ss:[ebp-0x2d0],eax
 *  01141169   8b43 78          mov eax,dword ptr ds:[ebx+0x78]
 *  0114116c   8985 24fdffff    mov dword ptr ss:[ebp-0x2dc],eax
 *  01141172   83f8 ff          cmp eax,-0x1
 *  01141175   75 23            jnz short .0114119a
 *  01141177   80ba 58010000 00 cmp byte ptr ds:[edx+0x158],0x0
 *  0114117e   74 11            je short .01141191
 *  01141180   8b83 dc000000    mov eax,dword ptr ds:[ebx+0xdc]
 *  01141186   8985 24fdffff    mov dword ptr ss:[ebp-0x2dc],eax
 *  0114118c   83f8 ff          cmp eax,-0x1
 *  0114118f   75 09            jnz short .0114119a
 *  01141191   8b43 28          mov eax,dword ptr ds:[ebx+0x28]
 *  01141194   8985 24fdffff    mov dword ptr ss:[ebp-0x2dc],eax
 *  0114119a   8b53 64          mov edx,dword ptr ds:[ebx+0x64]
 *  0114119d   0353 5c          add edx,dword ptr ds:[ebx+0x5c]
 *  011411a0   8b4b 60          mov ecx,dword ptr ds:[ebx+0x60]
 *  011411a3   034b 58          add ecx,dword ptr ds:[ebx+0x58]
 *  011411a6   83bd 3cfdffff 01 cmp dword ptr ss:[ebp-0x2c4],0x1
 *  011411ad   8bb5 30fdffff    mov esi,dword ptr ss:[ebp-0x2d0]
 *  011411b3   8b43 68          mov eax,dword ptr ds:[ebx+0x68]
 *  011411b6   c785 18fdffff 00>mov dword ptr ss:[ebp-0x2e8],0x0
 *  011411c0   0f44b5 18fdffff  cmove esi,dword ptr ss:[ebp-0x2e8]
 *  011411c7   80bb f2000000 00 cmp byte ptr ds:[ebx+0xf2],0x0
 *  011411ce   89b5 30fdffff    mov dword ptr ss:[ebp-0x2d0],esi
 *  011411d4   8bb5 3cfdffff    mov esi,dword ptr ss:[ebp-0x2c4]
 *  011411da   8985 48fdffff    mov dword ptr ss:[ebp-0x2b8],eax
 *  011411e0   8b85 30fdffff    mov eax,dword ptr ss:[ebp-0x2d0]
 *  011411e6   89b5 40fdffff    mov dword ptr ss:[ebp-0x2c0],esi
 *  011411ec   8bb5 2cfdffff    mov esi,dword ptr ss:[ebp-0x2d4]
 *  011411f2   8985 4cfdffff    mov dword ptr ss:[ebp-0x2b4],eax
 *  011411f8   8b85 24fdffff    mov eax,dword ptr ss:[ebp-0x2dc]
 *  011411fe   89b5 44fdffff    mov dword ptr ss:[ebp-0x2bc],esi
 *  01141204   8985 50fdffff    mov dword ptr ss:[ebp-0x2b0],eax
 *  0114120a   898d 54fdffff    mov dword ptr ss:[ebp-0x2ac],ecx
 *  01141210   8995 58fdffff    mov dword ptr ss:[ebp-0x2a8],edx
 *  01141216   74 19            je short .01141231
 *  01141218   8b43 58          mov eax,dword ptr ds:[ebx+0x58]
 *  0114121b   8983 f4000000    mov dword ptr ds:[ebx+0xf4],eax
 *  01141221   8b43 5c          mov eax,dword ptr ds:[ebx+0x5c]
 *  01141224   8983 f8000000    mov dword ptr ds:[ebx+0xf8],eax
 *  0114122a   c683 f2000000 00 mov byte ptr ds:[ebx+0xf2],0x0
 *  01141231   8b43 6c          mov eax,dword ptr ds:[ebx+0x6c]
 *  01141234   0385 28fdffff    add eax,dword ptr ss:[ebp-0x2d8]
 *  0114123a   0143 58          add dword ptr ds:[ebx+0x58],eax
 *  0114123d   8b85 3cfdffff    mov eax,dword ptr ss:[ebp-0x2c4]
 *  01141243   8b4b 58          mov ecx,dword ptr ds:[ebx+0x58]
 *  01141246   85c0             test eax,eax
 *  01141248   75 51            jnz short .0114129b
 *  0114124a   81fe 0c300000    cmp esi,0x300c  ; jichi: hook here, utf16 character is in esi
 *  01141250   74 10            je short .01141262
 *  01141252   81fe 0e300000    cmp esi,0x300e
 *  01141258   74 08            je short .01141262
 *  0114125a   81fe 08ff0000    cmp esi,0xff08
 *  01141260   75 39            jnz short .0114129b
 *  01141262   80bb f1000000 00 cmp byte ptr ds:[ebx+0xf1],0x0
 *  01141269   74 19            je short .01141284
 *  0114126b   898b e0000000    mov dword ptr ds:[ebx+0xe0],ecx
 *  01141271   66:89b3 e4000000 mov word ptr ds:[ebx+0xe4],si
 *  01141278   c783 e8000000 01>mov dword ptr ds:[ebx+0xe8],0x1
 *  01141282   eb 17            jmp short .0114129b
 *  01141284   0fb783 e4000000  movzx eax,word ptr ds:[ebx+0xe4]
 *  0114128b   3bf0             cmp esi,eax
 *  0114128d   8b85 3cfdffff    mov eax,dword ptr ss:[ebp-0x2c4]
 *  01141293   75 06            jnz short .0114129b
 *  01141295   ff83 e8000000    inc dword ptr ds:[ebx+0xe8]
 *  0114129b   8b93 e8000000    mov edx,dword ptr ds:[ebx+0xe8]
 *  011412a1   85d2             test edx,edx
 *  011412a3   7e 78            jle short .0114131d
 *  011412a5   85c0             test eax,eax
 *  011412a7   75 52            jnz short .011412fb
 *  011412a9   0fb78b e4000000  movzx ecx,word ptr ds:[ebx+0xe4]
 *  011412b0   b8 0c300000      mov eax,0x300c
 *  011412b5   66:3bc8          cmp cx,ax
 *  011412b8   75 11            jnz short .011412cb
 *  011412ba   81fe 0d300000    cmp esi,0x300d
 *  011412c0   75 09            jnz short .011412cb
 *  011412c2   8d42 ff          lea eax,dword ptr ds:[edx-0x1]
 *  011412c5   8983 e8000000    mov dword ptr ds:[ebx+0xe8],eax
 *  011412cb   b8 0e300000      mov eax,0x300e
 *  011412d0   66:3bc8          cmp cx,ax
 *  011412d3   75 0e            jnz short .011412e3
 *  011412d5   81fe 0f300000    cmp esi,0x300f
 *  011412db   75 06            jnz short .011412e3
 *  011412dd   ff8b e8000000    dec dword ptr ds:[ebx+0xe8]
 *  011412e3   b8 08ff0000      mov eax,0xff08
 *  011412e8   66:3bc8          cmp cx,ax
 *  011412eb   75 0e            jnz short .011412fb
 *  011412ed   81fe 09ff0000    cmp esi,0xff09
 *  011412f3   75 06            jnz short .011412fb
 *  011412f5   ff8b e8000000    dec dword ptr ds:[ebx+0xe8]
 *  011412fb   83bb e8000000 00 cmp dword ptr ds:[ebx+0xe8],0x0
 *  01141302   75 19            jnz short .0114131d
 *  01141304   33c0             xor eax,eax
 *  01141306   c783 e0000000 00>mov dword ptr ds:[ebx+0xe0],0x0
 *  01141310   66:8983 e4000000 mov word ptr ds:[ebx+0xe4],ax
 *  01141317   8983 e8000000    mov dword ptr ds:[ebx+0xe8],eax
 *  0114131d   8d85 40fdffff    lea eax,dword ptr ss:[ebp-0x2c0]
 *  01141323   50               push eax
 *  01141324   8d8b 0c010000    lea ecx,dword ptr ds:[ebx+0x10c]
 *  0114132a   e8 31a6fcff      call .0110b960
 *  0114132f   83bd 3cfdffff 00 cmp dword ptr ss:[ebp-0x2c4],0x0
 *  01141336   8bb5 34fdffff    mov esi,dword ptr ss:[ebp-0x2cc]
 *  0114133c   75 13            jnz short .01141351
 *  0114133e   ffb5 2cfdffff    push dword ptr ss:[ebp-0x2d4]
 *  01141344   8d8b a8000000    lea ecx,dword ptr ds:[ebx+0xa8]
 *  0114134a   e8 010a0000      call .01141d50
 *  0114134f   eb 64            jmp short .011413b5
 *  01141351   33c0             xor eax,eax
 *  01141353   c745 ec 07000000 mov dword ptr ss:[ebp-0x14],0x7
 *  0114135a   c745 e8 00000000 mov dword ptr ss:[ebp-0x18],0x0
 *  01141361   66:8945 d8       mov word ptr ss:[ebp-0x28],ax
 *  01141365   3bfe             cmp edi,esi
 *  01141367   74 10            je short .01141379
 *  01141369   8bc6             mov eax,esi
 *  0114136b   8d4d d8          lea ecx,dword ptr ss:[ebp-0x28]
 *  0114136e   2bc7             sub eax,edi
 *  01141370   d1f8             sar eax,1
 *  01141372   50               push eax
 *  01141373   57               push edi
 *  01141374   e8 b7daf2ff      call .0106ee30
 *  01141379   6a ff            push -0x1
 *  0114137b   6a 00            push 0x0
 *  0114137d   8d45 d8          lea eax,dword ptr ss:[ebp-0x28]
 *  01141380   c645 fc 02       mov byte ptr ss:[ebp-0x4],0x2
 *  01141384   50               push eax
 *  01141385   8d8b a8000000    lea ecx,dword ptr ds:[ebx+0xa8]
 *  0114138b   e8 205cf3ff      call .01076fb0
 *  01141390   837d ec 08       cmp dword ptr ss:[ebp-0x14],0x8
 *  01141394   72 0b            jb short .011413a1
 *  01141396   ff75 d8          push dword ptr ss:[ebp-0x28]
 *  01141399   e8 fccb0e00      call .0122df9a
 *  0114139e   83c4 04          add esp,0x4
 *  011413a1   33c0             xor eax,eax
 *  011413a3   c745 ec 07000000 mov dword ptr ss:[ebp-0x14],0x7
 *  011413aa   c745 e8 00000000 mov dword ptr ss:[ebp-0x18],0x0
 *  011413b1   66:8945 d8       mov word ptr ss:[ebp-0x28],ax
 *  011413b5   c683 f1000000 00 mov byte ptr ds:[ebx+0xf1],0x0
 *  011413bc   8d8d 90feffff    lea ecx,dword ptr ss:[ebp-0x170]
 *  011413c2   c745 fc 03000000 mov dword ptr ss:[ebp-0x4],0x3
 *  011413c9   e8 42bb0800      call .011ccf10
 *  011413ce   8d8d 60fdffff    lea ecx,dword ptr ss:[ebp-0x2a0]
 *  011413d4   c745 fc ffffffff mov dword ptr ss:[ebp-0x4],-0x1
 *  011413db   e8 30bb0800      call .011ccf10
 *  011413e0   8bbd 38fdffff    mov edi,dword ptr ss:[ebp-0x2c8]
 *  011413e6  ^e9 c5fbffff      jmp .01140fb0
 *  011413eb   8b9d 1cfdffff    mov ebx,dword ptr ss:[ebp-0x2e4]
 *  011413f1   85db             test ebx,ebx
 *  011413f3   74 6a            je short .0114145f
 *  011413f5   8b8d 38fdffff    mov ecx,dword ptr ss:[ebp-0x2c8]
 *  011413fb   8379 14 08       cmp dword ptr ds:[ecx+0x14],0x8
 *  011413ff   72 02            jb short .01141403
 *  01141401   8b09             mov ecx,dword ptr ds:[ecx]
 *  01141403   8b85 20fdffff    mov eax,dword ptr ss:[ebp-0x2e0]
 *  01141409   c745 d4 07000000 mov dword ptr ss:[ebp-0x2c],0x7
 *  01141410   c745 d0 00000000 mov dword ptr ss:[ebp-0x30],0x0
 *  01141417   8b00             mov eax,dword ptr ds:[eax]
 *  01141419   8d0441           lea eax,dword ptr ds:[ecx+eax*2]
 *  0114141c   33c9             xor ecx,ecx
 *  0114141e   66:894d c0       mov word ptr ss:[ebp-0x40],cx
 *  01141422   3bf8             cmp edi,eax
 *  01141424   74 0e            je short .01141434
 *  01141426   2bc7             sub eax,edi
 *  01141428   8d4d c0          lea ecx,dword ptr ss:[ebp-0x40]
 *  0114142b   d1f8             sar eax,1
 *  0114142d   50               push eax
 *  0114142e   57               push edi
 *  0114142f   e8 fcd9f2ff      call .0106ee30
 *  01141434   8d45 c0          lea eax,dword ptr ss:[ebp-0x40]
 *  01141437   c745 fc 00000000 mov dword ptr ss:[ebp-0x4],0x0
 *  0114143e   3bd8             cmp ebx,eax
 *  01141440   74 0c            je short .0114144e
 *  01141442   6a ff            push -0x1
 *  01141444   6a 00            push 0x0
 *  01141446   50               push eax
 *  01141447   8bcb             mov ecx,ebx
 *  01141449   e8 c2def2ff      call .0106f310
 *  0114144e   837d d4 08       cmp dword ptr ss:[ebp-0x2c],0x8
 *  01141452   72 0b            jb short .0114145f
 *  01141454   ff75 c0          push dword ptr ss:[ebp-0x40]
 *  01141457   e8 3ecb0e00      call .0122df9a
 *  0114145c   83c4 04          add esp,0x4
 *  0114145f   8b4d f4          mov ecx,dword ptr ss:[ebp-0xc]
 *  01141462   64:890d 00000000 mov dword ptr fs:[0],ecx
 *  01141469   59               pop ecx
 *  0114146a   5f               pop edi
 *  0114146b   5e               pop esi
 *  0114146c   5b               pop ebx
 *  0114146d   8b4d f0          mov ecx,dword ptr ss:[ebp-0x10]
 *  01141470   33cd             xor ecx,ebp
 *  01141472   e8 14cb0e00      call .0122df8b
 *  01141477   8be5             mov esp,ebp
 *  01141479   5d               pop ebp
 *  0114147a   c2 0800          retn 0x8
 *  0114147d   cc               int3
 *  0114147e   cc               int3
 *
 *  In AngleBeats, base = 0x09a0000
 *  00B6B87C   CC               INT3
 *  00B6B87D   CC               INT3
 *  00B6B87E   CC               INT3
 *  00B6B87F   CC               INT3
 *  00B6B880   55               PUSH EBP
 *  00B6B881   8BEC             MOV EBP,ESP
 *  00B6B883   6A FF            PUSH -0x1
 *  00B6B885   68 7964ED00      PUSH .00ED6479
 *  00B6B88A   64:A1 00000000   MOV EAX,DWORD PTR FS:[0]
 *  00B6B890   50               PUSH EAX
 *  00B6B891   81EC 1C040000    SUB ESP,0x41C
 *  00B6B897   A1 E0A4F800      MOV EAX,DWORD PTR DS:[0xF8A4E0]
 *  00B6B89C   33C5             XOR EAX,EBP
 *  00B6B89E   8945 F0          MOV DWORD PTR SS:[EBP-0x10],EAX
 *  00B6B8A1   53               PUSH EBX
 *  00B6B8A2   56               PUSH ESI
 *  00B6B8A3   57               PUSH EDI
 *  00B6B8A4   50               PUSH EAX
 *  00B6B8A5   8D45 F4          LEA EAX,DWORD PTR SS:[EBP-0xC]
 *  00B6B8A8   64:A3 00000000   MOV DWORD PTR FS:[0],EAX
 *  00B6B8AE   8BD9             MOV EBX,ECX
 *  00B6B8B0   8B7D 08          MOV EDI,DWORD PTR SS:[EBP+0x8]
 *  00B6B8B3   837F 10 00       CMP DWORD PTR DS:[EDI+0x10],0x0
 *  00B6B8B7   8B45 0C          MOV EAX,DWORD PTR SS:[EBP+0xC]
 *  00B6B8BA   8985 E0FBFFFF    MOV DWORD PTR SS:[EBP-0x420],EAX
 *  00B6B8C0   8D47 10          LEA EAX,DWORD PTR DS:[EDI+0x10]
 *  00B6B8C3   89BD FCFBFFFF    MOV DWORD PTR SS:[EBP-0x404],EDI
 *  00B6B8C9   8985 F0FBFFFF    MOV DWORD PTR SS:[EBP-0x410],EAX
 *  00B6B8CF   0F84 31060000    JE .00B6BF06
 *  00B6B8D5   8B8B 1C010000    MOV ECX,DWORD PTR DS:[EBX+0x11C]
 *  00B6B8DB   B8 71F8428A      MOV EAX,0x8A42F871
 *  00B6B8E0   2B8B 18010000    SUB ECX,DWORD PTR DS:[EBX+0x118]
 *  00B6B8E6   F7E9             IMUL ECX
 *  00B6B8E8   8B85 F0FBFFFF    MOV EAX,DWORD PTR SS:[EBP-0x410]
 *  00B6B8EE   03D1             ADD EDX,ECX
 *  00B6B8F0   8B8B 20010000    MOV ECX,DWORD PTR DS:[EBX+0x120]
 *  00B6B8F6   2B8B 18010000    SUB ECX,DWORD PTR DS:[EBX+0x118]
 *  00B6B8FC   C1FA 09          SAR EDX,0x9
 *  00B6B8FF   8BF2             MOV ESI,EDX
 *  00B6B901   C1EE 1F          SHR ESI,0x1F
 *  00B6B904   03F2             ADD ESI,EDX
 *  00B6B906   0330             ADD ESI,DWORD PTR DS:[EAX]
 *  00B6B908   B8 71F8428A      MOV EAX,0x8A42F871
 *  00B6B90D   F7E9             IMUL ECX
 *  00B6B90F   03D1             ADD EDX,ECX
 *  00B6B911   C1FA 09          SAR EDX,0x9
 *  00B6B914   8BC2             MOV EAX,EDX
 *  00B6B916   C1E8 1F          SHR EAX,0x1F
 *  00B6B919   03C2             ADD EAX,EDX
 *  00B6B91B   3BC6             CMP EAX,ESI
 *  00B6B91D   73 1E            JNB SHORT .00B6B93D
 *  00B6B91F   81FE 7C214500    CMP ESI,0x45217C
 *  00B6B925   76 0A            JBE SHORT .00B6B931
 *  00B6B927   68 C031F200      PUSH .00F231C0                           ; ASCII "vector<T> too long"
 *  00B6B92C   E8 D2FC0E00      CALL .00C5B603
 *  00B6B931   56               PUSH ESI
 *  00B6B932   8D8B 18010000    LEA ECX,DWORD PTR DS:[EBX+0x118]
 *  00B6B938   E8 A38DFCFF      CALL .00B346E0
 *  00B6B93D   837F 14 08       CMP DWORD PTR DS:[EDI+0x14],0x8
 *  00B6B941   72 04            JB SHORT .00B6B947
 *  00B6B943   8B37             MOV ESI,DWORD PTR DS:[EDI]
 *  00B6B945   EB 02            JMP SHORT .00B6B949
 *  00B6B947   8BF7             MOV ESI,EDI
 *  00B6B949   89B5 F8FBFFFF    MOV DWORD PTR SS:[EBP-0x408],ESI
 *  00B6B94F   90               NOP
 *  00B6B950   8B57 14          MOV EDX,DWORD PTR DS:[EDI+0x14]
 *  00B6B953   83FA 08          CMP EDX,0x8
 *  00B6B956   72 04            JB SHORT .00B6B95C
 *  00B6B958   8B07             MOV EAX,DWORD PTR DS:[EDI]
 *  00B6B95A   EB 02            JMP SHORT .00B6B95E
 *  00B6B95C   8BC7             MOV EAX,EDI
 *  00B6B95E   8B8D F0FBFFFF    MOV ECX,DWORD PTR SS:[EBP-0x410]
 *  00B6B964   8B09             MOV ECX,DWORD PTR DS:[ECX]
 *  00B6B966   03C9             ADD ECX,ECX
 *  00B6B968   03C1             ADD EAX,ECX
 *  00B6B96A   3BF0             CMP ESI,EAX
 *  00B6B96C   0F84 94050000    JE .00B6BF06
 *  00B6B972   8B85 FCFBFFFF    MOV EAX,DWORD PTR SS:[EBP-0x404]
 *  00B6B978   8BFE             MOV EDI,ESI
 *  00B6B97A   C785 00FCFFFF 00>MOV DWORD PTR SS:[EBP-0x400],0x0
 *  00B6B984   C785 E8FBFFFF FF>MOV DWORD PTR SS:[EBP-0x418],-0x1
 *  00B6B98E   83FA 08          CMP EDX,0x8
 *  00B6B991   72 02            JB SHORT .00B6B995
 *  00B6B993   8B00             MOV EAX,DWORD PTR DS:[EAX]
 *  00B6B995   03C1             ADD EAX,ECX
 *  00B6B997   8D95 00FCFFFF    LEA EDX,DWORD PTR SS:[EBP-0x400]
 *  00B6B99D   8D8D E8FBFFFF    LEA ECX,DWORD PTR SS:[EBP-0x418]
 *  00B6B9A3   51               PUSH ECX
 *  00B6B9A4   50               PUSH EAX
 *  00B6B9A5   8D8D F8FBFFFF    LEA ECX,DWORD PTR SS:[EBP-0x408]
 *  00B6B9AB   E8 5025FBFF      CALL .00B1DF00
 *  00B6B9B0   8BB5 E8FBFFFF    MOV ESI,DWORD PTR SS:[EBP-0x418]
 *  00B6B9B6   83C4 08          ADD ESP,0x8
 *  00B6B9B9   83FE 0A          CMP ESI,0xA
 *  00B6B9BC   75 18            JNZ SHORT .00B6B9D6
 *  00B6B9BE   8BCB             MOV ECX,EBX
 *  00B6B9C0   E8 FB070000      CALL .00B6C1C0
 *  00B6B9C5   8BB5 F8FBFFFF    MOV ESI,DWORD PTR SS:[EBP-0x408]
 *  00B6B9CB   8BBD FCFBFFFF    MOV EDI,DWORD PTR SS:[EBP-0x404]
 *  00B6B9D1  ^E9 7AFFFFFF      JMP .00B6B950
 *  00B6B9D6   83FE 07          CMP ESI,0x7
 *  00B6B9D9   75 38            JNZ SHORT .00B6BA13
 *  00B6B9DB   33C0             XOR EAX,EAX
 *  00B6B9DD   C783 EC000000 00>MOV DWORD PTR DS:[EBX+0xEC],0x0
 *  00B6B9E7   8BCB             MOV ECX,EBX
 *  00B6B9E9   66:8983 F0000000 MOV WORD PTR DS:[EBX+0xF0],AX
 *  00B6B9F0   8983 F4000000    MOV DWORD PTR DS:[EBX+0xF4],EAX
 *  00B6B9F6   E8 C5070000      CALL .00B6C1C0
 *  00B6B9FB   8BB5 F8FBFFFF    MOV ESI,DWORD PTR SS:[EBP-0x408]
 *  00B6BA01   8BBD FCFBFFFF    MOV EDI,DWORD PTR SS:[EBP-0x404]
 *  00B6BA07   C683 FD000000 01 MOV BYTE PTR DS:[EBX+0xFD],0x1
 *  00B6BA0E  ^E9 3DFFFFFF      JMP .00B6B950
 *  00B6BA13   8B85 00FCFFFF    MOV EAX,DWORD PTR SS:[EBP-0x400]
 *  00B6BA19   85C0             TEST EAX,EAX
 *  00B6BA1B   75 3A            JNZ SHORT .00B6BA57
 *  00B6BA1D   85F6             TEST ESI,ESI
 *  00B6BA1F   0F84 BE000000    JE .00B6BAE3
 *  00B6BA25   85C0             TEST EAX,EAX
 *  00B6BA27   75 2E            JNZ SHORT .00B6BA57
 *  00B6BA29   A1 486A2C05      MOV EAX,DWORD PTR DS:[0x52C6A48]
 *  00B6BA2E   A8 01            TEST AL,0x1
 *  00B6BA30   75 0D            JNZ SHORT .00B6BA3F
 *  00B6BA32   83C8 01          OR EAX,0x1
 *  00B6BA35   A3 486A2C05      MOV DWORD PTR DS:[0x52C6A48],EAX
 *  00B6BA3A   E8 B15F0B00      CALL .00C219F0
 *  00B6BA3F   0FB7C6           MOVZX EAX,SI
 *  00B6BA42   80B8 506A2C05 01 CMP BYTE PTR DS:[EAX+0x52C6A50],0x1
 *  00B6BA49   75 0C            JNZ SHORT .00B6BA57
 *  00B6BA4B   8B43 6C          MOV EAX,DWORD PTR DS:[EBX+0x6C]
 *  00B6BA4E   99               CDQ
 *  00B6BA4F   2BC2             SUB EAX,EDX
 *  00B6BA51   8BC8             MOV ECX,EAX
 *  00B6BA53   D1F9             SAR ECX,1
 *  00B6BA55   EB 03            JMP SHORT .00B6BA5A
 *  00B6BA57   8B4B 6C          MOV ECX,DWORD PTR DS:[EBX+0x6C]
 *  00B6BA5A   8B15 9C5DFA00    MOV EDX,DWORD PTR DS:[0xFA5D9C]
 *  00B6BA60   898D ECFBFFFF    MOV DWORD PTR SS:[EBP-0x414],ECX
 *  00B6BA66   83BA 84CF0000 01 CMP DWORD PTR DS:[EDX+0xCF84],0x1
 *  00B6BA6D   75 26            JNZ SHORT .00B6BA95
 *  00B6BA6F   8B43 60          MOV EAX,DWORD PTR DS:[EBX+0x60]
 *  00B6BA72   03C1             ADD EAX,ECX
 *  00B6BA74   8B8B AC000000    MOV ECX,DWORD PTR DS:[EBX+0xAC]
 *  00B6BA7A   8985 04FCFFFF    MOV DWORD PTR SS:[EBP-0x3FC],EAX
 *  00B6BA80   8B43 18          MOV EAX,DWORD PTR DS:[EBX+0x18]
 *  00B6BA83   03C1             ADD EAX,ECX
 *  00B6BA85   3985 04FCFFFF    CMP DWORD PTR SS:[EBP-0x3FC],EAX
 *  00B6BA8B   7F 39            JG SHORT .00B6BAC6
 *  00B6BA8D   398D 04FCFFFF    CMP DWORD PTR SS:[EBP-0x3FC],ECX
 *  00B6BA93   EB 24            JMP SHORT .00B6BAB9
 *  00B6BA95   8B43 5C          MOV EAX,DWORD PTR DS:[EBX+0x5C]
 *  00B6BA98   03C1             ADD EAX,ECX
 *  00B6BA9A   8B8B A8000000    MOV ECX,DWORD PTR DS:[EBX+0xA8]
 *  00B6BAA0   8985 04FCFFFF    MOV DWORD PTR SS:[EBP-0x3FC],EAX
 *  00B6BAA6   8B43 18          MOV EAX,DWORD PTR DS:[EBX+0x18]
 *  00B6BAA9   03C1             ADD EAX,ECX
 *  00B6BAAB   3985 04FCFFFF    CMP DWORD PTR SS:[EBP-0x3FC],EAX
 *  00B6BAB1   7F 13            JG SHORT .00B6BAC6
 *  00B6BAB3   398D 04FCFFFF    CMP DWORD PTR SS:[EBP-0x3FC],ECX
 *  00B6BAB9   7E 3F            JLE SHORT .00B6BAFA
 *  00B6BABB   8BCE             MOV ECX,ESI
 *  00B6BABD   E8 EEF9FFFF      CALL .00B6B4B0
 *  00B6BAC2   84C0             TEST AL,AL
 *  00B6BAC4   75 34            JNZ SHORT .00B6BAFA
 *  00B6BAC6   8BCB             MOV ECX,EBX
 *  00B6BAC8   E8 F3060000      CALL .00B6C1C0
 *  00B6BACD   83BD 00FCFFFF 00 CMP DWORD PTR SS:[EBP-0x400],0x0
 *  00B6BAD4   75 1E            JNZ SHORT .00B6BAF4
 *  00B6BAD6   83FE 20          CMP ESI,0x20
 *  00B6BAD9   74 08            JE SHORT .00B6BAE3
 *  00B6BADB   81FE 00300000    CMP ESI,0x3000
 *  00B6BAE1   75 11            JNZ SHORT .00B6BAF4
 *  00B6BAE3   8BB5 F8FBFFFF    MOV ESI,DWORD PTR SS:[EBP-0x408]
 *  00B6BAE9   8BBD FCFBFFFF    MOV EDI,DWORD PTR SS:[EBP-0x404]
 *  00B6BAEF  ^E9 5CFEFFFF      JMP .00B6B950
 *  00B6BAF4   8B15 9C5DFA00    MOV EDX,DWORD PTR DS:[0xFA5D9C]
 *  00B6BAFA   83BA 84CF0000 01 CMP DWORD PTR DS:[EDX+0xCF84],0x1
 *  00B6BB01   75 66            JNZ SHORT .00B6BB69
 *  00B6BB03   8B83 A8000000    MOV EAX,DWORD PTR DS:[EBX+0xA8]
 *  00B6BB09   F7D8             NEG EAX
 *  00B6BB0B   3943 5C          CMP DWORD PTR DS:[EBX+0x5C],EAX
 *  00B6BB0E   7F 68            JG SHORT .00B6BB78
 *  00B6BB10   8B9D E0FBFFFF    MOV EBX,DWORD PTR SS:[EBP-0x420]
 *  00B6BB16   85DB             TEST EBX,EBX
 *  00B6BB18   0F84 E8030000    JE .00B6BF06
 *  00B6BB1E   8B8D FCFBFFFF    MOV ECX,DWORD PTR SS:[EBP-0x404]
 *  00B6BB24   8379 14 08       CMP DWORD PTR DS:[ECX+0x14],0x8
 *  00B6BB28   72 02            JB SHORT .00B6BB2C
 *  00B6BB2A   8B09             MOV ECX,DWORD PTR DS:[ECX]
 *  00B6BB2C   8B85 F0FBFFFF    MOV EAX,DWORD PTR SS:[EBP-0x410]
 *  00B6BB32   C745 EC 07000000 MOV DWORD PTR SS:[EBP-0x14],0x7
 *  00B6BB39   C745 E8 00000000 MOV DWORD PTR SS:[EBP-0x18],0x0
 *  00B6BB40   8B00             MOV EAX,DWORD PTR DS:[EAX]
 *  00B6BB42   8D0441           LEA EAX,DWORD PTR DS:[ECX+EAX*2]
 *  00B6BB45   33C9             XOR ECX,ECX
 *  00B6BB47   66:894D D8       MOV WORD PTR SS:[EBP-0x28],CX
 *  00B6BB4B   3BF8             CMP EDI,EAX
 *  00B6BB4D   74 0E            JE SHORT .00B6BB5D
 *  00B6BB4F   2BC7             SUB EAX,EDI
 *  00B6BB51   8D4D D8          LEA ECX,DWORD PTR SS:[EBP-0x28]
 *  00B6BB54   D1F8             SAR EAX,1
 *  00B6BB56   50               PUSH EAX
 *  00B6BB57   57               PUSH EDI
 *  00B6BB58   E8 E334F2FF      CALL .00A8F040
 *  00B6BB5D   C745 FC 00000000 MOV DWORD PTR SS:[EBP-0x4],0x0
 *  00B6BB64   E9 82030000      JMP .00B6BEEB
 *  00B6BB69   8B43 60          MOV EAX,DWORD PTR DS:[EBX+0x60]
 *  00B6BB6C   3B83 AC000000    CMP EAX,DWORD PTR DS:[EBX+0xAC]
 *  00B6BB72   0F8D 23030000    JGE .00B6BE9B
 *  00B6BB78   8D8D 08FCFFFF    LEA ECX,DWORD PTR SS:[EBP-0x3F8]
 *  00B6BB7E   E8 EDDEFFFF      CALL .00B69A70
 *  00B6BB83   C745 FC 02000000 MOV DWORD PTR SS:[EBP-0x4],0x2
 *  00B6BB8A   8B43 78          MOV EAX,DWORD PTR DS:[EBX+0x78]
 *  00B6BB8D   8B15 C05DFA00    MOV EDX,DWORD PTR DS:[0xFA5DC0]
 *  00B6BB93   8985 F4FBFFFF    MOV DWORD PTR SS:[EBP-0x40C],EAX
 *  00B6BB99   83F8 FF          CMP EAX,-0x1
 *  00B6BB9C   75 23            JNZ SHORT .00B6BBC1
 *  00B6BB9E   80BA 60010000 00 CMP BYTE PTR DS:[EDX+0x160],0x0
 *  00B6BBA5   74 11            JE SHORT .00B6BBB8
 *  00B6BBA7   8B83 E0000000    MOV EAX,DWORD PTR DS:[EBX+0xE0]
 *  00B6BBAD   8985 F4FBFFFF    MOV DWORD PTR SS:[EBP-0x40C],EAX
 *  00B6BBB3   83F8 FF          CMP EAX,-0x1
 *  00B6BBB6   75 09            JNZ SHORT .00B6BBC1
 *  00B6BBB8   8B43 24          MOV EAX,DWORD PTR DS:[EBX+0x24]
 *  00B6BBBB   8985 F4FBFFFF    MOV DWORD PTR SS:[EBP-0x40C],EAX
 *  00B6BBC1   8B4B 7C          MOV ECX,DWORD PTR DS:[EBX+0x7C]
 *  00B6BBC4   898D E4FBFFFF    MOV DWORD PTR SS:[EBP-0x41C],ECX
 *  00B6BBCA   83F9 FF          CMP ECX,-0x1
 *  00B6BBCD   75 23            JNZ SHORT .00B6BBF2
 *  00B6BBCF   80BA 60010000 00 CMP BYTE PTR DS:[EDX+0x160],0x0
 *  00B6BBD6   74 11            JE SHORT .00B6BBE9
 *  00B6BBD8   8B8B E4000000    MOV ECX,DWORD PTR DS:[EBX+0xE4]
 *  00B6BBDE   898D E4FBFFFF    MOV DWORD PTR SS:[EBP-0x41C],ECX
 *  00B6BBE4   83F9 FF          CMP ECX,-0x1
 *  00B6BBE7   75 09            JNZ SHORT .00B6BBF2
 *  00B6BBE9   8B43 28          MOV EAX,DWORD PTR DS:[EBX+0x28]
 *  00B6BBEC   8985 E4FBFFFF    MOV DWORD PTR SS:[EBP-0x41C],EAX
 *  00B6BBF2   8B83 80000000    MOV EAX,DWORD PTR DS:[EBX+0x80]
 *  00B6BBF8   8985 04FCFFFF    MOV DWORD PTR SS:[EBP-0x3FC],EAX
 *  00B6BBFE   83F8 FF          CMP EAX,-0x1
 *  00B6BC01   75 23            JNZ SHORT .00B6BC26
 *  00B6BC03   80BA 60010000 00 CMP BYTE PTR DS:[EDX+0x160],0x0
 *  00B6BC0A   74 11            JE SHORT .00B6BC1D
 *  00B6BC0C   8B83 E8000000    MOV EAX,DWORD PTR DS:[EBX+0xE8]
 *  00B6BC12   8985 04FCFFFF    MOV DWORD PTR SS:[EBP-0x3FC],EAX
 *  00B6BC18   83F8 FF          CMP EAX,-0x1
 *  00B6BC1B   75 09            JNZ SHORT .00B6BC26
 *  00B6BC1D   8B43 2C          MOV EAX,DWORD PTR DS:[EBX+0x2C]
 *  00B6BC20   8985 04FCFFFF    MOV DWORD PTR SS:[EBP-0x3FC],EAX
 *  00B6BC26   8B53 68          MOV EDX,DWORD PTR DS:[EBX+0x68]
 *  00B6BC29   0353 60          ADD EDX,DWORD PTR DS:[EBX+0x60]
 *  00B6BC2C   8B4B 5C          MOV ECX,DWORD PTR DS:[EBX+0x5C]
 *  00B6BC2F   034B 64          ADD ECX,DWORD PTR DS:[EBX+0x64]
 *  00B6BC32   83BD 00FCFFFF 01 CMP DWORD PTR SS:[EBP-0x400],0x1
 *  00B6BC39   8BB5 F4FBFFFF    MOV ESI,DWORD PTR SS:[EBP-0x40C]
 *  00B6BC3F   8B43 6C          MOV EAX,DWORD PTR DS:[EBX+0x6C]
 *  00B6BC42   C785 DCFBFFFF 00>MOV DWORD PTR SS:[EBP-0x424],0x0
 *  00B6BC4C   0F44B5 DCFBFFFF  CMOVE ESI,DWORD PTR SS:[EBP-0x424]
 *  00B6BC53   80BB FE000000 00 CMP BYTE PTR DS:[EBX+0xFE],0x0
 *  00B6BC5A   89B5 F4FBFFFF    MOV DWORD PTR SS:[EBP-0x40C],ESI
 *  00B6BC60   8BB5 00FCFFFF    MOV ESI,DWORD PTR SS:[EBP-0x400]
 *  00B6BC66   8985 10FCFFFF    MOV DWORD PTR SS:[EBP-0x3F0],EAX
 *  00B6BC6C   8B85 F4FBFFFF    MOV EAX,DWORD PTR SS:[EBP-0x40C]
 *  00B6BC72   8985 14FCFFFF    MOV DWORD PTR SS:[EBP-0x3EC],EAX
 *  00B6BC78   8B85 E4FBFFFF    MOV EAX,DWORD PTR SS:[EBP-0x41C]
 *  00B6BC7E   89B5 08FCFFFF    MOV DWORD PTR SS:[EBP-0x3F8],ESI
 *  00B6BC84   8BB5 E8FBFFFF    MOV ESI,DWORD PTR SS:[EBP-0x418]
 *  00B6BC8A   8985 18FCFFFF    MOV DWORD PTR SS:[EBP-0x3E8],EAX
 *  00B6BC90   8B85 04FCFFFF    MOV EAX,DWORD PTR SS:[EBP-0x3FC]
 *  00B6BC96   89B5 0CFCFFFF    MOV DWORD PTR SS:[EBP-0x3F4],ESI
 *  00B6BC9C   8985 1CFCFFFF    MOV DWORD PTR SS:[EBP-0x3E4],EAX
 *  00B6BCA2   898D 20FCFFFF    MOV DWORD PTR SS:[EBP-0x3E0],ECX
 *  00B6BCA8   8995 24FCFFFF    MOV DWORD PTR SS:[EBP-0x3DC],EDX
 *  00B6BCAE   74 19            JE SHORT .00B6BCC9
 *  00B6BCB0   8B43 5C          MOV EAX,DWORD PTR DS:[EBX+0x5C]
 *  00B6BCB3   8983 00010000    MOV DWORD PTR DS:[EBX+0x100],EAX
 *  00B6BCB9   8B43 60          MOV EAX,DWORD PTR DS:[EBX+0x60]
 *  00B6BCBC   8983 04010000    MOV DWORD PTR DS:[EBX+0x104],EAX
 *  00B6BCC2   C683 FE000000 00 MOV BYTE PTR DS:[EBX+0xFE],0x0
 *  00B6BCC9   A1 9C5DFA00      MOV EAX,DWORD PTR DS:[0xFA5D9C]
 *  00B6BCCE   83B8 84CF0000 01 CMP DWORD PTR DS:[EAX+0xCF84],0x1
 *  00B6BCD5   8B43 70          MOV EAX,DWORD PTR DS:[EBX+0x70]
 *  00B6BCD8   75 0B            JNZ SHORT .00B6BCE5
 *  00B6BCDA   0385 ECFBFFFF    ADD EAX,DWORD PTR SS:[EBP-0x414]
 *  00B6BCE0   0143 60          ADD DWORD PTR DS:[EBX+0x60],EAX
 *  00B6BCE3   EB 09            JMP SHORT .00B6BCEE
 *  00B6BCE5   0385 ECFBFFFF    ADD EAX,DWORD PTR SS:[EBP-0x414]
 *  00B6BCEB   0143 5C          ADD DWORD PTR DS:[EBX+0x5C],EAX
 *  00B6BCEE   8B8D 00FCFFFF    MOV ECX,DWORD PTR SS:[EBP-0x400]
 *  00B6BCF4   85C9             TEST ECX,ECX
 *  00B6BCF6   75 42            JNZ SHORT .00B6BD3A
 *  00B6BCF8   81FE 0C300000    CMP ESI,0x300C ; jichi: type2 found here
 *  00B6BCFE   74 10            JE SHORT .00B6BD10
 *  00B6BD00   81FE 0E300000    CMP ESI,0x300E
 *  00B6BD06   74 08            JE SHORT .00B6BD10
 *  00B6BD08   81FE 08FF0000    CMP ESI,0xFF08
 *  00B6BD0E   75 2A            JNZ SHORT .00B6BD3A
 *  00B6BD10   80BB FD000000 00 CMP BYTE PTR DS:[EBX+0xFD],0x0
 *  00B6BD17   74 10            JE SHORT .00B6BD29
 *  00B6BD19   56               PUSH ESI
 */
bool InsertSiglus2Hook()
{
  //const BYTE bytes[] = { // size = 14
  //  0x01,0x53, 0x58,                // 0153 58          add dword ptr ds:[ebx+58],edx
  //  0x8b,0x95, 0x34,0xfd,0xff,0xff, // 8b95 34fdffff    mov edx,dword ptr ss:[ebp-2cc]
  //  0x8b,0x43, 0x58,                // 8b43 58          mov eax,dword ptr ds:[ebx+58]
  //  0x3b,0xd7                       // 3bd7             cmp edx,edi ; hook here
  //};
  //enum { cur_ins_size = 2 };
  //enum { addr_offset = sizeof(bytes) - cur_ins_size }; // = 14 - 2  = 12, current inst is the last one

  ULONG range = min(module_limit_ - module_base_, MAX_REL_ADDR);
  ULONG addr;
  { // type 1
    const BYTE bytes[] = {
      0x3b,0xd7,  // cmp edx,edi ; hook here
      0x75,0x4b   // jnz short
    };
    //enum { addr_offset = 0 };
    addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_base_ + range);
    if (addr)
      ConsoleOutput("vnreng:Siglus2: type 1 pattern found");
  }
  if (!addr) {
    // 81fe0c300000
    const BYTE bytes[] = {
      0x81,0xfe, 0x0c,0x30,0x00,0x00 // 0114124a   81fe 0c300000    cmp esi,0x300c  ; jichi: hook here
    };
    //enum { addr_offset = 0 };
    addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_base_ + range);
    if (addr)
      ConsoleOutput("vnreng:Siglus2: type 2 pattern found");
  }

  if (!addr) {
    ConsoleOutput("vnreng:Siglus2: both type1 and type2 patterns not found");
    return false;
  }

  HookParam hp = {};
  hp.address = addr;
  hp.offset = pusha_esi_off - 4; // -0x20
  hp.type = USING_UNICODE|FIXING_SPLIT; // jichi 6/1/2014: fixing the split value
  hp.length_offset = 1;

  ConsoleOutput("vnreng: INSERT Siglus2");
  NewHook(hp, "SiglusEngine2");
  //ConsoleOutput("SiglusEngine2");
  return true;
}

static void SpecialHookSiglus1(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  __asm
  {
    mov edx,esp_base
    mov ecx,[edx-0xc]
    mov eax,[ecx+0x14]
    add ecx,4
    cmp eax,0x8
    cmovnb ecx,[ecx]
    mov edx,len
    add eax,eax
    mov [edx],eax
    mov edx,data
    mov [edx],ecx
  }
}

// jichi: 8/17/2013: Change return type to bool
bool InsertSiglus1Hook()
{
  const BYTE bytes[] = {0x33,0xc0,0x8b,0xf9,0x89,0x7c,0x24};
  ULONG range = max(module_limit_ - module_base_, MAX_REL_ADDR);
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_base_ + range);
  if (!addr) { // jichi 8/17/2013: Add "== 0" check to prevent breaking new games
    //ConsoleOutput("Unknown SiglusEngine");
    ConsoleOutput("vnreng:Siglus: pattern not found");
    return false;
  }

  DWORD limit = addr - 0x100;
  while (addr > limit) {
    if (*(WORD*)addr == 0xff6a) {
      HookParam hp = {};
      hp.address = addr;
      hp.text_fun = SpecialHookSiglus1;
      hp.type = USING_UNICODE;
      ConsoleOutput("vnreng: INSERT Siglus");
      NewHook(hp, "SiglusEngine");
      //RegisterEngineType(ENGINE_SIGLUS);
      return true;
    }
    addr--;
  }
  ConsoleOutput("vnreng:Siglus: failed");
  return false;
}

} // unnamed namespace

// jichi 8/17/2013: Insert old first. As the pattern could also be found in the old engine.
bool InsertSiglusHook()
{
  if (InsertSiglus1Hook())
    return true;
  bool ok = InsertSiglus2Hook();
  ok = InsertSiglus3Hook() || ok;
  ok = InsertSiglus4Hook() || ok;
  return ok;
}

/********************************************************************************************
MAJIRO hook:
  Game folder contains both data.arc and scenario.arc. arc files is
  quite common seen so we need 2 files to confirm it's MAJIRO engine.

  Font caching issue. Find call to TextOutA and the function entry.

  The original Majiro hook will catch furiga mixed with the text.
  To split them out we need to find a parameter. Seems there's no
  simple way to handle this case.
  At the function entry, EAX seems to point to a structure to describe
  current drawing context. +28 seems to be font size. +48 is negative
  if furigana. I don't know exact meaning of this structure,
  just do memory comparisons and get the value working for current release.

********************************************************************************************/
// jichi 11/28/2014: Disable original Majiro special hook that does not work for new Majiro games, such as: 流され妻
// In the new Majiro engine, arg1 could be zero
#if 0
static void SpecialHookMajiro(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  // jichi 5/12/2014
  // See: http://stackoverflow.com/questions/14210614/bind-function-parameter-to-specific-register
  // x86-64, the first 6 (integral) parameters are passed in the registers %rdi, %rsi, %rdx, %rcx, %r8, and %r9.
  __asm
  {
    mov edx,esp_base
    mov edi,[edx+0xc] ; jichi 5/11/2014: the third function parameter is LPCSTR text
    mov eax,data
    mov [eax],edi
    or ecx,0xffffffff
    xor eax,eax
    repne scasb
    not ecx
    dec ecx
    mov eax,len
    mov [eax],ecx
    mov eax,[edx+4]    ; jichi 5/11/2014: the first function parameter is LPCSTR font name (MS Gothic)
    mov edx,[eax+0x28] ; 0x28 and 0x48 are in the caller of this fuction hook
    mov eax,[eax+0x48] ; *split = ([eax+0x28] & 0xff) | (([eax+0x48] >> 1) & 0xffff00)
    sar eax,0x1f
    mov dh,al
    mov ecx,split
    mov [ecx],edx
  }
}
#endif // 0

/** jichi 12/28/2014: new Majiro hook pattern
 *
 *  Different function starts:
 *
 *  Old Majiro:
 *  enum { sub_esp = 0xec81 }; // caller pattern: sub esp = 0x81,0xec byte
 *
 *  New Majiro since [141128] [アトリエさくら] 流され妻、綾�“ネトラレ”��体験版
 *  003e9230   55               push ebp
 *  003e9231   8bec             mov ebp,esp
 *  003e9233   83ec 64          sub esp,0x64
 *
 *  Also, function addresses are fixed in old majiro, but floating in new majiro.
 *  In the old Majiro game, caller's address could be used as split.
 *  In the new Majiro game, the hooked function is invoked by the same caller.
 *
 *  Use a split instead.
 *  Sample stack values are as follows.
 *  - Old majiro: arg3 is text, arg1 is font name
 *  - New majiro: arg3 is text, arg4 is font name
 *
 *  Name:
 *  0038f164   003e8163  return to .003e8163 from .003e9230
 *  0038f168   00000000
 *  0038f16c   00000000
 *  0038f170   08b04dbc ; jichi: arg3, text
 *  0038f174   006709f0 ; jichi: arg4, font name
 *  0038f178   006dace8
 *  0038f17c   00000000
 *  0038f180   00000013
 *  0038f184   006fcba8
 *  0038f188   00000078 ; jichi: 0x24, alternative split
 *  0038f18c   00000078
 *  0038f190   00000018
 *  0038f194   00000002
 *  0038f198   08b04dbc
 *  0038f19c   006709f0
 *  0038f1a0   00000000
 *  0038f1a4   00000000
 *  0038f1a8   00000078
 *  0038f1ac   00000018
 *  0038f1b0   08aa0130
 *  0038f1b4   01b6b6c0
 *  0038f1b8   beff26e4
 *  0038f1bc   0038f1fc
 *  0038f1c0   004154af  return to .004154af from .00415400 ; jichi: 0x52, could be used as split
 *  0038f1c4   0000000e
 *  0038f1c8   000001ae
 *  0038f1cc   00000158
 *  0038f1d0   00000023
 *  0038f1d4   beff2680
 *  0038f1d8   0038f208
 *  0038f1dc   003ecfda  return to .003ecfda from .00415400
 *
 *  Scenario:
 *  0038e57c   003e8163  return to .003e8163 from .003e9230
 *  0038e580   00000000
 *  0038e584   00000000
 *  0038e588   0038ee4c  ; jichi: arg3, text
 *  0038e58c   004d5400  .004d5400 ; jichi: arg4, font name
 *  0038e590   006dace8
 *  0038e594   0038ee6d
 *  0038e598   004d7549  .004d7549
 *  0038e59c   00000000
 *  0038e5a0   00000180 ; jichi: 0x24, alternative hook
 *  0038e5a4   00000180
 *  0038e5a8   00000018
 *  0038e5ac   00000002
 *  0038e5b0   0038ee4c
 *  0038e5b4   004d5400  .004d5400
 *  0038e5b8   00000000
 *  0038e5bc   00000000
 *  0038e5c0   00000180
 *  0038e5c4   00000018
 *  0038e5c8   006a0180
 *  0038e5cc   0038e5f8
 *  0038e5d0   0041fc87  return to .0041fc87 from .0041fc99
 *  0038e5d4   0038e5f8
 *  0038e5d8   00418165  return to .00418165 from .0041fc81 ; jichi: used as split
 *  0038e5dc   004d7549  .004d7549
 *  0038e5e0   0038ee6d
 *  0038e5e4   0038e608
 *  0038e5e8   00419555  return to .00419555 from .0041814e
 *  0038e5ec   00000000
 *  0038e5f0   004d7549  .004d7549
 *  0038e5f4   0038ee6d
 *
 *  12/4/2014: Add split for furigana.
 *  Sample game: [141128] [チュアブルソフト] 残念な俺達�青春事情
 *  Following are memory values after arg4 (font name)
 *
 *  Surface: � *  00EC5400  82 6C 82 72 20 82 6F 83 53 83 56 83 62 83 4E 00  �� �ゴシヂ�.
 *  00EC5410  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 *  00EC5420  01 00 00 00 00 00 00 00 1C 00 00 00 0D 00 00 00   ....... .......
 *  00EC5430 (2D)00 00 00 FF FF FF 00 00 00 00 02 00 00 00 00  -...���.... .... ; jichi: first byte as split in parenthesis
 *  00EC5440  00(00 00 00)60 F7 3F 00 F0 D8 FF FF 00 00 00 00  ....`・.   ....  ; jichi: first word without first byte as split
 *
 *  00EC5450  32 01 00 00 0C 00 00 00 A0 02 00 00 88 00 00 00  2 ......� ..・..
 *  00EC5460  00 00 00 00 01 00 00 00 00 00 00 00 32 01 00 00  .... .......2 ..
 *  00EC5470  14 00 00 00 01 00 00 00 82 6C 82 72 20 82 6F 83   ... ...�� �・ ; MS P Gothic
 *  00EC5480  53                                               S
 *
 *  Furigana: そ�
 *  00EC5400  82 6C 82 72 20 83 53 83 56 83 62 83 4E 00 4E 00  �� ゴシヂ�.N.
 *  00EC5410  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 *  00EC5420  01 00 00 00 00 00 00 00 0E 00 00 00 06 00 00 00   ....... ... ...
 *  00EC5430 (16)00 00 00 FF FF FF 00 00 00 00 02 00 00 00 00   ...���.... ....
 *  00EC5440  00(00 00 00)60 F7 3F 00 F0 D8 FF FF 00 00 00 00  ....`・.   ....
 *
 *  00EC5450  32 01 00 00 0C 00 00 00 A0 02 00 00 88 00 00 00  2 ......� ..・..
 *  00EC5460  00 00 00 00 00 00 00 00 00 00 00 00 32 01 00 00  ............2 ..
 *  00EC5470  14 00 00 00 01 00 00 00 82 6C 82 72 20 82 6F 83   ... ...�� �・ ; MS P Gothic
 *  00EC5480  53                                               S
 *
 *  Furigana: そ�
 *  00EC5400  82 6C 82 72 20 82 6F 83 53 83 56 83 62 83 4E 00  �� �ゴシヂ�.
 *  00EC5410  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 *  00EC5420  01 00 00 00 00 00 00 00 0E 00 00 00 06 00 00 00   ....... ... ...
 *  00EC5430 (2D)00 00 00 FF FF FF 00 00 00 00 02 00 00 00 00  -...���.... ....
 *  00EC5440  00(00 00 00)60 F7 3F 00 2B 01 00 00 06 00 00 00  ....`・.+ .. ...
 *
 *  00EC5450  32 01 00 00 0C 00 00 00 A0 02 00 00 88 00 00 00  2 ......� ..・..
 *  00EC5460  00 00 00 00 00 00 00 00 00 00 00 00 32 01 00 00  ............2 ..
 *  00EC5470  14 00 00 00 01 00 00 00 82 6C 82 72 20 82 6F 83   ... ...�� �・ ; MS P Gothic
 *  00EC5480  53                                               S
 *
 *  ---- need to split the above and below case
 *
 *  Text: � *  00EC5400  82 6C 82 72 20 82 6F 83 53 83 56 83 62 83 4E 00  �� �ゴシヂ�.
 *  00EC5410  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 *  00EC5420  01 00 00 00 00 00 00 00 1C 00 00 00 0D 00 00 00   ....... .......
 *  00EC5430 (2D)00 00 00 FF FF FF 00 00 00 00 02 00 00 00 00  -...���.... .... ; jichi: first byte as split in parenthesis
 *  00EC5440  FF(FF FF FF)60 F7 3F 00 32 01 00 00 14 00 00 00  ����`・.2 .. ... ; jichi: first word without first byte as split
 *
 *  00EC5450  32 01 00 00 0C 00 00 00 A0 02 00 00 88 00 00 00  2 ......� ..・..
 *  00EC5460  00 00 00 00 01 00 00 00 00 00 00 00 32 01 00 00  .... .......2 ..
 *  00EC5470  14 00 00 00 00 00 00 00 82 6C 82 72 20 82 6F 83   .......�� �・ ; MS P Gothic
 *  00EC5480  53                                               S
 *
 *  Text: らには、一人の少女� *  00EC5400  82 6C 82 72 20 82 6F 83 53 83 56 83 62 83 4E 00  �� �ゴシヂ�.
 *  00EC5410  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 *  00EC5420  01 00 00 00 00 00 00 00 1C 00 00 00 0D 00 00 00   ....... .......
 *  00EC5430 (2D)00 00 00 FF FF FF 00 00 00 00 02 00 00 00 00  -...���.... ....
 *  00EC5440  FF(FF FF FF)60 F7 3F 00 4D 01 00 00 14 00 00 00  ����`・.M .. ...
 *
 *  00EC5450  32 01 00 00 0C 00 00 00 A0 02 00 00 88 00 00 00  2 ......� ..・..
 *  00EC5460  00 00 00 00 01 00 00 00 00 00 00 00 32 01 00 00  .... .......2 ..
 *  00EC5470  14 00 00 00 00 00 00 00 82 6C 82 72 20 82 6F 83   .......�� �・ ; MS P Gothic
 *  00EC5480  53                                               S
 */

namespace { // unnamed

// These values are the same as the assembly logic of ITH:
//     ([eax+0x28] & 0xff) | (([eax+0x48] >> 1) & 0xffffff00)
// 0x28 = 10 * 4, 0x48 = 18 / 4
inline DWORD MajiroOldFontSplit(const DWORD *arg) // arg is supposed to be a string, though
{ return (arg[10] & 0xff) | ((arg[18] >> 1) & 0xffffff00); }

// Remove lower bytes use 0xffffff00, which are different for furigana
inline DWORD MajiroNewFontSplit(const DWORD *arg) // arg is supposed to be a string, though
{ return (arg[12] & 0xff) | (arg[16] & 0xffffff00); }

void SpecialHookMajiro(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  DWORD arg3 = argof(3, esp_base); // text
  *data = arg3;
  *len = ::strlen((LPCSTR)arg3);
  // IsBadReadPtr is not needed for old Majiro game.
  // I am not sure if it is needed by new Majiro game.
  if (hp->user_value) { // new majiro
    if (DWORD arg4 = argof(4, esp_base)) // old majiro
      *split = MajiroNewFontSplit((LPDWORD)arg4);
    else
      *split = *(DWORD *)(esp_base + 0x5c); // = 4 * 23, caller's caller
  } else if (DWORD arg1 = argof(1, esp_base)) // old majiro
    *split = MajiroOldFontSplit((LPDWORD)arg1);
}
} // unnamed namespace
bool InsertMajiroHook()
{
  // jichi 7/12/2014: Change to accurate memory ranges
  ULONG startAddress, stopAddress;
  if (!NtInspect::getProcessMemoryRange(&startAddress, &stopAddress)) { // need accurate stopAddress
    ConsoleOutput("vnreng:Majiro: failed to get memory range");
    return false;
  }
  // jichi 4/19/2014: There must be a function in Majiro game which contains 6 TextOutA.
  // That function draws all texts.
  //
  // jichi 11/28/2014: Add new function signature
  const DWORD funcs[] = { // caller patterns
    0xec81,     // sub esp = 0x81,0xec byte old majiro
    0x83ec8b55  // mov ebp,esp, sub esp,*  new majiro
  };
  enum { FunctionCount = sizeof(funcs) / sizeof(*funcs) };
  ULONG addr = MemDbg::findMultiCallerAddress((ULONG)::TextOutA, funcs, FunctionCount, startAddress, stopAddress);
  //ULONG addr = MemDbg::findCallerAddress((ULONG)::TextOutA, 0x83ec8b55, startAddress, stopAddress);
  if (!addr) {
    ConsoleOutput("vnreng:Majiro: failed");
    return false;
  }

  bool newMajiro = 0x55 == *(BYTE *)addr;

  HookParam hp = {};
  //hp.offset=0xc;
  //hp.split=4;
  //hp.split_index=0x28;
  //hp.type|=USING_STRING|USING_SPLIT|SPLIT_INDIRECT;
  hp.address = addr;
  hp.text_fun = SpecialHookMajiro;
  hp.user_value = newMajiro;
  if (newMajiro) {
    hp.type = NO_CONTEXT; // do not use return address for new majiro
    ConsoleOutput("vnreng: INSERT Majiro2");
    NewHook(hp, "Majiro2");
  } else {
    ConsoleOutput("vnreng: INSERT Majiro");
    NewHook(hp, "Majiro");
  }
  //RegisterEngineType(ENGINE_MAJIRO);
  return true;
}

/********************************************************************************************
CMVS hook:
  Process name is cmvs.exe or cnvs.exe or cmvs*.exe. Used by PurpleSoftware games.

  Font caching issue. Find call to GetGlyphOutlineA and the function entry.
********************************************************************************************/

namespace { // unnamed

// jichi 3/6/2014: This is the original CMVS hook in ITH
// It does not work for パ�プルソフトウェア games after しあわせ家族部 (2012)
bool InsertCMVS1Hook()
{
  // jichi 7/12/2014: Change to accurate memory ranges
  ULONG startAddress, stopAddress;
  if (!NtInspect::getProcessMemoryRange(&startAddress, &stopAddress)) { // need accurate stopAddress
    ConsoleOutput("vnreng:CMVS1: failed to get memory range");
    return false;
  }
  enum { sub_esp = 0xec83 }; // caller pattern: sub esp = 0x83,0xec
  ULONG addr = MemDbg::findCallerAddress((ULONG)::GetGlyphOutlineA, sub_esp, startAddress, stopAddress);
  if (!addr) {
    ConsoleOutput("vnreng:CMVS1: failed");
    return false;
  }

  HookParam hp = {};
  hp.address = addr;
  hp.offset = 0x8;
  hp.split = -0x18;
  hp.type = BIG_ENDIAN|USING_SPLIT;
  hp.length_offset = 1;

  ConsoleOutput("vnreng: INSERT CMVS1");
  NewHook(hp, "CMVS");
  //RegisterEngineType(ENGINE_CMVS);
  return true;
}

/**
 *  CMSV
 *  Sample games:
 *  ハピメア: /HAC@48FF3:cmvs32.exe
 *  ハピメアFD: /HB-1C*0@44EE95
 *
 *  Optional: ハピメアFD: /HB-1C*0@44EE95
 *  This hook has issue that the text will be split to a large amount of threads
 *  - length_offset: 1
 *  - off: 4294967264 = 0xffffffe0 = -0x20
 *  - type: 8
 *
 *  ハピメア: /HAC@48FF3:cmvs32.exe
 *  base: 0x400000
 *  - length_offset: 1
 *  - off: 12 = 0xc
 *  - type: 68 = 0x44
 *
 *  00448fee     cc             int3
 *  00448fef     cc             int3
 *  00448ff0  /$ 55             push ebp
 *  00448ff1  |. 8bec           mov ebp,esp
 *  00448ff3  |. 83ec 68        sub esp,0x68 ; jichi: hook here, it is actually  tagTEXTMETRICA
 *  00448ff6  |. 8b01           mov eax,dword ptr ds:[ecx]
 *  00448ff8  |. 56             push esi
 *  00448ff9  |. 33f6           xor esi,esi
 *  00448ffb  |. 33d2           xor edx,edx
 *  00448ffd  |. 57             push edi
 *  00448ffe  |. 894d fc        mov dword ptr ss:[ebp-0x4],ecx
 *  00449001  |. 3bc6           cmp eax,esi
 *  00449003  |. 74 37          je short cmvs32.0044903c
 *  00449005  |> 66:8b78 08     /mov di,word ptr ds:[eax+0x8]
 *  00449009  |. 66:3b7d 0c     |cmp di,word ptr ss:[ebp+0xc]
 *  0044900d  |. 75 0a          |jnz short cmvs32.00449019
 *  0044900f  |. 66:8b7d 10     |mov di,word ptr ss:[ebp+0x10]
 *  00449013  |. 66:3978 0a     |cmp word ptr ds:[eax+0xa],di
 *  00449017  |. 74 0a          |je short cmvs32.00449023
 *  00449019  |> 8bd0           |mov edx,eax
 *  0044901b  |. 8b00           |mov eax,dword ptr ds:[eax]
 *  0044901d  |. 3bc6           |cmp eax,esi
 *  0044901f  |.^75 e4          \jnz short cmvs32.00449005
 *  00449021  |. eb 19          jmp short cmvs32.0044903c
 *  00449023  |> 3bd6           cmp edx,esi
 *  00449025  |. 74 0a          je short cmvs32.00449031
 *  00449027  |. 8b38           mov edi,dword ptr ds:[eax]
 *  00449029  |. 893a           mov dword ptr ds:[edx],edi
 *  0044902b  |. 8b11           mov edx,dword ptr ds:[ecx]
 *  0044902d  |. 8910           mov dword ptr ds:[eax],edx
 *  0044902f  |. 8901           mov dword ptr ds:[ecx],eax
 *  00449031  |> 8b40 04        mov eax,dword ptr ds:[eax+0x4]
 *  00449034  |. 3bc6           cmp eax,esi
 *  00449036  |. 0f85 64010000  jnz cmvs32.004491a0
 *  0044903c  |> 8b55 08        mov edx,dword ptr ss:[ebp+0x8]
 *  0044903f  |. 53             push ebx
 *  00449040  |. 0fb75d 0c      movzx ebx,word ptr ss:[ebp+0xc]
 *  00449044  |. b8 00000100    mov eax,0x10000
 *  00449049  |. 8945 e4        mov dword ptr ss:[ebp-0x1c],eax
 *  0044904c  |. 8945 f0        mov dword ptr ss:[ebp-0x10],eax
 *  0044904f  |. 8d45 e4        lea eax,dword ptr ss:[ebp-0x1c]
 *  00449052  |. 50             push eax                                 ; /pMat2
 *  00449053  |. 56             push esi                                 ; |Buffer
 *  00449054  |. 56             push esi                                 ; |BufSize
 *  00449055  |. 8d4d d0        lea ecx,dword ptr ss:[ebp-0x30]          ; |
 *  00449058  |. 51             push ecx                                 ; |pMetrics
 *  00449059  |. 6a 05          push 0x5                                 ; |Format = GGO_GRAY4_BITMAP
 *  0044905b  |. 53             push ebx                                 ; |Char
 *  0044905c  |. 52             push edx                                 ; |hDC
 *  0044905d  |. 8975 e8        mov dword ptr ss:[ebp-0x18],esi          ; |
 *  00449060  |. 8975 ec        mov dword ptr ss:[ebp-0x14],esi          ; |
 *  00449063  |. ff15 5cf05300  call dword ptr ds:[<&gdi32.getglyphoutli>; \GetGlyphOutlineA
 *  00449069  |. 8b75 10        mov esi,dword ptr ss:[ebp+0x10]
 *  0044906c  |. 0faff6         imul esi,esi
 *  0044906f  |. 8bf8           mov edi,eax
 *  00449071  |. 8d04bd 0000000>lea eax,dword ptr ds:[edi*4]
 *  00449078  |. 3bc6           cmp eax,esi
 *  0044907a  |. 76 02          jbe short cmvs32.0044907e
 *  0044907c  |. 8bf0           mov esi,eax
 *  0044907e  |> 56             push esi                                 ; /Size
 *  0044907f  |. 6a 00          push 0x0                                 ; |Flags = LMEM_FIXED
 *  00449081  |. ff15 34f25300  call dword ptr ds:[<&kernel32.localalloc>; \LocalAlloc
 */
bool InsertCMVS2Hook()
{
  // There are multiple functions satisfy the pattern below.
  // Hook to any one of them is OK.
  const BYTE bytes[] = {  // function begin
    0x55,               // 00448ff0  /$ 55             push ebp
    0x8b,0xec,          // 00448ff1  |. 8bec           mov ebp,esp
    0x83,0xec, 0x68,    // 00448ff3  |. 83ec 68        sub esp,0x68 ; jichi: hook here
    0x8b,0x01,          // 00448ff6  |. 8b01           mov eax,dword ptr ds:[ecx]
    0x56,               // 00448ff8  |. 56             push esi
    0x33,0xf6,          // 00448ff9  |. 33f6           xor esi,esi
    0x33,0xd2,          // 00448ffb  |. 33d2           xor edx,edx
    0x57,               // 00448ffd  |. 57             push edi
    0x89,0x4d, 0xfc,    // 00448ffe  |. 894d fc        mov dword ptr ss:[ebp-0x4],ecx
    0x3b,0xc6,          // 00449001  |. 3bc6           cmp eax,esi
    0x74, 0x37          // 00449003  |. 74 37          je short cmvs32.0044903c
  };
  enum { addr_offset = 3 }; // offset from the beginning of the function
  ULONG range = min(module_limit_ - module_base_, MAX_REL_ADDR);
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_base_ + range);
  if (!addr) {
    ConsoleOutput("vnreng:CMVS2: pattern not found");
    return false;
  }

  //reladdr = 0x48ff0;
  //reladdr = 0x48ff3;
  HookParam hp = {};
  hp.address = addr + addr_offset;
  hp.offset = 0xc;
  hp.type = BIG_ENDIAN;
  hp.length_offset = 1;

  ConsoleOutput("vnreng: INSERT CMVS2");
  NewHook(hp, "CMVS2");
  return true;
}

} // unnamed namespace

// jichi 3/7/2014: Insert the old hook first since GetGlyphOutlineA can NOT be found in new games
bool InsertCMVSHook()
{
  // Both CMVS1 and CMVS2 exists in new games.
  // Insert the CMVS2 first. Since CMVS1 could break CMVS2
  // And the CMVS1 games do not have CMVS2 patterns.
  return InsertCMVS2Hook() || InsertCMVS1Hook();
}

namespace { // unnamed rUGP

/********************************************************************************************
rUGP hook:
  Process name is rugp.exe. Used by AGE/GIGA games.

  Font caching issue. Find call to GetGlyphOutlineA and keep stepping out functions.
  After several tries we comes to address in rvmm.dll and everything is catched.
  We see CALL [E*X+0x*] while EBP contains the character data.
  It's not as simple to reverse in rugp at run time as like reallive since rugp dosen't set
  up stack frame. In other words EBP is used for other purpose. We need to find alternative
  approaches.
  The way to the entry of that function gives us clue to find it. There is one CMP EBP,0x8140
  instruction in this function and that's enough! 0x8140 is the start of SHIFT-JIS
  characters. It's determining if ebp contains a SHIFT-JIS character. This function is not likely
  to be used in other ways. We simply search for this instruction and place hook around.
********************************************************************************************/
void SpecialHookRUGP1(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  CC_UNUSED(split);
  DWORD *stack = (DWORD *)esp_base;
  DWORD i, val;
  for (i = 0; i < 4; i++) {
    val = *stack++;
    if ((val >> 16) == 0)
      break;

  }
  if (i < 4) {
    hp->offset = i << 2;
    *data = val;
    *len = 2;
    hp->text_fun = nullptr;
    //hp->type &= ~EXTERN_HOOK;
  }
  else
    *len = 0;
}

// jichi 10/1/2013: Change return type to bool
bool InsertRUGP1Hook()
{
  DWORD low, high;
  if (!IthCheckFile(L"rvmm.dll") || !SafeFillRange(L"rvmm.dll", &low, &high)) {
    ConsoleOutput("vnreng:rUGP: rvmm.dll does not exist");
    return false;
  }
  //WCHAR str[0x40];
  LPVOID ch = (LPVOID)0x8140;
  enum { range = 0x20000 };
  DWORD t = SearchPattern(low + range, high - low - range, &ch, 4) + range;
  BYTE *s = (BYTE *)(low + t);
  //if (t) {
  if (t != range) { // jichi 10/1/2013: Changed to compare with 0x20000
    if (*(s - 2) != 0x81)
      return false;
    if (DWORD i = SafeFindEntryAligned((DWORD)s, 0x200)) {
      HookParam hp = {};
      hp.address = i;
      //hp.offset= -8;
      hp.length_offset = 1;
      hp.text_fun = SpecialHookRUGP1;
      hp.type = BIG_ENDIAN;
      ConsoleOutput("vnreng: INSERT rUGP#1");
      NewHook(hp, "rUGP");
      return true;
    }
  } else {
    t = SearchPattern(low, range, &s, 4);
    if (!t) {
      ConsoleOutput("vnreng:rUGP: pattern not found");
      //ConsoleOutput("Can't find characteristic instruction.");
      return false;
    }

    s = (BYTE *)(low + t);
    for (int i = 0; i < 0x200; i++, s--)
      if (s[0] == 0x90
          && *(DWORD *)(s - 3) == 0x90909090) {
        t = low+ t - i + 1;
        //swprintf(str, L"HookAddr 0x%.8x", t);
        //ConsoleOutput(str);
        HookParam hp = {};
        hp.address = t;
        hp.offset = 0x4;
        hp.length_offset = 1;
        hp.type = BIG_ENDIAN;
        ConsoleOutput("vnreng:INSERT rUGP#2");
        NewHook(hp, "rUGP");
        //RegisterEngineType(ENGINE_RUGP);
        return true;
      }
  }
  ConsoleOutput("vnreng:rUGP: failed");
  return false;
//rt:
  //ConsoleOutput("Unknown rUGP engine.");
}

/** rUGP2 10/11/2014 jichi
 *
 *  Sample game: マブラヴ オルタネイヂ�ヴ ト�タル・イクリプス
 *  The existing rUGP#1/#2 cannot be identified.
 *  H-codes:
 *  - /HAN-4@1E51D:VM60.DLL
 *    - addr: 124189 = 0x1e51d
 *    - length_offset: 1
 *    - module: 3037492083 = 0xb50c7373
 *    - off: 4294967288 = 0xfffffff8 = -8
 *    - type: 1092 = 0x444
 *  - /HAN-4@1001E51D ( alternative)
 *    - addr: 268559645 = 0x1001e51d
 *    - length_offset: 1
 *    - type: 1028 = 0x404
 *
 *  This function is very long.
 *  1001e4b2  ^e9 c0fcffff      jmp _18.1001e177
 *  1001e4b7   8b45 14          mov eax,dword ptr ss:[ebp+0x14]
 *  1001e4ba   c745 08 08000000 mov dword ptr ss:[ebp+0x8],0x8
 *  1001e4c1   85c0             test eax,eax
 *  1001e4c3   74 3c            je short _18.1001e501
 *  1001e4c5   8378 04 00       cmp dword ptr ds:[eax+0x4],0x0
 *  1001e4c9   7f 36            jg short _18.1001e501
 *  1001e4cb   7c 05            jl short _18.1001e4d2
 *  1001e4cd   8338 00          cmp dword ptr ds:[eax],0x0
 *  1001e4d0   73 2f            jnb short _18.1001e501
 *  1001e4d2   8b4d f0          mov ecx,dword ptr ss:[ebp-0x10]
 *  1001e4d5   8b91 38a20000    mov edx,dword ptr ds:[ecx+0xa238]
 *  1001e4db   8910             mov dword ptr ds:[eax],edx
 *  1001e4dd   8b89 3ca20000    mov ecx,dword ptr ds:[ecx+0xa23c]
 *  1001e4e3   8948 04          mov dword ptr ds:[eax+0x4],ecx
 *  1001e4e6   eb 19            jmp short _18.1001e501
 *  1001e4e8   c745 08 09000000 mov dword ptr ss:[ebp+0x8],0x9
 *  1001e4ef   eb 10            jmp short _18.1001e501
 *  1001e4f1   c745 08 16000000 mov dword ptr ss:[ebp+0x8],0x16
 *  1001e4f8   eb 07            jmp short _18.1001e501
 *  1001e4fa   c745 08 1f000000 mov dword ptr ss:[ebp+0x8],0x1f
 *  1001e501   8b45 08          mov eax,dword ptr ss:[ebp+0x8]
 *  1001e504   8ad0             mov dl,al
 *  1001e506   80f2 20          xor dl,0x20
 *  1001e509   80c2 5f          add dl,0x5f
 *  1001e50c   80fa 3b          cmp dl,0x3b
 *  1001e50f   0f87 80010000    ja _18.1001e695
 *  1001e515   0fb60e           movzx ecx,byte ptr ds:[esi]
 *  1001e518   c1e0 08          shl eax,0x8
 *  1001e51b   0bc1             or eax,ecx
 *  1001e51d   b9 01000000      mov ecx,0x1     ; jichi: hook here
 *  1001e522   03f1             add esi,ecx
 *  1001e524   8945 08          mov dword ptr ss:[ebp+0x8],eax
 *  1001e527   8975 0c          mov dword ptr ss:[ebp+0xc],esi
 *  1001e52a   3d 79810000      cmp eax,0x8179
 *  1001e52f   0f85 9d000000    jnz _18.1001e5d2
 *  1001e535   8b4d f0          mov ecx,dword ptr ss:[ebp-0x10]
 *  1001e538   56               push esi
 *  1001e539   8d55 d0          lea edx,dword ptr ss:[ebp-0x30]
 *  1001e53c   52               push edx
 *  1001e53d   e8 0e0bffff      call _18.1000f050
 *  1001e542   8d4d d0          lea ecx,dword ptr ss:[ebp-0x30]
 *  1001e545   c745 fc 07000000 mov dword ptr ss:[ebp-0x4],0x7
 *  1001e54c   ff15 500a0e10    call dword ptr ds:[0x100e0a50]           ; _19.6a712fa9
 *  1001e552   84c0             test al,al
 *  1001e554   75 67            jnz short _18.1001e5bd
 *  1001e556   8b75 f0          mov esi,dword ptr ss:[ebp-0x10]
 *  1001e559   8d45 d0          lea eax,dword ptr ss:[ebp-0x30]
 *  1001e55c   50               push eax
 *  1001e55d   8bce             mov ecx,esi
 *  1001e55f   c745 e4 01000000 mov dword ptr ss:[ebp-0x1c],0x1
 *  1001e566   c745 e0 00000000 mov dword ptr ss:[ebp-0x20],0x0
 *  1001e56d   e8 5e80ffff      call _18.100165d0
 *  1001e572   0fb7f8           movzx edi,ax
 *  1001e575   57               push edi
 *  1001e576   8bce             mov ecx,esi
 *  1001e578   e8 c380ffff      call _18.10016640
 *  1001e57d   85c0             test eax,eax
 *  1001e57f   74 0d            je short _18.1001e58e
 *  1001e581   f640 38 02       test byte ptr ds:[eax+0x38],0x2
 *  1001e585   74 07            je short _18.1001e58e
 *  1001e587   c745 e0 01000000 mov dword ptr ss:[ebp-0x20],0x1
 *  1001e58e   837d bc 10       cmp dword ptr ss:[ebp-0x44],0x10
 *  1001e592   74 29            je short _18.1001e5bd
 *  1001e594   8b43 28          mov eax,dword ptr ds:[ebx+0x28]
 *  1001e597   85c0             test eax,eax
 */
bool InsertRUGP2Hook()
{
  DWORD low, high;
  if (!IthCheckFile(L"vm60.dll") || !SafeFillRange(L"vm60.dll", &low, &high)) {
    ConsoleOutput("vnreng:rUGP2: vm60.dll does not exist");
    return false;
  }
  const BYTE bytes[] = {
    0x0f,0xb6,0x0e,             // 1001e515   0fb60e           movzx ecx,byte ptr ds:[esi]
    0xc1,0xe0, 0x08,            // 1001e518   c1e0 08          shl eax,0x8
    0x0b,0xc1,                  // 1001e51b   0bc1             or eax,ecx
    0xb9, 0x01,0x00,0x00,0x00,  // 1001e51d   b9 01000000      mov ecx,0x1     ; jichi: hook here
    0x03,0xf1,                  // 1001e522   03f1             add esi,ecx
    0x89,0x45, 0x08,            // 1001e524   8945 08          mov dword ptr ss:[ebp+0x8],eax
    0x89,0x75, 0x0c             // 1001e527   8975 0c          mov dword ptr ss:[ebp+0xc],esi
  };
  enum { addr_offset = 0x1001e51d - 0x1001e515 };
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), low, high);
  //GROWL_DWORD(addr);
  if (!addr) {
    ConsoleOutput("vnreng:rUGP2: pattern not found");
    return false;
  }

  HookParam hp = {};
  hp.address = addr + addr_offset;
  hp.length_offset = 1;
  hp.offset = -8;
  hp.type = NO_CONTEXT|BIG_ENDIAN;
  ConsoleOutput("vnreng: INSERT rUGP2");
  NewHook(hp, "rUGP2");
  return true;
}

} // unnamed namespace

bool InsertRUGPHook()
{ return InsertRUGP1Hook() || InsertRUGP2Hook(); }

/********************************************************************************************
Lucifen hook:
  Game folder contains *.lpk. Used by Navel games.
  Hook is same to GetTextExtentPoint32A, use ESP to split name.
********************************************************************************************/
void InsertLucifenHook()
{
  // BOOL GetTextExtentPoint32(
  //   _In_   HDC hdc,
  //   _In_   LPCTSTR lpString,
  //   _In_   int c,
  //   _Out_  LPSIZE lpSize
  // );
  HookParam hp = {};
  hp.address = (DWORD)::GetTextExtentPoint32A;
  hp.offset = 0x8; // arg2 lpString
  hp.split = -0x18; // jichi 8/12/2014: = -4 - pusha_esp_off
  hp.length_offset = 3;
  hp.type = USING_STRING|USING_SPLIT;
  ConsoleOutput("vnreng: INSERT Lucifen");
  NewHook(hp, "Lucifen");
  //RegisterEngineType(ENGINE_LUCIFEN);
}
/********************************************************************************************
System40 hook:
  System40 is a game engine developed by Alicesoft.
  Afaik, there are 2 very different types of System40. Each requires a particular hook.

  Pattern 1: Either SACTDX.dll or SACT2.dll exports SP_TextDraw.
  The first relative call in this function draw text to some surface.
  Text pointer is return by last absolute indirect call before that.
  Split parameter is a little tricky. The first register pushed onto stack at the begining
  usually is used as font size later. According to instruction opcode map, push
  eax -- 50, ecx -- 51, edx -- 52, ebx --53, esp -- 54, ebp -- 55, esi -- 56, edi -- 57
  Split parameter value:
  eax - -8,   ecx - -C,  edx - -10, ebx - -14, esp - -18, ebp - -1C, esi - -20, edi - -24
  Just extract the low 4 bit and shift left 2 bit, then minus by -8,
  will give us the split parameter. e.g. push ebx 53->3 *4->C, -8-C=-14.
  Sometimes if split function is enabled, ITH will split text spoke by different
  character into different thread. Just open hook dialog and uncheck split parameter.
  Then click modify hook.

  Pattern 2: *engine.dll exports SP_SetTextSprite.
  At the entry point, EAX should be a pointer to some structure, character at +0x8.
  Before calling this function, the caller put EAX onto stack, we can also find this
  value on stack. But seems parameter order varies from game release. If a future
  game breaks the EAX rule then we need to disassemble the caller code to determine
  data offset dynamically.
********************************************************************************************/

static void InsertAliceHook1(DWORD addr, DWORD module, DWORD limit)
{
  if (!addr) {
    ConsoleOutput("vnreng:AliceHook1: failed");
    return;
  }
  for (DWORD i = addr, s = addr; i < s + 0x100; i++)
    if (*(BYTE *)i == 0xe8) { // Find the first relative call.
      DWORD j = i + 5 + *(DWORD *)(i + 1);
      if (j > module && j < limit) {
        while (true) { // Find the first register push onto stack.
          DWORD c = ::disasm((BYTE *)s);
          if (c == 1)
            break;
          s += c;
        }
        DWORD c = *(BYTE *)s;
        HookParam hp = {};
        hp.address = j;
        hp.offset = -0x8;
        hp.split = -8 -((c & 0xf) << 2);
        hp.type = USING_STRING|USING_SPLIT;
        //if (s>j) hp.type^=USING_SPLIT;
        ConsoleOutput("vnreng: INSERT AliceHook1");
        NewHook(hp, "System40");
        //RegisterEngineType(ENGINE_SYS40);
        return;
      }
    }
  ConsoleOutput("vnreng:AliceHook1: failed");
}
static void InsertAliceHook2(DWORD addr)
{
  if (!addr) {
    ConsoleOutput("vnreng:AliceHook2: failed");
    return;
  }
  HookParam hp = {};
  hp.address = addr;
  hp.offset = -0x8;
  hp.index = 0x8;
  hp.length_offset = 1;
  hp.type = DATA_INDIRECT;
  ConsoleOutput("vnreng: INSERT AliceHook2");
  NewHook(hp, "System40");
  //RegisterEngineType(ENGINE_SYS40);
}

// jichi 8/23/2013 Move here from engine.cc
// Do not work for the latest Alice games
// jichi 5/13/2015: Looking for function entries in StoatSpriteEngine.dll
bool InsertAliceHook()
{
  DWORD low, high, addr;
  if (GetFunctionAddr("SP_TextDraw", &addr, &low, &high, 0) && addr) {
    InsertAliceHook1(addr, low, low + high);
    return true;
  }
  if (GetFunctionAddr("SP_SetTextSprite", &addr, &low, &high, 0) && addr) {
    InsertAliceHook2(addr);
    return true;
  }
  ConsoleOutput("vnreng:AliceHook: failed");
  return false;
}

/**
 *  jichi 12/26/2013: Rance hook
 *
 *  ランス01 光をもとめて: /HSN4:-14@5506A9
 *  - addr: 5572265 (0x5596a9)
 *  - off: 4
 *  - split: 4294967272 (0xffffffe8 = -0x18)
 *  - type: 1041 (0x411)
 *
 *    the above code has the same pattern except int3.
 *    005506a9  |. e8 f2fb1600    call Rance01.006c02a0 ; hook here
 *    005506ae  |. 83c4 0c        add esp,0xc
 *    005506b1  |. 5f             pop edi
 *    005506b2  |. 5e             pop esi
 *    005506b3  |. b0 01          mov al,0x1
 *    005506b5  |. 5b             pop ebx
 *    005506b6  \. c2 0400        retn 0x4
 *    005506b9     cc             int3
 *
 *  ランス・クエス� /hsn4:-14@42e08a
 *    0042e08a  |. e8 91ed1f00    call Ranceque.0062ce20 ; hook here
 *    0042e08f  |. 83c4 0c        add esp,0xc
 *    0042e092  |. 5f             pop edi
 *    0042e093  |. 5e             pop esi
 *    0042e094  |. b0 01          mov al,0x1
 *    0042e096  |. 5b             pop ebx
 *    0042e097  \. c2 0400        retn 0x4
 *    0042e09a     cc             int3
 *
 *  5/7/2015  イブニクル version 1.0.1
 *  The hooked function is no longer get called after loading AliceRunPatch.dll.
 *  The hooked function is below.
 *  See also ATcode: http://capita.tistory.com/m/post/256
 *    005C40AE   CC               INT3
 *    005C40AF   CC               INT3
 *    005C40B0   53               PUSH EBX
 *    005C40B1   8B5C24 08        MOV EBX,DWORD PTR SS:[ESP+0x8]
 *    005C40B5   56               PUSH ESI
 *    005C40B6   57               PUSH EDI
 *    005C40B7   8B7B 10          MOV EDI,DWORD PTR DS:[EBX+0x10]
 *    005C40BA   8BF0             MOV ESI,EAX
 *    005C40BC   47               INC EDI
 *    005C40BD   3B7E 0C          CMP EDI,DWORD PTR DS:[ESI+0xC]
 *    005C40C0   76 0F            JBE SHORT .005C40D1
 *    005C40C2   E8 79F8FFFF      CALL .005C3940
 *    005C40C7   84C0             TEST AL,AL
 *    005C40C9   75 06            JNZ SHORT .005C40D1
 *    005C40CB   5F               POP EDI
 *    005C40CC   5E               POP ESI
 *    005C40CD   5B               POP EBX
 *    005C40CE   C2 0400          RETN 0x4
 *    005C40D1   837B 14 10       CMP DWORD PTR DS:[EBX+0x14],0x10
 *    005C40D5   72 02            JB SHORT .005C40D9
 *    005C40D7   8B1B             MOV EBX,DWORD PTR DS:[EBX]
 *    005C40D9   837E 0C 00       CMP DWORD PTR DS:[ESI+0xC],0x0
 *    005C40DD   75 15            JNZ SHORT .005C40F4
 *    005C40DF   57               PUSH EDI
 *    005C40E0   33C0             XOR EAX,EAX
 *    005C40E2   53               PUSH EBX
 *    005C40E3   50               PUSH EAX
 *    005C40E4   E8 B7400D00      CALL .006981A0
 *    005C40E9   83C4 0C          ADD ESP,0xC
 *    005C40EC   5F               POP EDI
 *    005C40ED   5E               POP ESI
 *    005C40EE   B0 01            MOV AL,0x1
 *    005C40F0   5B               POP EBX
 *    005C40F1   C2 0400          RETN 0x4
 *    005C40F4   8B46 08          MOV EAX,DWORD PTR DS:[ESI+0x8]
 *    005C40F7   57               PUSH EDI
 *    005C40F8   53               PUSH EBX
 *    005C40F9   50               PUSH EAX
 *    005C40FA   E8 A1400D00      CALL .006981A0 ; jichi: call here
 *    005C40FF   83C4 0C          ADD ESP,0xC
 *    005C4102   5F               POP EDI
 *    005C4103   5E               POP ESI
 *    005C4104   B0 01            MOV AL,0x1
 *    005C4106   5B               POP EBX
 *    005C4107   C2 0400          RETN 0x4
 *    005C410A   CC               INT3
 *    005C410B   CC               INT3
 *    005C410C   CC               INT3 *
 */
static bool InsertSystem43OldHook(ULONG startAddress, ULONG stopAddress, LPCSTR hookName)
{
  // i.e. 83c40c5f5eb0015bc20400cccc without leading 0xe8
  //const BYTE ins[] = {  //   005506a9  |. e8 f2fb1600    call rance01.006c02a0 ; hook here
  //  0x83,0xc4, 0x0c,    //   005506ae  |. 83c4 0c        add esp,0xc
  //  0x5f,               //   005506b1  |. 5f             pop edi
  //  0x5e,               //   005506b2  |. 5e             pop esi
  //  0xb0, 0x01,         //   005506b3  |. b0 01          mov al,0x1
  //  0x5b,               //   005506b5  |. 5b             pop ebx
  //  0xc2, 0x04,0x00,    //   005506b6  \. c2 0400        retn 0x4
  //  0xcc, 0xcc // patching a few int3 to make sure that this is at the end of the code block
  //};
  //enum { addr_offset = -5 }; // the function call before the ins
  //ULONG addr = module_base_; //- sizeof(ins);
  ////addr = 0x5506a9;
  //enum { near_call = 0xe8 }; // intra-module function call
  //do {
  //  //addr += sizeof(ins); // so that each time return diff address -- not needed
  //  ULONG range = min(module_limit_ - addr, MAX_REL_ADDR);
  //  addr = MemDbg::findBytes(ins, sizeof(ins), addr, addr + range);
  //  if (!addr) {
  //    //ITH_MSG(L"failed");
  //    ConsoleOutput("vnreng:System43: pattern not found");
  //    return false;
  //  }
  //  addr += addr_offset;
  //} while(near_call != *(BYTE *)addr); // function call
  //GROWL_DWORD(addr);

  // i.e. 83c40c5f5eb0015bc20400cccc without leading 0xe8
  const BYTE bytes[] = {
    0xe8, XX4,          //   005506a9  |. e8 f2fb1600    call rance01.006c02a0 ; hook here
    0x83,0xc4, 0x0c,    //   005506ae  |. 83c4 0c        add esp,0xc
    0x5f,               //   005506b1  |. 5f             pop edi
    0x5e,               //   005506b2  |. 5e             pop esi
    0xb0, 0x01,         //   005506b3  |. b0 01          mov al,0x1
    0x5b,               //   005506b5  |. 5b             pop ebx
    0xc2, 0x04,0x00,    //   005506b6  \. c2 0400        retn 0x4
    0xcc, 0xcc // patching a few int3 to make sure that this is at the end of the code block
  };
  enum { addr_offset = 0 };
  ULONG addr = MemDbg::matchBytes(bytes, sizeof(bytes), startAddress, stopAddress);
  //GROWL_DWORD(addr);
  if (!addr) {
    ConsoleOutput("vnreng:System43: pattern not found");
    return false;
  }

  HookParam hp = {};
  hp.address = addr + addr_offset;
  hp.offset = 4;
  hp.split = -0x18;
  hp.type = NO_CONTEXT|USING_SPLIT|USING_STRING;
  ConsoleOutput("vnreng: INSERT System43");
  NewHook(hp, hookName);

  ConsoleOutput("vnreng:System43: disable GDI hooks"); // disable hooking to TextOutA, which is cached
  DisableGDIHooks();
  return true;
}

/** 5/13/2015 Add new hook for System43 engine that has no garbage threads and can detect character name
 *  Sample game: Evenicle
 *  See: http://capita.tistory.com/m/post/256
 *
 *  004EEA6C   CC               INT3
 *  004EEA6D   CC               INT3
 *  004EEA6E   CC               INT3
 *  004EEA6F   CC               INT3
 *  004EEA70   6A FF            PUSH -0x1
 *  004EEA72   68 E8267000      PUSH .007026E8
 *  004EEA77   64:A1 00000000   MOV EAX,DWORD PTR FS:[0]
 *  004EEA7D   50               PUSH EAX
 *  004EEA7E   83EC 20          SUB ESP,0x20
 *  004EEA81   A1 DCC47700      MOV EAX,DWORD PTR DS:[0x77C4DC]
 *  004EEA86   33C4             XOR EAX,ESP
 *  004EEA88   894424 1C        MOV DWORD PTR SS:[ESP+0x1C],EAX
 *  004EEA8C   53               PUSH EBX
 *  004EEA8D   55               PUSH EBP
 *  004EEA8E   56               PUSH ESI
 *  004EEA8F   57               PUSH EDI
 *  004EEA90   A1 DCC47700      MOV EAX,DWORD PTR DS:[0x77C4DC]
 *  004EEA95   33C4             XOR EAX,ESP
 *  004EEA97   50               PUSH EAX
 *  004EEA98   8D4424 34        LEA EAX,DWORD PTR SS:[ESP+0x34]
 *  004EEA9C   64:A3 00000000   MOV DWORD PTR FS:[0],EAX
 *  004EEAA2   8B4424 44        MOV EAX,DWORD PTR SS:[ESP+0x44]
 *  004EEAA6   8BF1             MOV ESI,ECX
 *  004EEAA8   E8 8346FBFF      CALL .004A3130
 *  004EEAAD   8BE8             MOV EBP,EAX
 *  004EEAAF   33DB             XOR EBX,EBX
 *  004EEAB1   3BEB             CMP EBP,EBX
 *  004EEAB3   75 07            JNZ SHORT .004EEABC
 *  004EEAB5   32C0             XOR AL,AL
 *  004EEAB7   E9 92000000      JMP .004EEB4E
 *  004EEABC   8B06             MOV EAX,DWORD PTR DS:[ESI]
 *  004EEABE   8B10             MOV EDX,DWORD PTR DS:[EAX]
 *  004EEAC0   8BCE             MOV ECX,ESI
 *  004EEAC2   FFD2             CALL EDX
 *  004EEAC4   8BC8             MOV ECX,EAX
 *  004EEAC6   C74424 28 0F0000>MOV DWORD PTR SS:[ESP+0x28],0xF
 *  004EEACE   895C24 24        MOV DWORD PTR SS:[ESP+0x24],EBX
 *  004EEAD2   885C24 14        MOV BYTE PTR SS:[ESP+0x14],BL
 *  004EEAD6   8D71 01          LEA ESI,DWORD PTR DS:[ECX+0x1]
 *  004EEAD9   8DA424 00000000  LEA ESP,DWORD PTR SS:[ESP]
 *  004EEAE0   8A11             MOV DL,BYTE PTR DS:[ECX]
 *  004EEAE2   41               INC ECX
 *  004EEAE3   3AD3             CMP DL,BL
 *  004EEAE5  ^75 F9            JNZ SHORT .004EEAE0
 *  004EEAE7   2BCE             SUB ECX,ESI
 *  004EEAE9   50               PUSH EAX
 *  004EEAEA   8BF9             MOV EDI,ECX
 *  004EEAEC   8D7424 18        LEA ESI,DWORD PTR SS:[ESP+0x18]
 *  004EEAF0   E8 CB27F1FF      CALL .004012C0
 *  004EEAF5   8B7C24 48        MOV EDI,DWORD PTR SS:[ESP+0x48]
 *  004EEAF9   895C24 3C        MOV DWORD PTR SS:[ESP+0x3C],EBX
 *  004EEAFD   8B75 3C          MOV ESI,DWORD PTR SS:[EBP+0x3C]
 *  004EEB00   E8 1B4A0100      CALL .00503520
 *  004EEB05   8BF8             MOV EDI,EAX
 *  004EEB07   8DB7 E4000000    LEA ESI,DWORD PTR DS:[EDI+0xE4]
 *  004EEB0D   8D4424 14        LEA EAX,DWORD PTR SS:[ESP+0x14]
 *  004EEB11   8BD6             MOV EDX,ESI
 *  004EEB13   E8 985CF1FF      CALL .004047B0
 *  004EEB18   BD 10000000      MOV EBP,0x10
 *  004EEB1D   84C0             TEST AL,AL
 *  004EEB1F   75 18            JNZ SHORT .004EEB39
 *  004EEB21   895E 10          MOV DWORD PTR DS:[ESI+0x10],EBX
 *  004EEB24   396E 14          CMP DWORD PTR DS:[ESI+0x14],EBP
 *  004EEB27   72 02            JB SHORT .004EEB2B
 *  004EEB29   8B36             MOV ESI,DWORD PTR DS:[ESI]
 *  004EEB2B   8D4424 14        LEA EAX,DWORD PTR SS:[ESP+0x14]
 *  004EEB2F   50               PUSH EAX
 *  004EEB30   8BCF             MOV ECX,EDI
 *  004EEB32   881E             MOV BYTE PTR DS:[ESI],BL
 *  004EEB34   E8 67CB0100      CALL .0050B6A0  ; jichi: ATcode modified here, text is on the top of the stack
 *  004EEB39   396C24 28        CMP DWORD PTR SS:[ESP+0x28],EBP
 *  004EEB3D   72 0D            JB SHORT .004EEB4C
 *  004EEB3F   8B4C24 14        MOV ECX,DWORD PTR SS:[ESP+0x14]
 *  004EEB43   51               PUSH ECX
 *  004EEB44   E8 42DC1900      CALL .0068C78B
 *  004EEB49   83C4 04          ADD ESP,0x4
 *  004EEB4C   B0 01            MOV AL,0x1
 *  004EEB4E   8B4C24 34        MOV ECX,DWORD PTR SS:[ESP+0x34]
 *  004EEB52   64:890D 00000000 MOV DWORD PTR FS:[0],ECX
 *  004EEB59   59               POP ECX
 *  004EEB5A   5F               POP EDI
 *  004EEB5B   5E               POP ESI
 *  004EEB5C   5D               POP EBP
 *  004EEB5D   5B               POP EBX
 *  004EEB5E   8B4C24 1C        MOV ECX,DWORD PTR SS:[ESP+0x1C]
 *  004EEB62   33CC             XOR ECX,ESP
 *  004EEB64   E8 9CD61900      CALL .0068C205
 *  004EEB69   83C4 2C          ADD ESP,0x2C
 *  004EEB6C   C3               RETN
 *  004EEB6D   CC               INT3
 *  004EEB6E   CC               INT3
 *
 *  Actual binary patch for Evenicle exe: http://capita.tistory.com/m/post/256
 *  {005E393B(EB), 004EEB34(E9 13 B6 21 00), 005C71E0(E9 48 2F 14 00), 005B6494(E9 10 3D 15 00), 0070A10F(90 90 90 90 90 E8 F7 9F EB FF E9 C7 D0 EB FF 90 90 90 90 90 E8 78 15 E0 FF E9 0C 4A DE FF 50 8B 87 B0 00 00 00 66 81 38 84 00 75 0E 83 78 EA 5B 75 08 E8 A2 00 00 00 58 EB C6 58 EB C8 50 52 BA E0 0B 7A 00 60 89 D7 8B 74 E4 28 B9 06 00 00 00 F3 A5 61 8B 44 E4 08 8B 40 10 85 C0 74 29 8B 44 E4 08 8B 40 14 83 F8 0F 75 08 89 54 E4 08 5A 58 EB 9D 8D 42 20 60 89 C7 8B 32 8B 4A 14 83 C1 09 F3 A4 61 89 02 EB E3 5A 58 EB 89 90 90 90 90 90 E8 6C 9F EB FF E9 F0 C2 EA FF 50 8B 44 E4 04 83 78 0C 01 76 31 8B 87 84 02 00 00 66 83 78 FC 46 75 24 83 78 F8 22 74 16 83 78 F8 13 75 18 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 E8 06 00 00 00 58 EB B5 58 EB B7 60 8B 74 E4 28 BF E0 0B 7A 00 89 7C E4 28 B9 0C 00 00 00 F3 A5 61 C3)}
 *
 *  ATcode: FORCEFONT(5),ENCODEKOR,FONT(Malgun Gothic,-13),HOOK(0x0070A10F,TRANS([[ESP]+0x8],LEN([ESP]+0XC),PTRCHEAT),RETNPOS(COPY)),HOOK(0x0070A11E,TRANS([ESP],SMSTR(IGNORE)),RETNPOS(COPY)),HOOK(0x0070A19A,TRANS([[ESP]+0x8],LEN([ESP]+0XC),PTRCHEAT),RETNPOS(COPY))
 *  FilterCode: DenyWord{CUT(2)},FixLine{},KoFilter{},DumpText{},CustomDic{CDic},CustomScript{Write,Pass(-1),Cache}
 *
 *  The second hooked address pointed to the text address.
 *  The logic here is simplify buffer the read text, and replace the text by zero
 *  Then translate/paint them together.
 *  Several variables near the text address is used to check if the text is finished or not.
 *
 *  Function immediately before patched code:
 *  0070A09E   CC               INT3
 *  0070A09F   CC               INT3
 *  0070A0A0   6A FF            PUSH -0x1
 *  0070A0A2   68 358A7000      PUSH .00708A35
 *  0070A0A7   64:A1 00000000   MOV EAX,DWORD PTR FS:[0]
 *  0070A0AD   50               PUSH EAX
 *  0070A0AE   51               PUSH ECX
 *  0070A0AF   56               PUSH ESI
 *  0070A0B0   A1 DCC47700      MOV EAX,DWORD PTR DS:[0x77C4DC]
 *  0070A0B5   33C4             XOR EAX,ESP
 *  0070A0B7   50               PUSH EAX
 *  0070A0B8   8D4424 0C        LEA EAX,DWORD PTR SS:[ESP+0xC]
 *  0070A0BC   64:A3 00000000   MOV DWORD PTR FS:[0],EAX
 *  0070A0C2   C74424 14 000000>MOV DWORD PTR SS:[ESP+0x14],0x0
 *  0070A0CA   A1 54D17900      MOV EAX,DWORD PTR DS:[0x79D154]
 *  0070A0CF   8B08             MOV ECX,DWORD PTR DS:[EAX]
 *  0070A0D1   50               PUSH EAX
 *  0070A0D2   51               PUSH ECX
 *  0070A0D3   8D7424 10        LEA ESI,DWORD PTR SS:[ESP+0x10]
 *  0070A0D7   E8 6416F8FF      CALL .0068B740
 *  0070A0DC   A1 54D17900      MOV EAX,DWORD PTR DS:[0x79D154]
 *  0070A0E1   50               PUSH EAX
 *  0070A0E2   E8 A426F8FF      CALL .0068C78B
 *  0070A0E7   83C4 04          ADD ESP,0x4
 *  0070A0EA   8B4C24 0C        MOV ECX,DWORD PTR SS:[ESP+0xC]
 *  0070A0EE   64:890D 00000000 MOV DWORD PTR FS:[0],ECX
 *  0070A0F5   59               POP ECX
 *  0070A0F6   5E               POP ESI
 *  0070A0F7   83C4 10          ADD ESP,0x10
 *  0070A0FA   C3               RETN
 *  0070A0FB   C705 C4C17900 64>MOV DWORD PTR DS:[0x79C1C4],.0070B664
 *  0070A105   B9 C4C17900      MOV ECX,.0079C1C4
 *  0070A10A  ^E9 0722F8FF      JMP .0068C316
 *
 *  Patched code:
 *  0070A10F   90               NOP ; jichi: ATcode hooked here
 *  0070A110   90               NOP
 *  0070A111   90               NOP
 *  0070A112   90               NOP
 *  0070A113   90               NOP
 *  0070A114   E8 F79FEBFF      CALL .005C4110
 *  0070A119  ^E9 C7D0EBFF      JMP .005C71E5
 *  0070A11E   90               NOP
 *  0070A11F   90               NOP
 *  0070A120   90               NOP
 *  0070A121   90               NOP
 *  0070A122   90               NOP
 *  0070A123   E8 7815E0FF      CALL .0050B6A0                  ; jichi: call the original function for hookpoint #2
 *  0070A128  ^E9 0C4ADEFF      JMP .004EEB39                   ; jichi: come back to hookpoint#2
 *  0070A12D   50               PUSH EAX    ; jichi: this is for hookpoint #3, translate the text before send it to paint
 *  0070A12E   8B87 B0000000    MOV EAX,DWORD PTR DS:[EDI+0xB0]
 *  0070A134   66:8138 8400     CMP WORD PTR DS:[EAX],0x84
 *  0070A139   75 0E            JNZ SHORT .0070A149
 *  0070A13B   8378 EA 5B       CMP DWORD PTR DS:[EAX-0x16],0x5B
 *  0070A13F   75 08            JNZ SHORT .0070A149
 *  0070A141   E8 A2000000      CALL .0070A1E8
 *  0070A146   58               POP EAX
 *  0070A147  ^EB C6            JMP SHORT .0070A10F
 *  0070A149   58               POP EAX
 *  0070A14A  ^EB C8            JMP SHORT .0070A114
 *  0070A14C   50               PUSH EAX                        ; jichi: hookpoint#2 jmp here, text address is in [esp]
 *  0070A14D   52               PUSH EDX
 *  0070A14E   BA E00B7A00      MOV EDX,.007A0BE0               ; jichi: 007A0BE0 points to unused zeroed memory
 *  0070A153   60               PUSHAD                          ; jichi esp -= 0x20, now, esp[0x28] is text address, esp[0x24] = eax, and esp[0x20] = edx
 *  0070A154   89D7             MOV EDI,EDX                     ; set 007A0BE0 as the target buffer to save text, edx is never modified
 *  0070A156   8B74E4 28        MOV ESI,DWORD PTR SS:[ESP+0x28] ; set source text as target
 *  0070A15A   B9 06000000      MOV ECX,0x6                     ; move for 6 bytes
 *  0070A15F   F3:A5            REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
 *  0070A161   61               POPAD   ; finished saving text, now [esp] is old edx, esp[0x4] is old eax, esp[0x8] is old text address
 *  0070A162   8B44E4 08        MOV EAX,DWORD PTR SS:[ESP+0x8]  ; eax = original text address
 *  0070A166   8B40 10          MOV EAX,DWORD PTR DS:[EAX+0x10] ; eax = text[0x10]
 *  0070A169   85C0             TEST EAX,EAX                    ; if end of text,
 *  0070A16B   74 29            JE SHORT .0070A196              ; jump if eax is zero, comeback to hookpoint and ignore it
 *  0070A16D   8B44E4 08        MOV EAX,DWORD PTR SS:[ESP+0x8]  ; otherwise, if eax is not zero
 *  0070A171   8B40 14          MOV EAX,DWORD PTR DS:[EAX+0x14] ; eax = text[0x14]
 *  0070A174   83F8 0F          CMP EAX,0xF                     ; jichi: compare text[0x14] with 0xf
 *  0070A177   75 08            JNZ SHORT .0070A181             ; jump if not zero leaving text not modified, other continue and modify the text
 *  0070A179   8954E4 08        MOV DWORD PTR SS:[ESP+0x8],EDX  ; override esp+8 with edx, i.e. override text address by new text address and do translation
 *  0070A17D   5A               POP EDX
 *  0070A17E   58               POP EAX                         ; jichi: restore edx and eax, now esp is back to normal. [esp] is the new text address
 *  0070A17F  ^EB 9D            JMP SHORT .0070A11E             ; jichi: jump to the top of the hooked place (nop) and do translation before coming back
 *  0070A181   8D42 20          LEA EAX,DWORD PTR DS:[EDX+0x20] ; text is not modified, esp[0x8] is the text address, edx is the modified buffer, eax = buffer[0x20] address
 *  0070A184   60               PUSHAD                          ; jichi: esp[0x28] is now the text address
 *  0070A185   89C7             MOV EDI,EAX                     ; jichi: edx[0x20] is the target
 *  0070A187   8B32             MOV ESI,DWORD PTR DS:[EDX]      ; jichi: edx is the source
 *  0070A189   8B4A 14          MOV ECX,DWORD PTR DS:[EDX+0x14]
 *  0070A18C   83C1 09          ADD ECX,0x9                     ; move for [edx+0x14]+0x9 time
 *  0070A18F   F3:A4            REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]    ; jichi: shift text by 0x14 dword ptr
 *  0070A191   61               POPAD                           ; jichi: now esp[0x8] is the text address
 *  0070A192   8902             MOV DWORD PTR DS:[EDX],EAX      ; eax is the new text address (edx+0x20), move the address to beginning of buffer ([edx]), i.e. edx is pointed to zero memory now
 *  0070A194  ^EB E3            JMP SHORT .0070A179             ; come bback to modify the text address
 *  0070A196   5A               POP EDX
 *  0070A197   58               POP EAX
 *  0070A198  ^EB 89            JMP SHORT .0070A123             ; jichi: come back to call
 *  0070A19A   90               NOP
 *  0070A19B   90               NOP
 *  0070A19C   90               NOP
 *  0070A19D   90               NOP
 *  0070A19E   90               NOP
 *  0070A19F   E8 6C9FEBFF      CALL .005C4110
 *  0070A1A4  ^E9 F0C2EAFF      JMP .005B6499
 *  0070A1A9   50               PUSH EAX                        ; jichi: from hookpoint #4
 *  0070A1AA   8B44E4 04        MOV EAX,DWORD PTR SS:[ESP+0x4]  ; jichi: move top of the old stack address to eax
 *  0070A1AE   8378 0C 01       CMP DWORD PTR DS:[EAX+0xC],0x1
 *  0070A1B2   76 31            JBE SHORT .0070A1E5             ; jichi: jump to leave if text[0xc] <= 0x1
 *  0070A1B4   8B87 84020000    MOV EAX,DWORD PTR DS:[EDI+0x284]
 *  0070A1BA   66:8378 FC 46    CMP WORD PTR DS:[EAX-0x4],0x46
 *  0070A1BF   75 24            JNZ SHORT .0070A1E5
 *  0070A1C1   8378 F8 22       CMP DWORD PTR DS:[EAX-0x8],0x22
 *  0070A1C5   74 16            JE SHORT .0070A1DD
 *  0070A1C7   8378 F8 13       CMP DWORD PTR DS:[EAX-0x8],0x13
 *  0070A1CB   75 18            JNZ SHORT .0070A1E5
 *  0070A1CD   90               NOP
 *  0070A1CE   90               NOP
 *  0070A1CF   90               NOP
 *  0070A1D0   90               NOP
 *  0070A1D1   90               NOP
 *  0070A1D2   90               NOP
 *  0070A1D3   90               NOP
 *  0070A1D4   90               NOP
 *  0070A1D5   90               NOP
 *  0070A1D6   90               NOP
 *  0070A1D7   90               NOP
 *  0070A1D8   90               NOP
 *  0070A1D9   90               NOP
 *  0070A1DA   90               NOP
 *  0070A1DB   90               NOP
 *  0070A1DC   90               NOP
 *  0070A1DD   E8 06000000      CALL .0070A1E8
 *  0070A1E2   58               POP EAX
 *  0070A1E3  ^EB B5            JMP SHORT .0070A19A
 *  0070A1E5   58               POP EAX
 *  0070A1E6  ^EB B7            JMP SHORT .0070A19F
 *  0070A1E8   60               PUSHAD
 *  0070A1E9   8B74E4 28        MOV ESI,DWORD PTR SS:[ESP+0x28]
 *  0070A1ED   BF E00B7A00      MOV EDI,.007A0BE0
 *  0070A1F2   897CE4 28        MOV DWORD PTR SS:[ESP+0x28],EDI
 *  0070A1F6   B9 0C000000      MOV ECX,0xC
 *  0070A1FB   F3:A5            REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
 *  0070A1FD   61               POPAD
 *  0070A1FE   C3               RETN
 *  0070A1FF   0000             ADD BYTE PTR DS:[EAX],AL
 *  0070A201   0000             ADD BYTE PTR DS:[EAX],AL
 *  0070A203   0000             ADD BYTE PTR DS:[EAX],AL
 *
 *  Modified places:
 *
 *  005E391C   CC               INT3
 *  005E391D   CC               INT3
 *  005E391E   CC               INT3
 *  005E391F   CC               INT3
 *  005E3920   55               PUSH EBP
 *  005E3921   8BEC             MOV EBP,ESP
 *  005E3923   83E4 C0          AND ESP,0xFFFFFFC0
 *  005E3926   83EC 34          SUB ESP,0x34
 *  005E3929   53               PUSH EBX
 *  005E392A   8B5D 08          MOV EBX,DWORD PTR SS:[EBP+0x8]
 *  005E392D   817B 04 00010000 CMP DWORD PTR DS:[EBX+0x4],0x100
 *  005E3934   56               PUSH ESI
 *  005E3935   57               PUSH EDI
 *  005E3936   8B7D 0C          MOV EDI,DWORD PTR SS:[EBP+0xC]
 *  005E3939   8BF0             MOV ESI,EAX
 *  005E393B   EB 67            JMP SHORT .005E39A4 ; jichi: here modified point#1, change to always jump to 5e39a4, when enabled it will change font size
 *  005E393D   8D4424 28        LEA EAX,DWORD PTR SS:[ESP+0x28]
 *  005E3941   50               PUSH EAX
 *  005E3942   8D4C24 30        LEA ECX,DWORD PTR SS:[ESP+0x30]
 *
 *  004EEA6E   CC               INT3
 *  004EEA6F   CC               INT3
 *  004EEA70   6A FF            PUSH -0x1
 *  004EEA72   68 E8267000      PUSH .007026E8
 *  004EEA77   64:A1 00000000   MOV EAX,DWORD PTR FS:[0]
 *  004EEA7D   50               PUSH EAX
 *  004EEA7E   83EC 20          SUB ESP,0x20
 *  004EEA81   A1 DCC47700      MOV EAX,DWORD PTR DS:[0x77C4DC]
 *  004EEA86   33C4             XOR EAX,ESP
 *  004EEA88   894424 1C        MOV DWORD PTR SS:[ESP+0x1C],EAX
 *  004EEA8C   53               PUSH EBX
 *  004EEA8D   55               PUSH EBP
 *  004EEA8E   56               PUSH ESI
 *  004EEA8F   57               PUSH EDI
 *  004EEA90   A1 DCC47700      MOV EAX,DWORD PTR DS:[0x77C4DC]
 *  004EEA95   33C4             XOR EAX,ESP
 *  004EEA97   50               PUSH EAX
 *  004EEA98   8D4424 34        LEA EAX,DWORD PTR SS:[ESP+0x34]
 *  004EEA9C   64:A3 00000000   MOV DWORD PTR FS:[0],EAX
 *  004EEAA2   8B4424 44        MOV EAX,DWORD PTR SS:[ESP+0x44]
 *  004EEAA6   8BF1             MOV ESI,ECX
 *  004EEAA8   E8 8346FBFF      CALL .004A3130
 *  004EEAAD   8BE8             MOV EBP,EAX
 *  004EEAAF   33DB             XOR EBX,EBX
 *  004EEAB1   3BEB             CMP EBP,EBX
 *  004EEAB3   75 07            JNZ SHORT .004EEABC
 *  004EEAB5   32C0             XOR AL,AL
 *  004EEAB7   E9 92000000      JMP .004EEB4E
 *  004EEABC   8B06             MOV EAX,DWORD PTR DS:[ESI]
 *  004EEABE   8B10             MOV EDX,DWORD PTR DS:[EAX]
 *  004EEAC0   8BCE             MOV ECX,ESI
 *  004EEAC2   FFD2             CALL EDX
 *  004EEAC4   8BC8             MOV ECX,EAX
 *  004EEAC6   C74424 28 0F0000>MOV DWORD PTR SS:[ESP+0x28],0xF
 *  004EEACE   895C24 24        MOV DWORD PTR SS:[ESP+0x24],EBX
 *  004EEAD2   885C24 14        MOV BYTE PTR SS:[ESP+0x14],BL
 *  004EEAD6   8D71 01          LEA ESI,DWORD PTR DS:[ECX+0x1]
 *  004EEAD9   8DA424 00000000  LEA ESP,DWORD PTR SS:[ESP]
 *  004EEAE0   8A11             MOV DL,BYTE PTR DS:[ECX]
 *  004EEAE2   41               INC ECX
 *  004EEAE3   3AD3             CMP DL,BL
 *  004EEAE5  ^75 F9            JNZ SHORT .004EEAE0
 *  004EEAE7   2BCE             SUB ECX,ESI
 *  004EEAE9   50               PUSH EAX
 *  004EEAEA   8BF9             MOV EDI,ECX
 *  004EEAEC   8D7424 18        LEA ESI,DWORD PTR SS:[ESP+0x18]
 *  004EEAF0   E8 CB27F1FF      CALL .004012C0
 *  004EEAF5   8B7C24 48        MOV EDI,DWORD PTR SS:[ESP+0x48]
 *  004EEAF9   895C24 3C        MOV DWORD PTR SS:[ESP+0x3C],EBX
 *  004EEAFD   8B75 3C          MOV ESI,DWORD PTR SS:[EBP+0x3C]
 *  004EEB00   E8 1B4A0100      CALL .00503520
 *  004EEB05   8BF8             MOV EDI,EAX
 *  004EEB07   8DB7 E4000000    LEA ESI,DWORD PTR DS:[EDI+0xE4]
 *  004EEB0D   8D4424 14        LEA EAX,DWORD PTR SS:[ESP+0x14]
 *  004EEB11   8BD6             MOV EDX,ESI
 *  004EEB13   E8 985CF1FF      CALL .004047B0
 *  004EEB18   BD 10000000      MOV EBP,0x10
 *  004EEB1D   84C0             TEST AL,AL
 *  004EEB1F   75 18            JNZ SHORT .004EEB39
 *  004EEB21   895E 10          MOV DWORD PTR DS:[ESI+0x10],EBX
 *  004EEB24   396E 14          CMP DWORD PTR DS:[ESI+0x14],EBP
 *  004EEB27   72 02            JB SHORT .004EEB2B
 *  004EEB29   8B36             MOV ESI,DWORD PTR DS:[ESI]
 *  004EEB2B   8D4424 14        LEA EAX,DWORD PTR SS:[ESP+0x14]
 *  004EEB2F   50               PUSH EAX
 *  004EEB30   8BCF             MOV ECX,EDI
 *  004EEB32   881E             MOV BYTE PTR DS:[ESI],BL
 *  004EEB34   E9 13B62100      JMP .0070A14C   ; jichi: here hookpoint#2, name is modified here, scenario and names are here accessed char by char on the top of the stack
 *  004EEB39   396C24 28        CMP DWORD PTR SS:[ESP+0x28],EBP
 *  004EEB3D   72 0D            JB SHORT .004EEB4C
 *  004EEB3F   8B4C24 14        MOV ECX,DWORD PTR SS:[ESP+0x14]
 *  004EEB43   51               PUSH ECX
 *  004EEB44   E8 42DC1900      CALL .0068C78B
 *  004EEB49   83C4 04          ADD ESP,0x4
 *  004EEB4C   B0 01            MOV AL,0x1
 *  004EEB4E   8B4C24 34        MOV ECX,DWORD PTR SS:[ESP+0x34]
 *  004EEB52   64:890D 00000000 MOV DWORD PTR FS:[0],ECX
 *  004EEB59   59               POP ECX
 *  004EEB5A   5F               POP EDI
 *  004EEB5B   5E               POP ESI
 *  004EEB5C   5D               POP EBP
 *  004EEB5D   5B               POP EBX
 *  004EEB5E   8B4C24 1C        MOV ECX,DWORD PTR SS:[ESP+0x1C]
 *  004EEB62   33CC             XOR ECX,ESP
 *  004EEB64   E8 9CD61900      CALL .0068C205
 *  004EEB69   83C4 2C          ADD ESP,0x2C
 *  004EEB6C   C3               RETN
 *  004EEB6D   CC               INT3
 *  004EEB6E   CC               INT3
 *
 *  005C70EE   CC               INT3
 *  005C70EF   CC               INT3
 *  005C70F0   83EC 18          SUB ESP,0x18
 *  005C70F3   A1 DCC47700      MOV EAX,DWORD PTR DS:[0x77C4DC]
 *  005C70F8   33C4             XOR EAX,ESP
 *  005C70FA   894424 14        MOV DWORD PTR SS:[ESP+0x14],EAX
 *  005C70FE   53               PUSH EBX
 *  005C70FF   8B5C24 20        MOV EBX,DWORD PTR SS:[ESP+0x20]
 *  005C7103   55               PUSH EBP
 *  005C7104   8B6C24 2C        MOV EBP,DWORD PTR SS:[ESP+0x2C]
 *  005C7108   8B45 1C          MOV EAX,DWORD PTR SS:[EBP+0x1C]
 *  005C710B   56               PUSH ESI
 *  005C710C   8BF2             MOV ESI,EDX
 *  005C710E   57               PUSH EDI
 *  005C710F   8BF9             MOV EDI,ECX
 *  005C7111   897424 10        MOV DWORD PTR SS:[ESP+0x10],ESI
 *  005C7115   83F8 44          CMP EAX,0x44
 *  005C7118   77 7A            JA SHORT .005C7194
 *  005C711A   0FB680 7C735C00  MOVZX EAX,BYTE PTR DS:[EAX+0x5C737C]
 *  005C7121   FF2485 60735C00  JMP DWORD PTR DS:[EAX*4+0x5C7360]
 *  005C7128   8B4B 0C          MOV ECX,DWORD PTR DS:[EBX+0xC]
 *  005C712B   8B4424 30        MOV EAX,DWORD PTR SS:[ESP+0x30]
 *  005C712F   C1E9 02          SHR ECX,0x2
 *  005C7132   3BC1             CMP EAX,ECX
 *  005C7134   73 5E            JNB SHORT .005C7194
 *  005C7136   837B 0C 00       CMP DWORD PTR DS:[EBX+0xC],0x0
 *  005C713A   75 1C            JNZ SHORT .005C7158
 *  005C713C   33DB             XOR EBX,EBX
 *  005C713E   5F               POP EDI
 *  005C713F   893483           MOV DWORD PTR DS:[EBX+EAX*4],ESI
 *  005C7142   5E               POP ESI
 *  005C7143   5D               POP EBP
 *  005C7144   B0 01            MOV AL,0x1
 *  005C7146   5B               POP EBX
 *  005C7147   8B4C24 14        MOV ECX,DWORD PTR SS:[ESP+0x14]
 *  005C714B   33CC             XOR ECX,ESP
 *  005C714D   E8 B3500C00      CALL .0068C205
 *  005C7152   83C4 18          ADD ESP,0x18
 *  005C7155   C2 0C00          RETN 0xC
 *  005C7158   8B5B 08          MOV EBX,DWORD PTR DS:[EBX+0x8]
 *  005C715B   5F               POP EDI
 *  005C715C   893483           MOV DWORD PTR DS:[EBX+EAX*4],ESI
 *  005C715F   5E               POP ESI
 *  005C7160   5D               POP EBP
 *  005C7161   B0 01            MOV AL,0x1
 *  005C7163   5B               POP EBX
 *  005C7164   8B4C24 14        MOV ECX,DWORD PTR SS:[ESP+0x14]
 *  005C7168   33CC             XOR ECX,ESP
 *  005C716A   E8 96500C00      CALL .0068C205
 *  005C716F   83C4 18          ADD ESP,0x18
 *  005C7172   C2 0C00          RETN 0xC
 *  005C7175   F3:0F104424 10   MOVSS XMM0,DWORD PTR SS:[ESP+0x10]
 *  005C717B   51               PUSH ECX
 *  005C717C   8B4C24 34        MOV ECX,DWORD PTR SS:[ESP+0x34]
 *  005C7180   8BC3             MOV EAX,EBX
 *  005C7182   F3:0F110424      MOVSS DWORD PTR SS:[ESP],XMM0
 *  005C7187   E8 14C7FFFF      CALL .005C38A0
 *  005C718C   84C0             TEST AL,AL
 *  005C718E   0F85 B2010000    JNZ .005C7346
 *  005C7194   5F               POP EDI
 *  005C7195   5E               POP ESI
 *  005C7196   5D               POP EBP
 *  005C7197   32C0             XOR AL,AL
 *  005C7199   5B               POP EBX
 *  005C719A   8B4C24 14        MOV ECX,DWORD PTR SS:[ESP+0x14]
 *  005C719E   33CC             XOR ECX,ESP
 *  005C71A0   E8 60500C00      CALL .0068C205
 *  005C71A5   83C4 18          ADD ESP,0x18
 *  005C71A8   C2 0C00          RETN 0xC
 *  005C71AB   8B4C24 30        MOV ECX,DWORD PTR SS:[ESP+0x30]
 *  005C71AF   8D5424 10        LEA EDX,DWORD PTR SS:[ESP+0x10]
 *  005C71B3   52               PUSH EDX
 *  005C71B4   8BC3             MOV EAX,EBX
 *  005C71B6   E8 25C7FFFF      CALL .005C38E0
 *  005C71BB   84C0             TEST AL,AL
 *  005C71BD  ^74 D5            JE SHORT .005C7194
 *  005C71BF   8B4C24 10        MOV ECX,DWORD PTR SS:[ESP+0x10]
 *  005C71C3   8BC7             MOV EAX,EDI
 *  005C71C5   E8 D6F0FFFF      CALL .005C62A0
 *  005C71CA   8BD8             MOV EBX,EAX
 *  005C71CC   8BCE             MOV ECX,ESI
 *  005C71CE   8BC7             MOV EAX,EDI
 *  005C71D0   E8 CBF0FFFF      CALL .005C62A0
 *  005C71D5   85DB             TEST EBX,EBX
 *  005C71D7  ^74 BB            JE SHORT .005C7194
 *  005C71D9   85C0             TEST EAX,EAX
 *  005C71DB  ^74 B7            JE SHORT .005C7194
 *  005C71DD   50               PUSH EAX
 *  005C71DE   8BC3             MOV EAX,EBX
 *  005C71E0   E8 2BCFFFFF      CALL .005C4110  ; original function call
 *  //005C71E0   E9 482F1400      JMP .0070A12D   ; jichi: here hookpoint#3, text is modified here, text in [[esp]+0x8]], length in [esp]+0xc
 *  005C71E5  ^EB A5            JMP SHORT .005C718C
 *  005C71E7   8B47 08          MOV EAX,DWORD PTR DS:[EDI+0x8]
 *  005C71EA   8B4F 0C          MOV ECX,DWORD PTR DS:[EDI+0xC]
 *  005C71ED   2BC8             SUB ECX,EAX
 *  005C71EF   C1F9 02          SAR ECX,0x2
 *  005C71F2   3BF1             CMP ESI,ECX
 *  005C71F4  ^73 9E            JNB SHORT .005C7194
 *  005C71F6   8B34B0           MOV ESI,DWORD PTR DS:[EAX+ESI*4]
 *  005C71F9   85F6             TEST ESI,ESI
 *  005C71FB  ^74 97            JE SHORT .005C7194
 *
 *  005B640E   CC               INT3
 *  005B640F   CC               INT3
 *  005B6410   53               PUSH EBX
 *  005B6411   56               PUSH ESI
 *  005B6412   B9 FCFFFFFF      MOV ECX,-0x4
 *  005B6417   57               PUSH EDI
 *  005B6418   8BF8             MOV EDI,EAX
 *  005B641A   018F B0020000    ADD DWORD PTR DS:[EDI+0x2B0],ECX
 *  005B6420   8B87 B0020000    MOV EAX,DWORD PTR DS:[EDI+0x2B0]
 *  005B6426   8B30             MOV ESI,DWORD PTR DS:[EAX]
 *  005B6428   018F B0020000    ADD DWORD PTR DS:[EDI+0x2B0],ECX
 *  005B642E   8B87 B0020000    MOV EAX,DWORD PTR DS:[EDI+0x2B0]
 *  005B6434   8B08             MOV ECX,DWORD PTR DS:[EAX]
 *  005B6436   8B87 E0010000    MOV EAX,DWORD PTR DS:[EDI+0x1E0]
 *  005B643C   2B87 DC010000    SUB EAX,DWORD PTR DS:[EDI+0x1DC]
 *  005B6442   C1F8 02          SAR EAX,0x2
 *  005B6445   3BF0             CMP ESI,EAX
 *  005B6447   73 0D            JNB SHORT .005B6456
 *  005B6449   8B87 DC010000    MOV EAX,DWORD PTR DS:[EDI+0x1DC]
 *  005B644F   8B14B0           MOV EDX,DWORD PTR DS:[EAX+ESI*4]
 *  005B6452   85D2             TEST EDX,EDX
 *  005B6454   75 13            JNZ SHORT .005B6469
 *  005B6456   68 70757200      PUSH .00727570
 *  005B645B   8BCF             MOV ECX,EDI
 *  005B645D   E8 AEC9FFFF      CALL .005B2E10
 *  005B6462   83C4 04          ADD ESP,0x4
 *  005B6465   5F               POP EDI
 *  005B6466   5E               POP ESI
 *  005B6467   5B               POP EBX
 *  005B6468   C3               RETN
 *  005B6469   8B9F E0010000    MOV EBX,DWORD PTR DS:[EDI+0x1E0]
 *  005B646F   2BD8             SUB EBX,EAX
 *  005B6471   C1FB 02          SAR EBX,0x2
 *  005B6474   3BCB             CMP ECX,EBX
 *  005B6476   73 07            JNB SHORT .005B647F
 *  005B6478   8B0488           MOV EAX,DWORD PTR DS:[EAX+ECX*4]
 *  005B647B   85C0             TEST EAX,EAX
 *  005B647D   75 14            JNZ SHORT .005B6493
 *  005B647F   51               PUSH ECX
 *  005B6480   68 A0757200      PUSH .007275A0
 *  005B6485   8BCF             MOV ECX,EDI
 *  005B6487   E8 84C9FFFF      CALL .005B2E10
 *  005B648C   83C4 08          ADD ESP,0x8
 *  005B648F   5F               POP EDI
 *  005B6490   5E               POP ESI
 *  005B6491   5B               POP EBX
 *  005B6492   C3               RETN
 *  005B6493   52               PUSH EDX
 *  005B6494   E8 77DC0000      CALL .005C4110
 *  //005B6494   E9 103D1500      JMP .0070A1A9   ; jichi: here hookpoint#4
 *  005B6499   84C0             TEST AL,AL
 *  005B649B   75 16            JNZ SHORT .005B64B3
 *  005B649D   68 D4757200      PUSH .007275D4
 *  005B64A2   B9 F0757200      MOV ECX,.007275F0                        ; ASCII "S_ASSIGN"
 *  005B64A7   E8 84C8FFFF      CALL .005B2D30
 *  005B64AC   83C4 04          ADD ESP,0x4
 *  005B64AF   5F               POP EDI
 *  005B64B0   5E               POP ESI
 *  005B64B1   5B               POP EBX
 *  005B64B2   C3               RETN
 *  005B64B3   8B8F B0020000    MOV ECX,DWORD PTR DS:[EDI+0x2B0]
 *  005B64B9   8931             MOV DWORD PTR DS:[ECX],ESI
 *  005B64BB   8387 B0020000 04 ADD DWORD PTR DS:[EDI+0x2B0],0x4
 *  005B64C2   5F               POP EDI
 *  005B64C3   5E               POP ESI
 *  005B64C4   5B               POP EBX
 *  005B64C5   C3               RETN
 *  005B64C6   CC               INT3
 *  005B64C7   CC               INT3
 *  005B64C8   CC               INT3
 *
 *  Slightly modified #4 in AliceRunPatch.dll
 *  101B6C10   5B               POP EBX
 *  101B6C11   59               POP ECX
 *  101B6C12   C3               RETN
 *  101B6C13   52               PUSH EDX
 *  101B6C14   8BC1             MOV EAX,ECX
 *  101B6C16   E9 4E7D1600      JMP .1031E969   ; jichi: hook here
 *  101B6C1B   84C0             TEST AL,AL
 *  101B6C1D   75 18            JNZ SHORT .101B6C37
 *  101B6C1F   68 FCB53310      PUSH .1033B5FC
 *  101B6C24   B9 18B63310      MOV ECX,.1033B618                        ; ASCII "S_ASSIGN"
 *  101B6C29   E8 92B8FFFF      CALL .101B24C0
 *  101B6C2E   83C4 04          ADD ESP,0x4
 *  101B6C31   5F               POP EDI
 *  101B6C32   5E               POP ESI
 *  101B6C33   5D               POP EBP
 *  101B6C34   5B               POP EBX
 *  101B6C35   59               POP ECX
 *  101B6C36   C3               RETN
 *  101B6C37   53               PUSH EBX
 *  101B6C38   56               PUSH ESI
 *  101B6C39   E8 E29C0100      CALL .101D0920
 *  101B6C3E   5F               POP EDI
 *  101B6C3F   5E               POP ESI
 *  101B6C40   5D               POP EBP
 *  101B6C41   5B               POP EBX
 *  101B6C42   59               POP ECX
 *  101B6C43   C3               RETN
 *  101B6C44   CC               INT3
 *  101B6C45   CC               INT3
 *  101B6C46   CC               INT3
 *
 *  The function get called to paint string of names for hookpoint #2, text in arg1:
 *  0050B69E   CC               INT3
 *  0050B69F   CC               INT3
 *  0050B6A0   55               PUSH EBP
 *  0050B6A1   8BEC             MOV EBP,ESP
 *  0050B6A3   83E4 F8          AND ESP,0xFFFFFFF8
 *  0050B6A6   6A FF            PUSH -0x1
 *  0050B6A8   68 F8277000      PUSH .007027F8
 *  0050B6AD   64:A1 00000000   MOV EAX,DWORD PTR FS:[0]
 *  0050B6B3   50               PUSH EAX
 *  0050B6B4   83EC 18          SUB ESP,0x18
 *  0050B6B7   53               PUSH EBX
 *  0050B6B8   56               PUSH ESI
 *  0050B6B9   57               PUSH EDI
 *  0050B6BA   A1 DCC47700      MOV EAX,DWORD PTR DS:[0x77C4DC]
 *  0050B6BF   33C4             XOR EAX,ESP
 *  0050B6C1   50               PUSH EAX
 *  0050B6C2   8D4424 28        LEA EAX,DWORD PTR SS:[ESP+0x28]
 *  0050B6C6   64:A3 00000000   MOV DWORD PTR FS:[0],EAX
 *  0050B6CC   8BF9             MOV EDI,ECX
 *  0050B6CE   57               PUSH EDI
 *  0050B6CF   E8 5CEAFFFF      CALL .0050A130
 *  0050B6D4   8B45 08          MOV EAX,DWORD PTR SS:[EBP+0x8]
 *  0050B6D7   6A FF            PUSH -0x1
 *  0050B6D9   33DB             XOR EBX,EBX
 *  0050B6DB   53               PUSH EBX
 *  0050B6DC   8DB7 E4000000    LEA ESI,DWORD PTR DS:[EDI+0xE4]
 *  0050B6E2   50               PUSH EAX
 *  0050B6E3   E8 886BEFFF      CALL .00402270
 *  0050B6E8   895C24 14        MOV DWORD PTR SS:[ESP+0x14],EBX
 *  0050B6EC   895C24 18        MOV DWORD PTR SS:[ESP+0x18],EBX
 *  0050B6F0   895C24 1C        MOV DWORD PTR SS:[ESP+0x1C],EBX
 *  0050B6F4   56               PUSH ESI
 *  0050B6F5   8D4C24 18        LEA ECX,DWORD PTR SS:[ESP+0x18]
 *  0050B6F9   51               PUSH ECX
 *  0050B6FA   57               PUSH EDI
 *  0050B6FB   895C24 3C        MOV DWORD PTR SS:[ESP+0x3C],EBX
 *  0050B6FF   E8 6C290000      CALL .0050E070
 *  0050B704   8D5424 14        LEA EDX,DWORD PTR SS:[ESP+0x14]
 *  0050B708   8BCF             MOV ECX,EDI
 *  0050B70A   E8 B1010000      CALL .0050B8C0
 *  0050B70F   8B7424 14        MOV ESI,DWORD PTR SS:[ESP+0x14]
 *  0050B713   C687 E0000000 01 MOV BYTE PTR DS:[EDI+0xE0],0x1
 *  0050B71A   3BF3             CMP ESI,EBX
 *  0050B71C   74 14            JE SHORT .0050B732
 *  0050B71E   8B7C24 18        MOV EDI,DWORD PTR SS:[ESP+0x18]
 *  0050B722   8BC6             MOV EAX,ESI
 *  0050B724   E8 7751F0FF      CALL .004108A0
 *  0050B729   56               PUSH ESI
 *  0050B72A   E8 5C101800      CALL .0068C78B
 *  0050B72F   83C4 04          ADD ESP,0x4
 *  0050B732   8B4C24 28        MOV ECX,DWORD PTR SS:[ESP+0x28]
 *  0050B736   64:890D 00000000 MOV DWORD PTR FS:[0],ECX
 *  0050B73D   59               POP ECX
 *  0050B73E   5F               POP EDI
 *  0050B73F   5E               POP ESI
 *  0050B740   5B               POP EBX
 *  0050B741   8BE5             MOV ESP,EBP
 *  0050B743   5D               POP EBP
 *  0050B744   C2 0400          RETN 0x4
 *  0050B747   CC               INT3
 *  0050B748   CC               INT3
 *  0050B749   CC               INT3
 *  0050B74A   CC               INT3
 *  0050B74B   CC               INT3
 *  0050B74C   CC               INT3
 *
 *  Function get called for hookpoint #3, text in [arg1+0x10], length in arg1+0xc, only for scenario, function call is looped
 *  005C410D   CC               INT3
 *  005C410E   CC               INT3
 *  005C410F   CC               INT3
 *  005C4110   53               PUSH EBX
 *  005C4111   8B5C24 08        MOV EBX,DWORD PTR SS:[ESP+0x8]
 *  005C4115   837B 0C 00       CMP DWORD PTR DS:[EBX+0xC],0x0
 *  005C4119   56               PUSH ESI
 *  005C411A   57               PUSH EDI
 *  005C411B   8BF0             MOV ESI,EAX
 *  005C411D   74 07            JE SHORT .005C4126
 *  005C411F   8B43 08          MOV EAX,DWORD PTR DS:[EBX+0x8]
 *  005C4122   85C0             TEST EAX,EAX
 *  005C4124   75 04            JNZ SHORT .005C412A
 *  005C4126   33C0             XOR EAX,EAX
 *  005C4128   EB 0F            JMP SHORT .005C4139
 *  005C412A   8D50 01          LEA EDX,DWORD PTR DS:[EAX+0x1]
 *  005C412D   8D49 00          LEA ECX,DWORD PTR DS:[ECX]
 *  005C4130   8A08             MOV CL,BYTE PTR DS:[EAX]
 *  005C4132   40               INC EAX
 *  005C4133   84C9             TEST CL,CL
 *  005C4135  ^75 F9            JNZ SHORT .005C4130
 *  005C4137   2BC2             SUB EAX,EDX
 *  005C4139   8D78 01          LEA EDI,DWORD PTR DS:[EAX+0x1]
 *  005C413C   3B7E 0C          CMP EDI,DWORD PTR DS:[ESI+0xC]
 *  005C413F   76 0F            JBE SHORT .005C4150
 *  005C4141   E8 FAF7FFFF      CALL .005C3940
 *  005C4146   84C0             TEST AL,AL
 *  005C4148   75 06            JNZ SHORT .005C4150
 *  005C414A   5F               POP EDI
 *  005C414B   5E               POP ESI
 *  005C414C   5B               POP EBX
 *  005C414D   C2 0400          RETN 0x4
 *  005C4150   837B 0C 00       CMP DWORD PTR DS:[EBX+0xC],0x0
 *  005C4154   75 04            JNZ SHORT .005C415A
 *  005C4156   33C9             XOR ECX,ECX
 *  005C4158   EB 03            JMP SHORT .005C415D
 *  005C415A   8B4B 08          MOV ECX,DWORD PTR DS:[EBX+0x8]
 *  005C415D   837E 0C 00       CMP DWORD PTR DS:[ESI+0xC],0x0
 *  005C4161   75 15            JNZ SHORT .005C4178
 *  005C4163   57               PUSH EDI
 *  005C4164   33C0             XOR EAX,EAX
 *  005C4166   51               PUSH ECX
 *  005C4167   50               PUSH EAX
 *  005C4168   E8 33400D00      CALL .006981A0
 *  005C416D   83C4 0C          ADD ESP,0xC
 *  005C4170   5F               POP EDI
 *  005C4171   5E               POP ESI
 *  005C4172   B0 01            MOV AL,0x1
 *  005C4174   5B               POP EBX
 *  005C4175   C2 0400          RETN 0x4
 *  005C4178   8B46 08          MOV EAX,DWORD PTR DS:[ESI+0x8]
 *  005C417B   57               PUSH EDI
 *  005C417C   51               PUSH ECX
 *  005C417D   50               PUSH EAX
 *  005C417E   E8 1D400D00      CALL .006981A0
 *  005C4183   83C4 0C          ADD ESP,0xC
 *  005C4186   5F               POP EDI
 *  005C4187   5E               POP ESI
 *  005C4188   B0 01            MOV AL,0x1
 *  005C418A   5B               POP EBX
 *  005C418B   C2 0400          RETN 0x4
 *  005C418E   CC               INT3
 */
static bool InsertSystem43NewHook(ULONG startAddress, ULONG stopAddress, LPCSTR hookName)
{
  const BYTE bytes[] = {
    0xe8, XX4,              // 004eeb34   e8 67cb0100      call .0050b6a0  ; jichi: hook here, text on the top of the stack
    0x39,0x6c,0x24, 0x28,   // 004eeb39   396c24 28        cmp dword ptr ss:[esp+0x28],ebp
    0x72, 0x0d,             // 004eeb3d   72 0d            jb short .004eeb4c
    0x8b,0x4c,0x24, 0x14,   // 004eeb3f   8b4c24 14        mov ecx,dword ptr ss:[esp+0x14]
    0x51,                   // 004eeb43   51               push ecx
    0xe8 //, XX4,           // 004eeb44   e8 42dc1900      call .0068c78b
  };
  enum { addr_offset = 0 };
  ULONG addr = MemDbg::matchBytes(bytes, sizeof(bytes), startAddress, stopAddress);
  //GROWL_DWORD(addr);
  if (!addr) {
    ConsoleOutput("vnreng:System43+: pattern not found");
    return false;
  }

  //addr = *(DWORD *)(addr+1) + addr + 5; // change to hook to the actual address of function being called

  HookParam hp = {};
  hp.address = addr;
  hp.type = NO_CONTEXT|USING_STRING|USING_SPLIT|SPLIT_INDIRECT;
  //hp.type = NO_CONTEXT|USING_STRING|FIXING_SPLIT;
  hp.split_index = 0x10; // use [[esp]+0x10] to differentiate name and thread
  //hp.offset = 4 * 1; // text in arg1

  // Only name can be modified here, where the value of split is 0x6, and text in 0x2

  ConsoleOutput("vnreng: INSERT System43+");
  NewHook(hp, hookName);

  ConsoleOutput("vnreng:System43+: disable GDI hooks"); // disable hooking to TextOutA, which is cached
  DisableGDIHooks();
  return true;
}

bool InsertSystem43Hook()
{
  //bool patched = IthCheckFile(L"AliceRunPatch.dll");
  bool patched = ::GetModuleHandleA("AliceRunPatch.dll");
  ULONG startAddress, stopAddress;
  if (patched ?
      !NtInspect::getModuleMemoryRange(L"AliceRunPatch.dll", &startAddress, &stopAddress) :
      !NtInspect::getProcessMemoryRange(&startAddress, &stopAddress)) {
    ConsoleOutput("vnreng:System43: failed to get memory range");
    return false;
  }
  // Insert new hook first
  bool ok = InsertSystem43OldHook(startAddress, stopAddress, patched ? "AliceRunPatch43" : "System43");
  ok = InsertSystem43NewHook(startAddress, stopAddress, "System43+") || ok;
  return ok;
}

/********************************************************************************************
AtelierKaguya hook:
  Game folder contains message.dat. Used by AtelierKaguya games.
  Usually has font caching issue with TextOutA.
  Game engine uses EBP to set up stack frame so we can easily trace back.
  Keep step out until it's in main game module. We notice that either register or
  stack contains string pointer before call instruction. But it's not quite stable.
  In-depth analysis of the called function indicates that there's a loop traverses
  the string one character by one. We can set a hook there.
  This search process is too complex so I just make use of some characteristic
  instruction(add esi,0x40) to locate the right point.
********************************************************************************************/
bool InsertAtelierHook()
{
  //SafeFillRange(process_name_, &base, &size);
  //size=size-base;
  //DWORD sig = 0x40c683; // add esi,0x40
  //i=module_base_+SearchPattern(module_base_,module_limit_-module_base_,&sig,3);
  DWORD i;
  for (i = module_base_; i < module_limit_ - 4; i++) {
    DWORD sig = *(DWORD *)i & 0xffffff;
    if (0x40c683 == sig) // add esi,0x40
      break;
  }
  if (i < module_limit_ - 4)
    for (DWORD j=i-0x200; i>j; i--)
      if (*(DWORD *)i == 0xff6acccc) { // find the function entry
        HookParam hp = {};
        hp.address = i+2;
        hp.offset = 8;
        hp.split = -0x18;
        hp.length_offset = 1;
        hp.type = USING_SPLIT;
        ConsoleOutput("vnreng: INSERT Aterlier KAGUYA");
        NewHook(hp, "Atelier KAGUYA");
        //RegisterEngineType(ENGINE_ATELIER);
        return true;
      }

  ConsoleOutput("vnreng:Aterlier: failed");
  return false;
  //ConsoleOutput("Unknown Atelier KAGUYA engine.");
}
/********************************************************************************************
CIRCUS hook:
  Game folder contains advdata folder. Used by CIRCUS games.
  Usually has font caching issues. But trace back from GetGlyphOutline gives a hook
  which generate repetition.
  If we study circus engine follow Freaka's video, we can easily discover that
  in the game main module there is a static buffer, which is filled by new text before
  it's drawing to screen. By setting a hardware breakpoint there we can locate the
  function filling the buffer. But we don't have to set hardware breakpoint to search
  the hook address if we know some characteristic instruction(cmp al,0x24) around there.
********************************************************************************************/
bool InsertCircusHook1() // jichi 10/2/2013: Change return type to bool
{
  for (DWORD i = module_base_ + 0x1000; i < module_limit_ - 4; i++)
    if (*(WORD *)i == 0xa3c)  //cmp al, 0xA; je
      for (DWORD j = i; j < i + 0x100; j++) {
        BYTE c = *(BYTE *)j;
        if (c == 0xc3)
          break;
        if (c == 0xe8) {
          DWORD k = *(DWORD *)(j+1)+j+5;
          if (k > module_base_ && k < module_limit_) {
            HookParam hp = {};
            hp.address = k;
            hp.offset = 0xc;
            hp.split = -0x18;
            hp.length_offset = 1;
            hp.type = DATA_INDIRECT|USING_SPLIT;
            ConsoleOutput("vnreng: INSERT CIRCUS#1");
            NewHook(hp, "Circus1");
            //RegisterEngineType(ENGINE_CIRCUS);
            return true;
          }
        }
      }
      //break;
  //ConsoleOutput("Unknown CIRCUS engine");
  ConsoleOutput("vnreng:CIRCUS1: failed");
  return false;
}

/**
 *  jichi 6/5/2014: Sample function from DC3 at 0x4201d0
 *  004201ce     cc             int3
 *  004201cf     cc             int3
 *  004201d0  /$ 8b4c24 08      mov ecx,dword ptr ss:[esp+0x8]
 *  004201d4  |. 8a01           mov al,byte ptr ds:[ecx]
 *  004201d6  |. 84c0           test al,al
 *  004201d8  |. 74 1c          je short dc3.004201f6
 *  004201da  |. 8b5424 04      mov edx,dword ptr ss:[esp+0x4]
 *  004201de  |. 8bff           mov edi,edi
 *  004201e0  |> 3c 24          /cmp al,0x24
 *  004201e2  |. 75 05          |jnz short dc3.004201e9
 *  004201e4  |. 83c1 02        |add ecx,0x2
 *  004201e7  |. eb 04          |jmp short dc3.004201ed
 *  004201e9  |> 8802           |mov byte ptr ds:[edx],al
 *  004201eb  |. 42             |inc edx
 *  004201ec  |. 41             |inc ecx
 *  004201ed  |> 8a01           |mov al,byte ptr ds:[ecx]
 *  004201ef  |. 84c0           |test al,al
 *  004201f1  |.^75 ed          \jnz short dc3.004201e0
 *  004201f3  |. 8802           mov byte ptr ds:[edx],al
 *  004201f5  |. c3             retn
 *  004201f6  |> 8b4424 04      mov eax,dword ptr ss:[esp+0x4]
 *  004201fa  |. c600 00        mov byte ptr ds:[eax],0x0
 *  004201fd  \. c3             retn
 */
bool InsertCircusHook2() // jichi 10/2/2013: Change return type to bool
{
  for (DWORD i = module_base_ + 0x1000; i < module_limit_ -4; i++)
    if ((*(DWORD *)i & 0xffffff) == 0x75243c) { // cmp al, 24; je
      if (DWORD j = SafeFindEntryAligned(i, 0x80)) {
        HookParam hp = {};
        hp.address = j;
        hp.offset = 0x8;
        //hp.filter_fun = CharNewLineFilter; // \n\s* is used to remove new line
        hp.type = USING_STRING;
        ConsoleOutput("vnreng: INSERT CIRCUS#2");
        //GROWL_DWORD(hp.address); // jichi 6/5/2014: 0x4201d0 for DC3
        NewHook(hp, "Circus");
        //RegisterEngineType(ENGINE_CIRCUS);
        return true;
      }
      break;
    }
  //ConsoleOutput("Unknown CIRCUS engine.");
  ConsoleOutput("vnreng:CIRCUS: failed");
  return false;
}

/********************************************************************************************
ShinaRio hook:
  Game folder contains rio.ini.
  Problem of default hook GetTextExtentPoint32A is that the text repeat one time.
  But KF just can't resolve the issue. ShinaRio engine always perform integrity check.
  So it's very difficult to insert a hook into the game module. Freaka suggests to refine
  the default hook by adding split parameter on the stack. So far there is 2 different
  version of ShinaRio engine that needs different split parameter. Seems this value is
  fixed to the last stack frame. We just navigate to the entry. There should be a
  sub esp,* instruction. This value plus 4 is just the offset we need.

  New ShinaRio engine (>=2.48) uses different approach.
********************************************************************************************/
namespace { // unnamed
// jichi 3/1/2015: hook for new ShinaRio games
void SpecialHookShina2(DWORD esp_base, HookParam *, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  DWORD ptr = *(DWORD*)(esp_base-0x20); ; // jichi: esi
  *split = ptr; // [esi]
  char* str = *(char**)(ptr+0x160);
  strcpy(text_buffer, str);
  int skip = 0;
  for (str = text_buffer; *str; str++)
    if (str[0] == 0x5f) {   // jichi 7/10/2015: Skip _r (new line)
      if (str[1] == 0x72)   // jichi 7/10/2015: Skip _t until /
        str[0] = str[1]=1;
      else if (str[1] == 0x74) {
        while (str[0] != 0x2f)
          *str++ = 1;
        *str=1;
      }
    }

  for (str = text_buffer; str[skip];)
    if (str[skip] == 1)
      skip++;
    else {
      str[0]=str[skip];
      str++;
    }

  str[0] = 0;
  if (strcmp(text_buffer, text_buffer_prev) == 0)
    *len=0;
  else {
    for (skip = 0; text_buffer[skip]; skip++)
      text_buffer_prev[skip] = text_buffer[skip];
    text_buffer_prev[skip] = 0;
    *data = (DWORD)text_buffer_prev;
    *len = skip;
  }
}

// jichi 3/1/2015: hook for old ShinaRio games
// Used to merge correct text thread.
// 1. Only keep threads with 0 and -1 split
// 2. Skip the thread withb 0 split and with minimum return address
void SpecialHookShina1(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  static DWORD min_retaddr = -1;
  DWORD s = *(DWORD *)(esp_base + hp->split);
  if (s == 0 || (s & 0xffff) == 0xffff) { // only keep threads with 0 and -1 split
    if (s == 0 && retof(esp_base) <= min_retaddr) {
      min_retaddr = retof(esp_base);
      return;
    }
    *split = FIXED_SPLIT_VALUE;
    // Follow the same logic as the hook.
    *data = *(DWORD *)*data; // DATA_INDIRECT
    *len = LeadByteTable[*data & 0xff];
  }
}

// jichi 8/27/2013
// Return ShinaRio version number
// The head of Rio.ini usually looks like:
//     [椎名里�v2.49]
// This function will return 49 in the above case.
//
// Games from アトリエさく�do not have Rio.ini, but $procname.ini.
int GetShinaRioVersion()
{
  int ret = 0;
  HANDLE hFile = IthCreateFile(L"RIO.INI", FILE_READ_DATA, FILE_SHARE_READ, FILE_OPEN);
  if (hFile == INVALID_HANDLE_VALUE)  {
    size_t len = ::wcslen(process_name_);
    if (len > 3) {
      wchar_t fname[MAX_PATH];
      ::wcscpy(fname, process_name_);
      fname[len -1] = 'i';
      fname[len -2] = 'n';
      fname[len -3] = 'i';
      hFile = IthCreateFile(fname, FILE_READ_DATA, FILE_SHARE_READ, FILE_OPEN);
    }
  }

  if (hFile != INVALID_HANDLE_VALUE)  {
    IO_STATUS_BLOCK ios;
    //char *buffer,*version;//,*ptr;
    enum { BufferSize = 0x40 };
    char buffer[BufferSize];
    NtReadFile(hFile, 0, 0, 0, &ios, buffer, BufferSize, 0, 0);
    NtClose(hFile);
    if (buffer[0] == '[') {
      buffer[0x3f] = 0; // jichi 8/24/2013: prevent strstr from overflow
      if (char *version = ::strstr(buffer, "v2."))
        ::sscanf(version + 3, "%d", &ret); // +3 to skip "v2."
    }
  }
  return ret;
}

} // unnamed namespace

// jichi 8/24/2013: Rewrite ShinaRio logic.
// Test games: ���×S�� (PK), version ShinaRio 2.47
bool InsertShinaHook()
{
  int ver = GetShinaRioVersion();
  if (ver >= 48) { // v2.48, v2.49
    HookParam hp = {};
    hp.address = (DWORD)::GetTextExtentPoint32A;
    hp.text_fun = SpecialHookShina2;
    hp.type = USING_STRING;
    ConsoleOutput("vnreng: INSERT ShinaRio > 2.47");
    NewHook(hp, "ShinaRio");
    //RegisterEngineType(ENGINE_SHINA);
    return true;

  } else if (ver > 40) { // <= v2.47. Older games like あやかしびと does not require hcode
    // jichi 3/13/2015: GetGlyphOutlineA is not hooked, which might produce correct text
    // BOOL GetTextExtentPoint32(HDC hdc, LPCTSTR lpString, int c, LPSIZE lpSize);
    enum stack { // current stack
      arg0_retaddr = 0 // pseudo arg
      , arg1_hdc = 4 * 1
      , arg2_lpString = 4 * 2
      , arg3_c = 4 * 3
      , arg4_lpSize = 4 * 4
    };

    HookParam hp = {};
    hp.address = (DWORD)::GetTextExtentPoint32A;
    hp.offset = arg2_lpString; // 0x8
    hp.length_offset = 1;
    hp.type = DATA_INDIRECT|USING_SPLIT;

    enum { sub_esp = 0xec81 }; // jichi: caller pattern: sub esp = 0x81,0xec
    if (DWORD s = Util::FindCallAndEntryBoth((DWORD)GetTextExtentPoint32A, module_limit_ - module_base_, module_base_, sub_esp)) {
      ConsoleOutput("vnreng: INSERT ShinaRio <= 2.47 dynamic split");
      hp.split = *(DWORD *)(s + 2) + 4;
       //RegisterEngineType(ENGINE_SHINA);
      NewHook(hp, "ShinaRio");

    } else {
      // jichi 3/13/2015: GetTextExtentPoint32A is not statically invoked in ���×S�� (PK)
      // See: http://sakuradite.com/topic/671
      // See: http://www.hongfire.com/forum/showthread.php/36807-AGTH-text-extraction-tool-for-games-translation/page347
      //
      // [Guilty+]Rin x Sen �Hakudaku Onna Kyoushi to Yaroudomo /HB8*0:44@0:GDI32.dll:GetTextExtentPoint32A /Ftext@4339A2:0;choices@4339A2:ffffff
      //
      // addr: 0 , text_fun: 0x0 , function: 135408591 , hook_len: 0 , ind: 0 , length_of
      // fset: 1 , module: 1409538707 , off: 8 , recover_len: 0 , split: 68 , split_ind:
      // 0 , type: 216
      //
      // Message speed needs to be set to something slower then fastest(instant) or text wont show up in agth.
      // Last edited by Freaka; 09-29-2009 at 11:48 AM.

      // Issues:
      // 1. The text speed must NOT to be set to the fastest.
      // 2. There might be a wrong text thread that is almost correct, except that its first character is chopped.
      // Otherwise, the first character will be split in another thread
      ConsoleOutput("vnreng: INSERT ShinaRio <= 2.47 static split");
      hp.split = 0x44;
      //hp.type |= FIXING_SPLIT|NO_CONTEXT; // merge all threads
      //hp.text_fun = SpecialHookShina1;
      NewHook(hp, "ShinaRio2"); // jichi: mark as ShinaRio2 so that VNR is able to warn user about the text speed issue
    }
    return true;
  }
  ConsoleOutput("vnreng:ShinaRio: unknown version");
  return false;
}

bool InsertWaffleDynamicHook(LPVOID addr, DWORD frame, DWORD stack)
{
  if (addr != ::GetTextExtentPoint32A)
    return false;

  DWORD handler;
  __asm
  {
    mov eax,fs:[0]
    mov eax,[eax]
    mov eax,[eax]
    mov eax,[eax]
    mov eax,[eax]
    mov eax,[eax]
    mov ecx, [eax + 4]
    mov handler, ecx
  }

  union {
    DWORD i;
    BYTE *ib;
    DWORD *id;
  };
  // jichi 9/30/2013: Fix the bug in ITH logic where j is uninitialized
  for (i = module_base_ + 0x1000; i < module_limit_ - 4; i++)
    if (*id == handler && *(ib - 1) == 0x68)
      if (DWORD t = SafeFindEntryAligned(i, 0x40)) {
        HookParam hp = {};
        hp.address = t;
        hp.offset = 8;
        hp.index = 4;
        hp.length_offset = 1;
        hp.type = DATA_INDIRECT;
        ConsoleOutput("vnreng: INSERT Dynamic Waffle");
        NewHook(hp, "Waffle");
        return true;
      }
  ConsoleOutput("vnreng:DynamicWaffle: failed");
  //ConsoleOutput("Unknown waffle engine.");
  return true; // jichi 12/25/2013: return true
}
//  DWORD retn,limit,str;
//  WORD ch;
//  NTSTATUS status;
//  MEMORY_BASIC_INFORMATION info;
//  str = *(DWORD*)(stack+0xC);
//  ch = *(WORD*)str;
//  if (ch<0x100) return false;
//  limit = (stack | 0xFFFF) + 1;
//  __asm int 3
//  for (stack += 0x10; stack < limit; stack += 4)
//  {
//    str = *(DWORD*)stack;
//    if ((str >> 16) != (stack >> 16))
//    {
//      status = NtQueryVirtualMemory(NtCurrentProcess(),(PVOID)str,MemoryBasicInformation,&info,sizeof(info),0);
//      if (!NT_SUCCESS(status) || info.Protect & PAGE_NOACCESS) continue; //Accessible
//    }
//    if (*(WORD*)(str + 4) == ch) break;
//  }
//  if (stack < limit)
//  {
//    for (limit = stack + 0x100; stack < limit ; stack += 4)
//    if (*(DWORD*)stack == -1)
//    {
//      retn = *(DWORD*)(stack + 4);
//      if (retn > module_base_ && retn < module_limit_)
//      {
//        HookParam hp = {};
//        hp.address = retn + *(DWORD*)(retn - 4);
//        hp.length_offset = 1;
//        hp.offset = -0x20;
//        hp.index = 4;
//        //hp.split = 0x1E8;
//        hp.type = DATA_INDIRECT;
//        NewHook(hp, "WAFFLE");
//        //RegisterEngineType(ENGINE_WAFFLE);
//        return true;
//      }
//
//    }
//
//  }

/** jichi 8/18/2015
 *  Sample game: 完全時間停止 体験版
 *  GDI text: TextOutA and GetTextExtentPoint32A
 */
void InsertWaffleHook()
{
  for (DWORD i = module_base_ + 0x1000; i < module_limit_ - 4; i++)
    if (*(DWORD *)i == 0xac68) {
      HookParam hp = {};
      hp.address = i;
      hp.length_offset = 1;
      hp.offset = -0x20;
      hp.index = 4;
      hp.split = 0x1e8;
      hp.type = DATA_INDIRECT|USING_SPLIT;
      ConsoleOutput("vnreng: INSERT WAFFLE");
      NewHook(hp, "WAFFLE");
      return;
    }
  //ConsoleOutput("Probably Waffle. Wait for text.");
  trigger_fun_ = InsertWaffleDynamicHook;
  SwitchTrigger(true);
  //ConsoleOutput("vnreng:WAFFLE: failed");
}

void InsertTinkerBellHook()
{
  //DWORD s1,s2,i;
  //DWORD ch=0x8141;
  DWORD i;
  WORD count;
  count = 0;
  HookParam hp = {};
  hp.length_offset = 1;
  hp.type = BIG_ENDIAN|NO_CONTEXT;
  for (i = module_base_; i< module_limit_ - 4; i++) {
    if (*(DWORD*)i == 0x8141) {
      BYTE t = *(BYTE*)(i - 1);
      if (t == 0x3d || t == 0x2d) {
        hp.offset = -0x8;
        hp.address = i - 1;
      } else if (*(BYTE*)(i-2) == 0x81) {
        t &= 0xf8;
        if (t == 0xf8 || t == 0xe8) {
          hp.offset = -8 - ((*(BYTE*)(i-1) & 7) << 2);
          hp.address = i - 2;
        }
      }
      if (hp.address) {
        char hook_name[0x20];
        ::strcpy(hook_name, "TinkerBell"); // size = 0xa
        hook_name[0xa] = '0' + count;
        hook_name[0xb] = 0;
        ConsoleOutput("vnreng:INSERT TinkerBell");
        NewHook(hp, hook_name);
        count++;
        hp.address = 0;
      }
    }
  }
  ConsoleOutput("vnreng:TinkerBell: failed");
}

//  s1=SearchPattern(module_base_,module_limit_-module_base_-4,&ch,4);
//  if (s1)
//  {
//    for (i=s1;i>s1-0x400;i--)
//    {
//      if (*(WORD*)(module_base_+i)==0xec83)
//      {
//        hp.address=module_base_+i;
//        NewHook(hp, "C.System");
//        break;
//      }
//    }
//  }
//  s2=s1+SearchPattern(module_base_+s1+4,module_limit_-s1-8,&ch,4);
//  if (s2)
//  {
//    for (i=s2;i>s2-0x400;i--)
//    {
//      if (*(WORD*)(module_base_+i)==0xec83)
//      {
//        hp.address=module_base_+i;
//        NewHook(hp, "TinkerBell");
//        break;
//      }
//    }
//  }
//  //if (count)
  //RegisterEngineType(ENGINE_TINKER);

// jichi 3/19/2014: Insert both hooks
//void InsertLuneHook()
bool InsertMBLHook()
{
  enum : DWORD { fun = 0xec8b55 }; // jichi 10/20/2014: mov ebp,esp, sub esp,*
  bool ret = false;
  if (DWORD c = Util::FindCallOrJmpAbs((DWORD)::ExtTextOutA, module_limit_ - module_base_, module_base_, true))
    if (DWORD addr = Util::FindCallAndEntryRel(c, module_limit_ - module_base_, module_base_, fun)) {
      HookParam hp = {};
      hp.address = addr;
      hp.offset = 4;
      hp.type = USING_STRING;
      ConsoleOutput("vnreng:INSERT MBL-Furigana");
      NewHook(hp, "MBL-Furigana");
      ret = true;
    }
  if (DWORD c = Util::FindCallOrJmpAbs((DWORD)::GetGlyphOutlineA, module_limit_ - module_base_, module_base_, true))
    if (DWORD addr = Util::FindCallAndEntryRel(c, module_limit_ - module_base_, module_base_, fun)) {
      HookParam hp = {};
      hp.address = addr;
      hp.offset = 4;
      hp.split = -0x18;
      hp.length_offset = 1;
      hp.type = BIG_ENDIAN|USING_SPLIT;
      ConsoleOutput("vnreng:INSERT MBL");
      NewHook(hp, "MBL");
      ret = true;
    }
  if (!ret)
    ConsoleOutput("vnreng:MBL: failed");
  return ret;
}

/** jichi 7/26/2014: E.A.G.L.S engine for TechArts games (SQUEEZ, May-Be Soft)
 *  Sample games: [May-Be Soft] ちぽ�んじ� *  Should also work for SQUEEZ's 孕ませシリーズ
 *
 *  Two functions  calls to GetGlyphOutlineA are responsible for painting.
 *  - 0x4094ef
 *  - 0x409e35
 *  However, by default, one of the thread is like: scenario namename scenario
 *  The other thread have infinite loop.
 */
bool InsertEaglsHook()
{
  // DWORD GetGlyphOutline(HDC hdc, UINT uChar,  UINT uFormat, LPGLYPHMETRICS lpgm, DWORD cbBuffer, LPVOID lpvBuffer, const MAT2 *lpmat2);
  enum stack { // current stack
    arg0_retaddr = 0 // pseudo arg
    , arg1_hdc = 4 * 1
    , arg2_uChar = 4 * 2
    , arg3_uFormat = 4 * 3
    , arg4_lpgm = 4 * 4
    , arg5_cbBuffer = 4 * 5
    , arg6_lpvBuffer = 4 * 6
    , arg7_lpmat2 = 4 * 7
  };

  // Modify the split for GetGlyphOutlineA
  HookParam hp = {};
  hp.address = (DWORD)::GetGlyphOutlineA;
  hp.type = BIG_ENDIAN|USING_SPLIT; // the only difference is the split value
  hp.offset = arg2_uChar;
  hp.split = arg4_lpgm;
  //hp.split = arg7_lpmat2;
  hp.length_offset = 1;
  ConsoleOutput("vnreng:INSERT EAGLS");
  NewHook(hp, "EAGLS");
  return true;
}

/********************************************************************************************
YU-RIS hook:
  Becomes common recently. I first encounter this game in Whirlpool games.
  Problem is name is repeated multiple times.
  Step out of function call to TextOuA, just before call to this function,
  there should be a piece of code to calculate the length of the name.
  This length is 2 for single character name and text,
  For a usual name this value is greater than 2.
********************************************************************************************/

//bool InsertWhirlpoolHook() // jichi: 12/27/2014: Renamed to YU-RIS
static bool InsertYuris1Hook()
{
  //IthBreak();
  DWORD entry = Util::FindCallAndEntryBoth((DWORD)TextOutA, module_limit_ - module_base_, module_base_, 0xec83);
  //GROWL_DWORD(entry);
  if (!entry) {
    ConsoleOutput("vnreng:YU-RIS: function entry does not exist");
    return false;
  }
  entry = Util::FindCallAndEntryRel(entry - 4, module_limit_ - module_base_, module_base_, 0xec83);
  //GROWL_DWORD(entry);
  if (!entry) {
    ConsoleOutput("vnreng:YU-RIS: function entry does not exist");
    return false;
  }
  entry = Util::FindCallOrJmpRel(entry - 4,module_limit_ - module_base_ - 0x10000, module_base_ + 0x10000, false);
  DWORD i,
        t = 0;
  //GROWL_DWORD(entry);
  ITH_TRY { // jichi 12/27/2014
    for (i = entry - 4; i > entry - 0x100; i--)
      if (::IsBadReadPtr((LPCVOID)i, 4)) { // jichi 12/27/2014: might raise in new YU-RIS, 4 = sizeof(DWORD)
        ConsoleOutput("vnreng:YU-RIS: do not have read permission");
        return false;
      } else if (*(WORD *)i == 0xc085) {
        t = *(WORD *)(i + 2);
        if ((t & 0xff) == 0x76) {
          t = 4;
          break;
        } else if ((t & 0xffff) == 0x860f) {
          t = 8;
          break;
        }
      }

  } ITH_EXCEPT {
    ConsoleOutput("vnreng:YU-RIS: illegal access exception");
    return false;
  }
  if (i == entry - 0x100) {
    ConsoleOutput("vnreng:YU-RIS: pattern not exist");
    return false;
  }
  //GROWL_DWORD2(i,t);
  HookParam hp = {};
  hp.address = i + t;
  hp.offset = -0x24;
  hp.split = -0x8;
  hp.type = USING_STRING|USING_SPLIT;
  ConsoleOutput("vnreng: INSERT YU-RIS");
  //GROWL_DWORD(hp.address);
  NewHook(hp, "YU-RIS");
  //RegisterEngineType(ENGINE_WHIRLPOOL);
  return true;
}

/** jichi 12/27/2014
 *
 *  Sample game: [Whirlpool] [150217] 鯨神�ヂ�アスヂ�ラ
 *  Call site of TextOutA.
 *  00441811   90               nop
 *  00441812   90               nop
 *  00441813   90               nop
 *  00441814   8b4424 04        mov eax,dword ptr ss:[esp+0x4]
 *  00441818   8b5424 08        mov edx,dword ptr ss:[esp+0x8]
 *  0044181c   8b4c24 0c        mov ecx,dword ptr ss:[esp+0xc]
 *  00441820   57               push edi
 *  00441821   56               push esi
 *  00441822   55               push ebp
 *  00441823   53               push ebx
 *  00441824   83ec 50          sub esp,0x50
 *  00441827   8bf9             mov edi,ecx
 *  00441829   897c24 1c        mov dword ptr ss:[esp+0x1c],edi
 *  0044182d   8bda             mov ebx,edx
 *  0044182f   8be8             mov ebp,eax
 *  00441831   8b349d 603f7b00  mov esi,dword ptr ds:[ebx*4+0x7b3f60]
 *  00441838   807c24 74 01     cmp byte ptr ss:[esp+0x74],0x1
 *  0044183d   b9 00000000      mov ecx,0x0
 *  00441842   0f94c1           sete cl
 *  00441845   8d041b           lea eax,dword ptr ds:[ebx+ebx]
 *  00441848   03c3             add eax,ebx
 *  0044184a   0fafc1           imul eax,ecx
 *  0044184d   03c3             add eax,ebx
 *  0044184f   894424 0c        mov dword ptr ss:[esp+0xc],eax
 *  00441853   897424 10        mov dword ptr ss:[esp+0x10],esi
 *  00441857   8bc3             mov eax,ebx
 *  00441859   8bd7             mov edx,edi
 *  0044185b   0fbe4c24 70      movsx ecx,byte ptr ss:[esp+0x70]
 *  00441860   e8 0c030000      call .00441b71
 *  00441865   0fbec8           movsx ecx,al
 *  00441868   83f9 ff          cmp ecx,-0x1
 *  0044186b   0f84 db020000    je .00441b4c
 *  00441871   8bce             mov ecx,esi
 *  00441873   0fafc9           imul ecx,ecx
 *  00441876   a1 64365d00      mov eax,dword ptr ds:[0x5d3664]
 *  0044187b   8bf9             mov edi,ecx
 *  0044187d   c1ff 02          sar edi,0x2
 *  00441880   c1ef 1d          shr edi,0x1d
 *  00441883   03f9             add edi,ecx
 *  00441885   c1ff 03          sar edi,0x3
 *  00441888   68 ff000000      push 0xff
 *  0044188d   57               push edi
 *  0044188e   ff3485 70b48300  push dword ptr ds:[eax*4+0x83b470]
 *  00441895   ff15 a4355d00    call dword ptr ds:[0x5d35a4]             ; .00401c88
 *  0044189b   83c4 0c          add esp,0xc
 *  0044189e   8b0d 64365d00    mov ecx,dword ptr ds:[0x5d3664]
 *  004418a4   ff348d b4b48300  push dword ptr ds:[ecx*4+0x83b4b4]
 *  004418ab   ff348d d4b48300  push dword ptr ds:[ecx*4+0x83b4d4]
 *  004418b2   ff15 54e05800    call dword ptr ds:[0x58e054]             ; gdi32.selectobject
 *  004418b8   a3 b0b48300      mov dword ptr ds:[0x83b4b0],eax
 *  004418bd   8b0d 64365d00    mov ecx,dword ptr ds:[0x5d3664]
 *  004418c3   ff348d 30b48300  push dword ptr ds:[ecx*4+0x83b430]
 *  004418ca   ff348d d4b48300  push dword ptr ds:[ecx*4+0x83b4d4]
 *  004418d1   ff15 54e05800    call dword ptr ds:[0x58e054]             ; gdi32.selectobject
 *  004418d7   a3 2cb48300      mov dword ptr ds:[0x83b42c],eax
 *  004418dc   8b3d 64365d00    mov edi,dword ptr ds:[0x5d3664]
 *  004418e2   33c9             xor ecx,ecx
 *  004418e4   880cbd f5b48300  mov byte ptr ds:[edi*4+0x83b4f5],cl
 *  004418eb   880cbd f6b48300  mov byte ptr ds:[edi*4+0x83b4f6],cl
 *  004418f2   0fb64d 00        movzx ecx,byte ptr ss:[ebp]
 *  004418f6   0fb689 a0645b00  movzx ecx,byte ptr ds:[ecx+0x5b64a0]
 *  004418fd   41               inc ecx
 *  004418fe   0fbec9           movsx ecx,cl
 *  00441901   51               push ecx
 *  00441902   55               push ebp
 *  00441903   33c9             xor ecx,ecx
 *  00441905   51               push ecx
 *  00441906   51               push ecx
 *  00441907   ff34bd d4b48300  push dword ptr ds:[edi*4+0x83b4d4]
 *  0044190e   ff15 74e05800    call dword ptr ds:[0x58e074]             ; gdi32.textouta, jichi: TextOutA here
 *  00441914   0fb67d 00        movzx edi,byte ptr ss:[ebp]
 *  00441918   0fb68f a0645b00  movzx ecx,byte ptr ds:[edi+0x5b64a0]
 *  0044191f   41               inc ecx
 *  00441920   0fbef9           movsx edi,cl
 *  00441923   8b0d 64365d00    mov ecx,dword ptr ds:[0x5d3664]
 *  00441929   03c9             add ecx,ecx
 *  0044192b   8d8c09 f4b48300  lea ecx,dword ptr ds:[ecx+ecx+0x83b4f4]
 *
 *  Runtime stack: The first dword after arguments on the stack seems to be good split value.
 */
static bool InsertYuris2Hook()
{
  ULONG addr = MemDbg::findCallAddress((ULONG)::TextOutA, module_base_, module_limit_);
  if (!addr) {
    ConsoleOutput("vnreng:YU-RIS2: failed");
    return false;
  }

  // BOOL TextOut(
  //   _In_  HDC hdc,
  //   _In_  int nXStart,
  //   _In_  int nYStart,
  //   _In_  LPCTSTR lpString,
  //   _In_  int cchString
  // );
  enum stack { // current stack
    arg1_hdc = 4 * 0 // starting from 0 before function call
    , arg2_nXStart = 4 * 1
    , arg3_nYStart = 4 * 2
    , arg4_lpString = 4 * 3
    , arg5_cchString = 4 * 4
    , arg6_split = 4 * 5 // dummy argument
  };

  HookParam hp = {};
  hp.address = addr;
  hp.type = USING_STRING|USING_SPLIT|NO_CONTEXT; // disable context that will cause thread split
  hp.offset = arg4_lpString;
  hp.split = arg6_split;

  ConsoleOutput("vnreng: INSERT YU-RIS 2");
  NewHook(hp, "YU-RIS2");
  return true;
}

bool InsertYurisHook()
{ return InsertYuris1Hook() || InsertYuris2Hook(); }

bool InsertCotophaHook()
{
  // jichi 7/12/2014: Change to accurate memory ranges
  ULONG startAddress, stopAddress;
  if (!NtInspect::getProcessMemoryRange(&startAddress, &stopAddress)) { // need accurate stopAddress
    ConsoleOutput("vnreng:Cotopha: failed to get memory range");
    return false;
  }
  enum : DWORD { ins = 0xec8b55 }; // mov ebp,esp, sub esp,*  ; jichi 7/12/2014
  ULONG addr = MemDbg::findCallerAddress((ULONG)::GetTextMetricsA, ins, startAddress, stopAddress);
  if (!addr) {
    ConsoleOutput("vnreng:Cotopha: pattern not exist");
    return false;
  }
  HookParam hp = {};
  hp.address = addr;
  hp.offset = 4;
  hp.split = -0x1c; // jichi: esp?
  hp.type = USING_UNICODE|USING_SPLIT|USING_STRING;
  ConsoleOutput("vnreng: INSERT Cotopha");
  NewHook(hp, "Cotopha");
  //RegisterEngineType(ENGINE_COTOPHA);
  return true;
}

// jichi 5/10/2014
// See also: http://bbs.sumisora.org/read.php?tid=11044704&fpage=2
//
// Old engine:  グリザイアの迷宮
// 0053cc4e   cc               int3
// 0053cc4f   cc               int3
// 0053cc50   6a ff            push -0x1    ; jichi: hook here
// 0053cc52   68 6b486000      push .0060486b
// 0053cc57   64:a1 00000000   mov eax,dword ptr fs:[0]
// 0053cc5d   50               push eax
// 0053cc5e   81ec 24020000    sub esp,0x224
// 0053cc64   a1 f8647600      mov eax,dword ptr ds:[0x7664f8]
// 0053cc69   33c4             xor eax,esp
// 0053cc6b   898424 20020000  mov dword ptr ss:[esp+0x220],eax
// 0053cc72   53               push ebx
// 0053cc73   55               push ebp
// 0053cc74   56               push esi
// 0053cc75   57               push edi
//
// Stack:
// 0544e974   0053d593  return to .0053d593 from .0053cc50
// 0544e978   045cc820
// 0544e97c   00008dc5  : jichi: text
// 0544e980   00000016
// 0544e984   0452f2e4
// 0544e988   00000000
// 0544e98c   00000001
// 0544e990   0544ea94
// 0544e994   04513840
// 0544e998   0452f2b8
// 0544e99c   04577638
// 0544e9a0   04620450
// 0544e9a4   00000080
// 0544e9a8   00000080
// 0544e9ac   004914f3  return to .004914f3 from .0055c692
//
// Registers:
// edx 0
// ebx 00000016
//
//
// New engine: イノセントガール
// Stack:
// 051ae508   0054e9d1  return to .0054e9d1 from .0054e310
// 051ae50c   04361650
// 051ae510   00008ca9  ; jichi: text
// 051ae514   0000001a
// 051ae518   04343864
// 051ae51c   00000000
// 051ae520   00000001
// 051ae524   051ae62c
// 051ae528   041edc20
// 051ae52c   04343830
// 051ae530   0434a8b0
// 051ae534   0434a7f0
// 051ae538   00000080
// 051ae53c   00000080
// 051ae540   3f560000
// 051ae544   437f8000
// 051ae548   4433e000
// 051ae54c   16f60c00
// 051ae550   051ae650
// 051ae554   042c4c20
// 051ae558   0000002c
// 051ae55c   00439bc5  return to .00439bc5 from .0043af60
//
// Registers & stack:
// Scenario:
// eax 04361650
// ecx 04357640
// edx 04343864
// ebx 0000001a
// esp 051ae508
// ebp 00008169
// esi 04357640
// edi 051ae62c
// eip 0054e310 .0054e310
//
// 051ae508   0054e9d1  return to .0054e9d1 from .0054e310
// 051ae50c   04361650
// 051ae510   00008169
// 051ae514   0000001a
// 051ae518   04343864
// 051ae51c   00000000
// 051ae520   00000001
// 051ae524   051ae62c
// 051ae528   041edc20
// 051ae52c   04343830
// 051ae530   0434a8b0
// 051ae534   0434a7f0
// 051ae538   00000080
// 051ae53c   00000080
// 051ae540   3f560000
// 051ae544   437f8000
// 051ae548   4433e000
// 051ae54c   16f60c00
// 051ae550   051ae650
// 051ae554   042c4c20
// 051ae558   0000002c
//
// Name:
//
// eax 04362430
// ecx 17025230
// edx 0430b6e4
// ebx 0000001a
// esp 051ae508
// ebp 00008179
// esi 17025230
// edi 051ae62c
// eip 0054e310 .0054e310
//
// 051ae508   0054e9d1  return to .0054e9d1 from .0054e310
// 051ae50c   04362430
// 051ae510   00008179
// 051ae514   0000001a
// 051ae518   0430b6e4
// 051ae51c   00000000
// 051ae520   00000001
// 051ae524   051ae62c
// 051ae528   041edae0
// 051ae52c   0430b6b0
// 051ae530   0434a790
// 051ae534   0434a910
// 051ae538   00000080
// 051ae53c   00000080
// 051ae540   3efa0000
// 051ae544   4483f000
// 051ae548   44322000
// 051ae54c   16f60aa0
// 051ae550   051ae650
// 051ae554   042c4c20
// 051ae558   0000002c

static void SpecialHookCatSystem3(DWORD esp_base, HookParam *, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  //DWORD ch = *data = *(DWORD *)(esp_base + hp->offset); // arg2
  DWORD ch = *data = argof(2, esp_base);
  *len = LeadByteTable[(ch >> 8) & 0xff];  // BIG_ENDIAN
  *split = regof(edx, esp_base) >> 16;
}

bool InsertCatSystemHook()
{
  //DWORD search=0x95EB60F;
  //DWORD j,i=SearchPattern(module_base_,module_limit_-module_base_,&search,4);
  //if (i==0) return;
  //i+=module_base_;
  //for (j=i-0x100;i>j;i--)
  //  if (*(DWORD*)i==0xcccccccc) break;
  //if (i==j) return;
  //hp.address=i+4;
  //hp.offset=-0x8;
  //hp.index=4;
  //hp.split=4;
  //hp.split_index=0x18;
  //hp.type =BIG_ENDIAN|DATA_INDIRECT|USING_SPLIT|SPLIT_INDIRECT;
  //hp.length_offset=1;

  // jichi 7/12/2014: Change to accurate memory ranges
  ULONG startAddress, stopAddress;
  if (!NtInspect::getProcessMemoryRange(&startAddress, &stopAddress)) { // need accurate stopAddress
    ConsoleOutput("vnreng:CatSystem2: failed to get memory range");
    return false;
  }
  enum { beg = 0xff6acccc }; // jichi 7/12/2014: beginning of the function
  enum { addr_offset = 2 }; // skip two leading 0xcc
  ULONG addr = MemDbg::findCallerAddress((ULONG)::GetTextMetricsA, beg, startAddress, stopAddress);
  if (!addr) {
    ConsoleOutput("vnreng:CatSystem2: pattern not exist");
    return false;
  }

  HookParam hp = {};
  hp.address = addr + addr_offset; // skip 1 push?
  hp.offset = 4 * 2; // text character is in arg2
  hp.length_offset = 1; // only 1 character

  // jichi 12/23/2014: Modify split for new catsystem
  bool newEngine = IthCheckFile(L"cs2conf.dll");
  if (newEngine) {
    hp.text_fun = SpecialHookCatSystem3; // type not needed
    NewHook(hp, "CatSystem3");
    ConsoleOutput("vnreng: INSERT CatSystem3");
  } else {
    hp.type = BIG_ENDIAN|USING_SPLIT;
    hp.split = pusha_edx_off - 4; // -0x10
    NewHook(hp, "CatSystem2");
    ConsoleOutput("vnreng: INSERT CatSystem2");
  }
  //RegisterEngineType(ENGINE_CATSYSTEM);
  return true;
}

bool InsertNitroplusHook()
{
  const BYTE bytes[] = {0xb0, 0x74, 0x53};
  DWORD addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_limit_);
  if (!addr) {
    ConsoleOutput("vnreng:Nitroplus: pattern not exist");
    return false;
  }
  enum : WORD { sub_esp = 0xec83 }; // caller pattern: sub esp = 0x83,0xec
  BYTE b = *(BYTE *)(addr + 3) & 3;
  while (*(WORD *)addr != sub_esp)
    addr--;
  HookParam hp = {};
  hp.address = addr;
  hp.offset = -0x14+ (b << 2);
  hp.length_offset = 1;
  hp.type = BIG_ENDIAN;
  ConsoleOutput("vnreng: INSERT Nitroplus");
  NewHook(hp, "Nitroplus");
  //RegisterEngineType(ENGINE_Nitroplus);
  return true;
}

// jichi 6/21/2015
namespace { // unnamed

void SpecialHookRetouch1(DWORD esp_base, HookParam *, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  DWORD text = argof(1, esp_base);
  *data = text;
  *len = ::strlen((LPCSTR)text);
  *split =
    regof(eax,esp_base) == 0 ? FIXED_SPLIT_VALUE * 2 : // name
    regof(ebx,esp_base) == 0 ? FIXED_SPLIT_VALUE * 1 : // scenario
                               FIXED_SPLIT_VALUE * 3 ; // other
}

bool InsertRetouch1Hook()
{
  HMODULE hModule = ::GetModuleHandleA("resident.dll");
  if (!hModule) {
    ConsoleOutput("vnreng:Retouch: failed, dll handle not loaded");
    return false;
  }
  // private: bool __thiscall RetouchPrintManager::printSub(char const *,class UxPrintData &,unsigned long)	0x10050650	0x00050650	2904 (0xb58)	resident.dll	C:\Local\箱庭ロジヂ�\resident.dll	Exported Function
  const char *sig = "?printSub@RetouchPrintManager@@AAE_NPBDAAVUxPrintData@@K@Z";
  DWORD addr = (DWORD)::GetProcAddress(hModule, sig);
  if (!addr) {
    ConsoleOutput("vnreng:Retouch: failed, procedure not found");
    return false;
  }

  HookParam hp = {};
  hp.address = addr;
  hp.offset = 4;
  hp.type = USING_STRING|NO_CONTEXT;
  hp.text_fun = SpecialHookRetouch1;
  ConsoleOutput("vnreng: INSERT Retouch");
  NewHook(hp, "Retouch");
  return true;
}

bool InsertRetouch2Hook()
{
  HMODULE hModule = ::GetModuleHandleA("resident.dll");
  if (!hModule) {
    ConsoleOutput("vnreng:Retouch2: failed, dll handle not loaded");
    return false;
  }
  // private: void __thiscall RetouchPrintManager::printSub(char const *,unsigned long,int &,int &)	0x10046560	0x00046560	2902 (0xb56)	resident.dll	C:\Local\箱庭ロジヂ�\resident.dll	Exported Function
  const char *sig = "?printSub@RetouchPrintManager@@AAEXPBDKAAH1@Z";
  DWORD addr = (DWORD)::GetProcAddress(hModule, sig);
  if (!addr) {
    ConsoleOutput("vnreng:Retouch2: failed, procedure not found");
    return false;
  }

  HookParam hp = {};
  hp.address = addr;
  hp.offset = 4;
  hp.type = USING_STRING;
  ConsoleOutput("vnreng: INSERT Retouch");
  NewHook(hp, "Retouch");
  return true;
}

} // unnamed namespace
bool InsertRetouchHook()
{
  bool ok = InsertRetouch1Hook();
  ok = InsertRetouch2Hook() || ok;
  return ok;
}

namespace { // unnamed Malie
/********************************************************************************************
Malie hook:
  Process name is malie.exe.
  This is the most complicate code I have made. Malie engine store text string in
  linked list. We need to insert a hook to where it travels the list. At that point
  EBX should point to a structure. We can find character at -8 and font size at +10.
  Also need to enable ITH suppress function.
********************************************************************************************/
bool InsertMalieHook1()
{
  const DWORD sig1 = 0x05e3c1;
  enum { sig1_size = 3 };
  DWORD i = SearchPattern(module_base_, module_limit_ - module_base_, &sig1, sig1_size);
  if (!i) {
    ConsoleOutput("vnreng:MalieHook1: pattern i not exist");
    return false;
  }

  const WORD sig2 = 0xc383;
  enum { sig2_size = 2 };
  DWORD j = i + module_base_ + sig1_size;
  i = SearchPattern(j, module_limit_ - j, &sig2, sig2_size);
  //if (!j)
  if (!i) { // jichi 8/19/2013: Change the condition fro J to I
    ConsoleOutput("vnreng:MalieHook1: pattern j not exist");
    return false;
  }
  HookParam hp = {};
  hp.address = j + i;
  hp.offset = -0x14;
  hp.index = -0x8;
  hp.split = -0x14;
  hp.split_index = 0x10;
  hp.length_offset = 1;
  hp.type = USING_UNICODE|USING_SPLIT|DATA_INDIRECT|SPLIT_INDIRECT;
  ConsoleOutput("vnreng: INSERT MalieHook1");
  NewHook(hp, "Malie");
  //RegisterEngineType(ENGINE_MALIE);
  return true;
}

DWORD malie_furi_flag_; // jichi 8/20/2013: Make it global so that it can be reset
void SpecialHookMalie(DWORD esp_base, HookParam *, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  DWORD ch = *(DWORD *)(esp_base - 0x8) & 0xffff,
        ptr = *(DWORD *)(esp_base - 0x24);
  *data = ch;
  *len = 2;
  if (malie_furi_flag_) {
    DWORD index = *(DWORD *)(esp_base - 0x10);
    if (*(WORD *)(ptr + index * 2 - 2) < 0xa)
      malie_furi_flag_ = 0;
  }
  else if (ch == 0xa) {
    malie_furi_flag_ = 1;
    len = 0;
  }
  *split = malie_furi_flag_;
}

bool InsertMalieHook2() // jichi 8/20/2013: Change return type to boolean
{
  const BYTE bytes[] = {0x66,0x3d,0x1,0x0};
  DWORD start = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_limit_);
  if (!start) {
    ConsoleOutput("vnreng:MalieHook2: pattern not exist");
    return false;
  }
  BYTE *ptr = (BYTE *)start;
  while (true) {
    if (*(WORD *)ptr == 0x3d66) {
      ptr += 4;
      if (ptr[0] == 0x75) {
        ptr += ptr[1]+2;
        continue;
      }
      if (*(WORD *)ptr == 0x850f) {
        ptr += *(DWORD *)(ptr + 2) + 6;
        continue;
      }
    }
    break;
  }
  malie_furi_flag_ = 0; // reset old malie flag
  HookParam hp = {};
  hp.address = (DWORD)ptr + 4;
  hp.offset = -8;
  hp.length_offset = 1;
  hp.text_fun = SpecialHookMalie;
  hp.type = USING_SPLIT|USING_UNICODE|NO_CONTEXT;
  hp.type = NO_CONTEXT;
  ConsoleOutput("vnreng: INSERT MalieHook2");
  NewHook(hp, "Malie");
  //RegisterEngineType(ENGINE_MALIE);
  ConsoleOutput("vnreng:Malie2: disable GDI hooks");
  DisableGDIHooks();
  return true;
}

/**
 *  jichi 12/17/2013: Added for Electro Arms
 *  Observations from Electro Arms:
 *  1. split = 0xC can handle most texts and its dwRetn is always zero
 *  2. The text containing furigana needed to split has non-zero dwRetn when split = 0
 *
 *  3/15/2015: logic modified as the plus operation would create so many threads
 */
void SpecialHookMalie2(DWORD esp_base, HookParam *, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  CC_UNUSED(data);
  //*len = GetHookDataLength(*hp, esp_base, (DWORD)data);
  *len = 2;

  DWORD s1 = *(DWORD *)(esp_base + 0xc), // base split, which is stable
        s2 = (*(DWORD *)esp_base); // used to split out furigana, but un stable
  // http://www.binaryhexconverter.com/decimal-to-binary-converter
  //enum : DWORD { mask = 0x14 };
  *split = s1 + (s2 ? 1 : 0);
}

//  static DWORD last_split; // FIXME: This makes the special function stateful
//  DWORD s1 = *(DWORD *)esp_base; // current split at 0x0
//  if (!s1)
//    *split = last_split;
//  else {
//    DWORD s2 = *(DWORD *)(esp_base + 0xc); // second split
//    *split = last_split = s1 + s2; // not sure if plus is a good way
//  }

/**
 *  jichi 8/20/2013: Add hook for sweet light BRAVA!!
 *  See: http://www.hongfire.com/forum/printthread.php?t=36807&pp=10&page=680
 *
 *  BRAVA!! /H code: "/HWN-4:C@1A3DF4:malie.exe"
 *  - addr: 1719796 = 0x1a3df4
 *  - text_fun: 0x0
 *  - function: 0
 *  - hook_len: 0
 *  - ind: 0
 *  - length_offset: 1
 *  - module: 751199171 = 0x2cc663c3
 *  - off: 4294967288 = 0xfffffff8L = -0x8
 *  - recover_len: 0
 *  - split: 12 = 0xc
 *  - split_ind: 0
 *  - type: 1106 = 0x452
 */
bool InsertMalie2Hook()
{
  // 001a3dee    6900 70000000   imul eax,dword ptr ds:[eax],70
  // 001a3df4    0200            add al,byte ptr ds:[eax]   ; this is the place to hook
  // 001a3df6    50              push eax
  // 001a3df7    0069 00         add byte ptr ds:[ecx],ch
  // 001a3dfa    0000            add byte ptr ds:[eax],al
  const BYTE bytes1[] = {
    0x40,            // inc eax
    0x89,0x56, 0x08, // mov dword ptr ds:[esi+0x8],edx
    0x33,0xd2,       // xor edx,edx
    0x89,0x46, 0x04  // mov dword ptr ds:[esi+0x4],eax
  };
  ULONG range1 = min(module_limit_ - module_base_, MAX_REL_ADDR);
  ULONG addr = MemDbg::findBytes(bytes1, sizeof(bytes1), module_base_, module_base_ + range1);
  //reladdr = 0x1a3df4;
  if (!addr) {
    //ITH_MSG(0, "Wrong1", "t", 0);
    //ConsoleOutput("Not malie2 engine");
    ConsoleOutput("vnreng:Malie2Hook: pattern p not exist");
    return false;
  }

  addr += sizeof(bytes1); // skip bytes1
  //const BYTE bytes2[] = { 0x85, 0xc0 }; // test eax,eax
  const WORD bytes2 = 0xc085; // test eax,eax
  enum { range2 = 0x200 };
  addr = MemDbg::findBytes(&bytes2, sizeof(bytes2), addr, addr + range2);
  if (!addr) {
    //ConsoleOutput("Not malie2 engine");
    ConsoleOutput("vnreng:Malie2Hook: pattern q not exist");
    return false;
  }

  HookParam hp = {};
  hp.address = addr;
  hp.offset = -8; // pusha_eax_off - 4
  hp.length_offset = 1;
  //hp.split = 0xc; // jichi 12/17/2013: Subcontext removed
  //hp.split = -0xc; // jichi 12/17/2013: This could split the furigana, but will mess up the text
  //hp.type = USING_SPLIT|USING_UNICODE|NO_CONTEXT;
  // jichi 12/17/2013: Need extern func for Electro Arms
  // Though the hook parameter is quit similar to Malie, the original extern function does not work
  hp.type = USING_SPLIT|NO_CONTEXT|USING_UNICODE;
  hp.text_fun = SpecialHookMalie2;
  ConsoleOutput("vnreng: INSERT Malie2");
  NewHook(hp, "Malie2");

  //GROWL_DWORD2(hp.address, reladdr);
  //RegisterEngineType(ENGINE_MALIE);
  return true;
}

// jichi 2/8/3014: Return the beginning and the end of the text
// Remove the leading illegal characters
enum { _MALIE3_MAX_LENGTH = VNR_TEXT_CAPACITY };
LPCWSTR _Malie3LTrim(LPCWSTR p)
{
  if (p)
    for (int count = 0; count < _MALIE3_MAX_LENGTH; count++,
        p++)
      if (p[0] == L'v' && p[1] == L'_') { // ex. v_akr0001, v_mzk0001
        p += 9;
        return p; // must return otherwise trimming more will break the ITH repetition elimination
      } else if (p[0] >= 0xa) // ltrim illegal characters less than 0xa
        return p;
  return nullptr;
}
// Remove the trailing illegal characters
LPCWSTR _Malie3RTrim(LPCWSTR p)
{
  if (p)
    for (int count = 0; count < _MALIE3_MAX_LENGTH; count++,
         p--)
      if (p[-1] >= 0xa) { // trim illegal characters less than 0xa
        if (p[-1] >= L'0' && p[-1] <= L'9'&& p[-1-7] == L'_')
          p -= 9;
        else
          return p;
      }
  return nullptr;
}

// Example section in memory:
// 0D7D7E00  07 00 08 00 76 00 5F 00 7A 00 65 00 70 00 30 00  v_zep0
// 0D7D7E10  30 00 37 00 35 00 00 00 0C 30 42 30 41 30 01 30  075.「あぁ�// 0D7D7E20  41 30 26 20 26 20 07 00 09 00 07 00 06 00 07 00  ぁ…….
// 0D7D7E30  08 00 76 00 5F 00 7A 00 65 00 70 00 30 00 30 00  v_zep00
// 0D7D7E40  37 00 36 00 00 00 46 30 01 30 42 30 01 30 41 30  76.぀�あ、ぁ
// 0D7D7E50  41 30 41 30 26 20 26 20 26 20 26 20 01 30 63 30  ぁぁ…………、っ
// 0D7D7E60  07 00 09 00 0D 30 07 00 06 00 0A 00 0A 00 00 30  .�..
// 0D7D7E70  16 60 44 30 01 30 16 60 44 30 01 30 4A 30 5E 30  怖い、怖い、お�// 0D7D7E80  7E 30 57 30 44 30 02 30 55 4F 4C 30 16 60 44 30  ましい。何が怖い
// 0D7D7E90  6E 30 4B 30 55 30 48 30 01 30 06 52 4B 30 89 30  のかさえ、�から
// 0D7D7EA0  6A 30 44 30 02 30 07 00 06 00 0A 00 00 30 8B 89  な぀. �// 0D7D7EB0  8B 30 6A 30 88 30 02 30 8B 89 8B 30 6A 30 02 30  るなよ。見るな�// 0D7D7EC0  07 00 06 00 8B 89 8B 30 6A 30 01 30 8B 89 8B 30  見るな、見る
// 0D7D7ED0  6A 30 8B 89 8B 30 6A 30 8B 89 8B 30 6A 30 01 30  な見るな見るな�// 0D7D7EE0  1F 75 4D 30 66 30 66 30 AA 60 44 30 4B 30 88 30  生きてて悪ぁ��// 0D7D7EF0  02 30 C5 60 51 30 6A 30 44 30 63 30 66 30 07 00  。情けなぁ�て
// 0D7D7F00  01 00 E4 55 0A 00 8F 30 89 30 00 00 46 30 6A 30  嗤.わら.ぁ�
// 0D7D7F10  88 30 02 30 07 00 06 00 BE 7C 00 4E 6F 67 6A 30  よ�精一杯な
// 0D7D7F20  93 30 60 30 8B 89 03 90 57 30 66 30 4F 30 8C 30  んだ見送�てくれ
// 0D7D7F30  02 30 4A 30 58 98 44 30 57 30 7E 30 59 30 01 30  。お願いします�// 0D7D7F40  60 30 4B 30 89 30 69 30 46 30 4B 30 5D 30 6E 30  �からどぁ�そ�
// 0D7D7F50  EE 76 92 30 84 30 81 30 66 30 01 30 4F 30 60 30  目をやめて、く�
// 0D7D7F60  55 30 44 30 01 30 5D 30 93 30 6A 30 02 30 07 00  さい、そんな�
// 0D7D7F70  06 00 0A 00 00 30 07 00 01 00 BA 87 50 5B 0A 00  . 螺�
// 0D7D7F80  59 30 4C 30 00 00 8B 30 88 30 46 30 6A 30 EE 76  すが.るよぁ�目
// 0D7D7F90  67 30 00 25 00 25 07 00 06 00 BF 30 01 30 B9 30  で──タ、ス
// 0D7D7FA0  01 30 B1 30 01 30 C6 30 01 30 6A 30 93 30 66 30  、ケ、テ、なんて
// 0D7D7FB0  02 30 07 00 06 00 00 00 00 00 00 00 00 00 00 00  �.....
// 0D7D7FC0  FC D8 C0 22 00 00 00 80 74 00 00 00 00 00 00 00  .耀t...
//
// Return the end of the line
LPCWSTR _Malie3GetEOL(LPCWSTR p)
{
  if (p)
    for (int count = 0; count < _MALIE3_MAX_LENGTH; count++,
        p++)
      switch (*p) {
      case 0:
      case 0xa: // stop at \0, or \n where the text after 0xa is furigana
        return p;
      case 0x7:
        // \x07\x00\x01\x00 is used to split furigana, which we want to keep
        // \x07\x00\x04\x00 is used to split sentences, observed in シルヴァリオ ヴェンヂ�ヂ�
        // \x07\x00\x06\x00 is used to split paragraph, observed in シルヴァリオ ヴェンヂ�ヂ�
        if (p[1] < 0xa && p[1] != 0x1)
          return p;
      }
  return nullptr;
}

/**
 *  jichi 3/8/2014: Add hook for 相州戦神館學�八命陣
 *  See: http://sakuradite.com/topic/157
 *  check 0x5b51ed for ecx+edx*2
 *  Also need to skip furigana.
 */

void SpecialHookMalie3(DWORD esp_base, HookParam *, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  CC_UNUSED(split);
  DWORD ecx = regof(ecx, esp_base), // *(DWORD *)(esp_base + pusha_ecx_off - 4),
        edx = regof(edx, esp_base); // *(DWORD *)(esp_base + pusha_edx_off - 4);
  //*data = ecx + edx*2; // [ecx+edx*2];
  //*len = wcslen((LPCWSTR)data) << 2;
  // There are garbage characters
  LPCWSTR start = _Malie3LTrim((LPCWSTR)(ecx + edx*2)),
          stop = _Malie3RTrim(_Malie3GetEOL(start));
  *data = (DWORD)start;
  *len = max(0, stop - start) * 2;
  *split = FIXED_SPLIT_VALUE;
  //GROWL_DWORD5((DWORD)start, (DWORD)stop, *len, (DWORD)*start, (DWORD)_Malie3GetEOL(start));
}

/**
 *  jichi 8/20/2013: Add hook for 相州戦神館學�八命陣
 *  See: http://sakuradite.com/topic/157
 *  Credits: @ok123
 *
 *  Debugging method: insert hardware breakpoint into text
 *  There are four matches of text in the memory
 *
 *  Sample game: シルヴァリオ ヴェンヂ�ヂ�
 *  0065478B   90               NOP
 *  0065478C   90               NOP
 *  0065478D   90               NOP
 *  0065478E   90               NOP
 *  0065478F   90               NOP
 *  00654790   8B4424 04        MOV EAX,DWORD PTR SS:[ESP+0x4]
 *  00654794   56               PUSH ESI
 *  00654795   57               PUSH EDI
 *  00654796   8B50 08          MOV EDX,DWORD PTR DS:[EAX+0x8]
 *  00654799   8B08             MOV ECX,DWORD PTR DS:[EAX]
 *  0065479B   33F6             XOR ESI,ESI
 *  0065479D   66:8B3451        MOV SI,WORD PTR DS:[ECX+EDX*2]  ; jichi: text accessed here
 *  006547A1   42               INC EDX
 *  006547A2   8970 04          MOV DWORD PTR DS:[EAX+0x4],ESI
 *  006547A5   8950 08          MOV DWORD PTR DS:[EAX+0x8],EDX
 *  006547A8   8B50 04          MOV EDX,DWORD PTR DS:[EAX+0x4]
 *  006547AB   83FA 01          CMP EDX,0x1
 *  006547AE   75 2C            JNZ SHORT malie.006547DC
 *  006547B0   8B50 08          MOV EDX,DWORD PTR DS:[EAX+0x8]
 *  006547B3   33F6             XOR ESI,ESI
 *  006547B5   66:8B3451        MOV SI,WORD PTR DS:[ECX+EDX*2]
 *  006547B9   42               INC EDX
 *  006547BA   8970 04          MOV DWORD PTR DS:[EAX+0x4],ESI
 *  006547BD   33F6             XOR ESI,ESI
 *  006547BF   8950 08          MOV DWORD PTR DS:[EAX+0x8],EDX
 *  006547C2   66:8B3451        MOV SI,WORD PTR DS:[ECX+EDX*2]
 *  006547C6   8970 04          MOV DWORD PTR DS:[EAX+0x4],ESI
 *  006547C9   42               INC EDX
 *  006547CA   33F6             XOR ESI,ESI
 *  006547CC   8950 08          MOV DWORD PTR DS:[EAX+0x8],EDX
 *  006547CF   66:8B3451        MOV SI,WORD PTR DS:[ECX+EDX*2]
 *  006547D3   42               INC EDX
 *  006547D4   8970 04          MOV DWORD PTR DS:[EAX+0x4],ESI
 *  006547D7   8950 08          MOV DWORD PTR DS:[EAX+0x8],EDX
 *  006547DA  ^EB BF            JMP SHORT malie.0065479B
 *  006547DC   83FA 02          CMP EDX,0x2
 *  006547DF   0F84 59010000    JE malie.0065493E
 *  006547E5   83FA 03          CMP EDX,0x3
 *  006547E8   75 12            JNZ SHORT malie.006547FC
 *  006547EA   8B50 08          MOV EDX,DWORD PTR DS:[EAX+0x8]
 *  006547ED   33F6             XOR ESI,ESI
 *  006547EF   66:8B3451        MOV SI,WORD PTR DS:[ECX+EDX*2]
 *  006547F3   42               INC EDX
 *  006547F4   8970 04          MOV DWORD PTR DS:[EAX+0x4],ESI
 *  006547F7   8950 08          MOV DWORD PTR DS:[EAX+0x8],EDX
 *  006547FA  ^EB 9F            JMP SHORT malie.0065479B
 *  006547FC   83FA 04          CMP EDX,0x4
 *  006547FF   0F84 39010000    JE malie.0065493E
 *  00654805   83FA 07          CMP EDX,0x7
 *  00654808   0F85 27010000    JNZ malie.00654935
 *  0065480E   8B50 08          MOV EDX,DWORD PTR DS:[EAX+0x8]
 *  00654811   33F6             XOR ESI,ESI
 *  00654813   66:8B3451        MOV SI,WORD PTR DS:[ECX+EDX*2]
 *  00654817   8970 04          MOV DWORD PTR DS:[EAX+0x4],ESI
 *  0065481A   8D72 01          LEA ESI,DWORD PTR DS:[EDX+0x1]
 *  0065481D   8B50 04          MOV EDX,DWORD PTR DS:[EAX+0x4]
 *  00654820   8970 08          MOV DWORD PTR DS:[EAX+0x8],ESI
 *  00654823   8D7A FF          LEA EDI,DWORD PTR DS:[EDX-0x1]
 *  00654826   83FF 3B          CMP EDI,0x3B
 *  00654829  ^0F87 79FFFFFF    JA malie.006547A8
 *  0065482F   33D2             XOR EDX,EDX
 *  00654831   8A97 9C496500    MOV DL,BYTE PTR DS:[EDI+0x65499C]
 *  00654837   FF2495 80496500  JMP DWORD PTR DS:[EDX*4+0x654980]
 *  0065483E   8B50 0C          MOV EDX,DWORD PTR DS:[EAX+0xC]
 *  00654841   85D2             TEST EDX,EDX
 *  00654843   0F8F 2B010000    JG malie.00654974
 *  00654849   33D2             XOR EDX,EDX
 *  0065484B   66:8B1471        MOV DX,WORD PTR DS:[ECX+ESI*2]
 *  0065484F   46               INC ESI
 *  00654850   85D2             TEST EDX,EDX
 *  00654852   8950 04          MOV DWORD PTR DS:[EAX+0x4],EDX
 *  00654855   8970 08          MOV DWORD PTR DS:[EAX+0x8],ESI
 *  00654858   0F84 E0000000    JE malie.0065493E
 *  0065485E   8B50 08          MOV EDX,DWORD PTR DS:[EAX+0x8]
 *  00654861   33F6             XOR ESI,ESI
 *  00654863   66:8B3451        MOV SI,WORD PTR DS:[ECX+EDX*2]
 *  00654867   42               INC EDX
 *  00654868   8950 08          MOV DWORD PTR DS:[EAX+0x8],EDX
 *  0065486B   8BD6             MOV EDX,ESI
 *  0065486D   85D2             TEST EDX,EDX
 *  0065486F   8970 04          MOV DWORD PTR DS:[EAX+0x4],ESI
 *  00654872  ^75 EA            JNZ SHORT malie.0065485E
 *  00654874   8B50 08          MOV EDX,DWORD PTR DS:[EAX+0x8]
 */
bool InsertMalie3Hook()
{
  // i.e. 8b44240456578b50088b0833f6668b345142
  const BYTE bytes[] = {
    // 0x90 nop
    0x8b,0x44,0x24, 0x04,   // 5b51e0  mov eax,dword ptr ss:[esp+0x4]   ; jichi: function starts
    0x56,                   // 5b51e4  push esi
    0x57,                   // 5b51e5  push edi
    0x8b,0x50, 0x08,        // 5b51e6  mov edx,dword ptr ds:[eax+0x8]
    0x8b,0x08,              // 5b51e9  mov ecx,dword ptr ds:[eax]
    0x33,0xf6,              // 5b51eb  xor esi,esi
    0x66,0x8b,0x34,0x51,    // 5b51ed  mov si,word ptr ds:[ecx+edx*2] // jichi: hook here
    0x42                    // 5b51f1  inc edx
  };
  enum {addr_offset = 0x5b51ed - 0x5b51e0};
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_limit_);
  if (!addr) {
    ConsoleOutput("vnreng:Malie3: pattern not found");
    return false;
  }
  HookParam hp = {};
  hp.address = addr + addr_offset;
  //GROWL(hp.address);
  //hp.address = 0x5b51ed;
  //hp.address = 0x5b51f1;
  //hp.address = 0x5b51f2;
  // jichi 3/15/2015: Remove 0704 in シルヴァリオ ヴェンッ�タ
  hp.filter_fun = IllegalWideCharsFilter; // remove illegal control chars such as 0x07,0x01
  hp.text_fun = SpecialHookMalie3;
  hp.type = USING_SPLIT|NO_CONTEXT|USING_UNICODE;
  //hp.filter_fun = Malie3Filter;
  ConsoleOutput("vnreng: INSERT Malie3");
  NewHook(hp, "Malie3");
  ConsoleOutput("vnreng:Malie3: disable GDI hooks");
  DisableGDIHooks();
  return true;
}

bool InsertMalie4Hook()
{
  // i.e. 50 8B 45 10 D9 9F ?? ?? ?? ?? 0F B7 04 58 50 51 E8 ?? ?? ?? ?? 8B 45 14 83 C4 10
  const BYTE bytes[] = {
    0x50,                   // 65904E | 50                       | push eax                                | mireado: pattern starts
    0x8B,0x45,0x10,			// 65904F | 8B 45 10                 | mov eax,dword ptr ss:[ebp+10]           |
    0xD9,0x9F,XX4,			// 659052 | D9 9F E8 6B 87 00        | fstp dword ptr ds:[edi+876BE8]          |
    0x0F,0xB7,0x04,0x58,	// 659058 | 0F B7 04 58              | movzx eax,word ptr ds:[eax+ebx*2]       |
    0x50,					// 65905C | 50                       | push eax                                |
    0x51,					// 65905D | 51                       | push ecx                                |
    0xE8,XX4, 				// 65905E | E8 DD 1D EA FF           | call malie.4FAE40                       | mireado: hook here
    0x8B,0x45,0x14,			// 659063 | 8B 45 14                 | mov eax,dword ptr ss:[ebp+14]           |
    0x83,0xC4,0x10			// 659066 | 83 C4 10                 | add esp,10                              |
  };
  enum {addr_offset = 0x65905E - 0x65904E};
  ULONG addr = MemDbg::matchBytes(bytes, sizeof(bytes), module_base_, module_limit_);
  if (!addr) {
    ConsoleOutput("vnreng:Malie4: pattern not found");
    return false;
  }
  
  HookParam hp = {};
  hp.address = addr + addr_offset;
  hp.offset = -8; // pusha_eax_off - 4
  hp.length_offset = 1;
  //hp.split = 0xc; // jichi 12/17/2013: Subcontext removed
  //hp.type = USING_SPLIT|USING_UNICODE|NO_CONTEXT;
  // jichi 12/17/2013: Need extern func for Electro Arms
  // Though the hook parameter is quit similar to Malie, the original extern function does not work
  hp.split = -0x10; // jichi 12/17/2013: This could split the furigana, but will mess up the text
  hp.type = USING_SPLIT|NO_CONTEXT|USING_UNICODE;
  ConsoleOutput("vnreng: INSERT Malie4");
  NewHook(hp, "Malie4");

  //GROWL_DWORD2(hp.address, reladdr);
  //RegisterEngineType(ENGINE_MALIE);
  return true;
}

// jichi 3/12/2015: Return guessed Malie engine year
//int GetMalieYear()
//{
//  if (Util::SearchResourceString(L"2013 light"))
//    return 2013;
//  if (Util::SearchResourceString(L"2014 light"))
//    return 2014;
//  return 2015;
//}

} // unnamed Malie

bool InsertMalieHook()
{
  if (IthCheckFile(L"tools.dll"))
    return InsertMalieHook1(); // jichi 3/5/2015: For old light games such as Dies irae.

  else { // For old Malie games before 2015
    // jichi 8/20/2013: Add hook for sweet light engine
    // Insert both malie and malie2 hook.
    bool ok = false;

    // jichi 3/12/2015: Disable MalieHook2 which will crash シルヴァリオ ヴェンッ�タ
    //if (!IthCheckFile(L"gdiplus.dll"))
    if (IthFindFile(L"System\\*")) { // Insert old Malie hook. There are usually System/cursor.cur
      ok = InsertMalieHook2() || ok;
      ok = InsertMalie2Hook() || ok; // jichi 8/20/2013
    }

    // The main disadvantage of Malie3 is that it cannot find character name
    ok = InsertMalie3Hook() || ok; // jichi 3/7/2014
    ok = InsertMalie4Hook() || ok; 

    if (ok) {
      ConsoleOutput("vnreng:Malie: disable GDI hooks");
      DisableGDIHooks();
    }
    return ok;
  }
}

/********************************************************************************************
EMEHook hook: (Contributed by Freaka)
  EmonEngine is used by LoveJuice company and TakeOut. Earlier builds were apparently
  called Runrun engine. String parsing varies a lot depending on the font settings and
  speed setting. E.g. without antialiasing (which very early versions did not have)
  uses TextOutA, fast speed triggers different functions then slow/normal. The user can
  set his own name and some odd control characters are used (0x09 for line break, 0x0D
  for paragraph end) which is parsed and put together on-the-fly while playing so script
  can't be read directly.
********************************************************************************************/
bool InsertEMEHook()
{
  ULONG addr = MemDbg::findCallAddress((ULONG)::IsDBCSLeadByte, module_base_, module_limit_);
  // no needed as first call to IsDBCSLeadByte is correct, but sig could be used for further verification
  //WORD sig = 0x51C3;
  //while (c && (*(WORD*)(c-2)!=sig))
  //{
  //  //-0x1000 as FindCallOrJmpAbs always uses an offset of 0x1000
  //  c = Util::FindCallOrJmpAbs((DWORD)IsDBCSLeadByte,module_limit_-c-0x1000+4,c-0x1000+4,false);
  //}
  if (!addr) {
    ConsoleOutput("vnreng:EME: pattern does not exist");
    return false;
  }
  HookParam hp = {};
  hp.address = addr;
  hp.offset = -0x8;
  hp.length_offset = 1;
  hp.type = NO_CONTEXT|DATA_INDIRECT;
  ConsoleOutput("vnreng: INSERT EmonEngine");
  NewHook(hp, "EmonEngine");
  //ConsoleOutput("EmonEngine, hook will only work with text speed set to slow or normal!");
  //else ConsoleOutput("Unknown EmonEngine engine");
  return true;
}
static void SpecialRunrunEngine(DWORD esp_base, HookParam *, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  CC_UNUSED(split);
  DWORD eax = regof(eax, esp_base), // *(DWORD *)(esp_base - 0x8),
        edx = regof(edx, esp_base); // *(DWORD *)(esp_base - 0x10);
  DWORD addr = eax + edx; // eax + edx
  *data = *(WORD *)(addr);
  *len = 2;
}
bool InsertRREHook()
{
  ULONG addr = MemDbg::findCallAddress((ULONG)::IsDBCSLeadByte, module_base_, module_limit_);
  if (!addr) {
    ConsoleOutput("vnreng:RRE: function call does not exist");
    return false;
  }
  WORD sig = 0x51c3;
  HookParam hp = {};
  hp.address = addr;
  hp.length_offset = 1;
  hp.type = NO_CONTEXT|DATA_INDIRECT;
  if ((*(WORD *)(addr-2) != sig)) {
    hp.text_fun = SpecialRunrunEngine;
    ConsoleOutput("vnreng: INSERT Runrun#1");
    NewHook(hp, "RunrunEngine Old");
  } else {
    hp.offset = -0x8;
    ConsoleOutput("vnreng: INSERT Runrun#2");
    NewHook(hp, "RunrunEngine");
  }
  return true;
  //ConsoleOutput("RunrunEngine, hook will only work with text speed set to slow or normal!");
  //else ConsoleOutput("Unknown RunrunEngine engine");
}
bool InsertMEDHook()
{
  for (DWORD i = module_base_; i < module_limit_ - 4; i++)
    if (*(DWORD *)i == 0x8175) //cmp *, 8175
      for (DWORD j = i, k = i + 0x100; j < k; j++)
        if (*(BYTE *)j == 0xe8) {
          DWORD t = j + 5 + *(DWORD *)(j + 1);
          if (t > module_base_ && t < module_limit_) {
            HookParam hp = {};
            hp.address = t;
            hp.offset = -0x8;
            hp.length_offset = 1;
            hp.type = BIG_ENDIAN;
            ConsoleOutput("vnreng: INSERT MED");
            NewHook(hp, "MED");
            //RegisterEngineType(ENGINE_MED);
            return true;
          }
        }

  //ConsoleOutput("Unknown MED engine.");
  ConsoleOutput("vnreng:MED: failed");
  return false;
}
/********************************************************************************************
AbelSoftware hook:
  The game folder usually is made up many no extended name files(file name doesn't have '.').
  And these files have common prefix which is the game name, and 2 digit in order.


********************************************************************************************/
/** 7/31/2015
 *  Sample game オタカ� *  Hooked address: 0x4413b0
 *
 *  GDI functions are cached: TextOutA and GetTextExtentPoint32A
 *
 *  004413AB   90               NOP
 *  004413AC   90               NOP
 *  004413AD   90               NOP
 *  004413AE   90               NOP
 *  004413AF   90               NOP
 *  004413B0   6A FF            PUSH -0x1   ; jichi: text in arg1, but text painted character by character
 *  004413B2   68 D0714900      PUSH .004971D0
 *  004413B7   64:A1 00000000   MOV EAX,DWORD PTR FS:[0]
 *  004413BD   50               PUSH EAX
 *  004413BE   64:8925 00000000 MOV DWORD PTR FS:[0],ESP
 *  004413C5   83EC 4C          SUB ESP,0x4C
 *  004413C8   A1 C00B4B00      MOV EAX,DWORD PTR DS:[0x4B0BC0]
 *  004413CD   53               PUSH EBX
 *  004413CE   55               PUSH EBP
 *  004413CF   56               PUSH ESI
 *  004413D0   57               PUSH EDI
 *  004413D1   8BF1             MOV ESI,ECX
 *  004413D3   894424 48        MOV DWORD PTR SS:[ESP+0x48],EAX
 *  004413D7   894424 4C        MOV DWORD PTR SS:[ESP+0x4C],EAX
 *  004413DB   894424 58        MOV DWORD PTR SS:[ESP+0x58],EAX
 *  004413DF   8B4424 6C        MOV EAX,DWORD PTR SS:[ESP+0x6C]
 *  004413E3   33DB             XOR EBX,EBX
 *  004413E5   50               PUSH EAX
 *  004413E6   8D4C24 4C        LEA ECX,DWORD PTR SS:[ESP+0x4C]
 *  004413EA   895C24 68        MOV DWORD PTR SS:[ESP+0x68],EBX
 *  004413EE   E8 74520400      CALL .00486667
 *  004413F3   8B4C24 78        MOV ECX,DWORD PTR SS:[ESP+0x78]
 *  004413F7   51               PUSH ECX
 *  004413F8   8D4C24 50        LEA ECX,DWORD PTR SS:[ESP+0x50]
 *  004413FC   E8 66520400      CALL .00486667
 *  00441401   8B5424 7C        MOV EDX,DWORD PTR SS:[ESP+0x7C]
 *  00441405   8D4C24 58        LEA ECX,DWORD PTR SS:[ESP+0x58]
 *  00441409   52               PUSH EDX
 *  0044140A   E8 58520400      CALL .00486667
 *  0044140F   8B4424 70        MOV EAX,DWORD PTR SS:[ESP+0x70]
 *  00441413   894424 50        MOV DWORD PTR SS:[ESP+0x50],EAX
 *  00441417   8B4424 74        MOV EAX,DWORD PTR SS:[ESP+0x74]
 *  0044141B   83F8 FF          CMP EAX,-0x1
 *  0044141E   75 06            JNZ SHORT .00441426
 *  00441420   895C24 54        MOV DWORD PTR SS:[ESP+0x54],EBX
 *  00441424   EB 2E            JMP SHORT .00441454
 *  00441426   8BC8             MOV ECX,EAX
 *  00441428   33D2             XOR EDX,EDX
 *  0044142A   81E1 FF000000    AND ECX,0xFF
 *  00441430   8AD4             MOV DL,AH
 *  00441432   81C9 00FFFFFF    OR ECX,0xFFFFFF00
 *  00441438   81E2 FF000000    AND EDX,0xFF
 *  0044143E   C1E1 08          SHL ECX,0x8
 *  00441441   0BCA             OR ECX,EDX
 *  00441443   C1E8 10          SHR EAX,0x10
 *  00441446   C1E1 08          SHL ECX,0x8
 *  00441449   25 FF000000      AND EAX,0xFF
 *  0044144E   0BC8             OR ECX,EAX
 *  00441450   894C24 54        MOV DWORD PTR SS:[ESP+0x54],ECX
 *  00441454   8B4424 48        MOV EAX,DWORD PTR SS:[ESP+0x48]
 *  00441458   3958 F8          CMP DWORD PTR DS:[EAX-0x8],EBX
 *  0044145B   0F84 7A030000    JE .004417DB
 *  00441461   8B8E 08020000    MOV ECX,DWORD PTR DS:[ESI+0x208]
 *  00441467   83F9 20          CMP ECX,0x20
 *  0044146A   0F8D 35030000    JGE .004417A5
 *  00441470   0FBE00           MOVSX EAX,BYTE PTR DS:[EAX]
 *  00441473   83E8 09          SUB EAX,0x9
 *  00441476   0F84 29030000    JE .004417A5
 *  0044147C   48               DEC EAX
 *  0044147D   0F84 0A030000    JE .0044178D
 *  00441483   83E8 03          SUB EAX,0x3
 *  00441486   0F84 19030000    JE .004417A5
 *  0044148C   8BBE 38010000    MOV EDI,DWORD PTR DS:[ESI+0x138]
 *  00441492   68 80C84A00      PUSH .004AC880
 *  00441497   8BCF             MOV ECX,EDI
 *  00441499   E8 E2E9FDFF      CALL .0041FE80
 *  0044149E   3BC3             CMP EAX,EBX
 *  004414A0   7D 0F            JGE SHORT .004414B1
 *  004414A2   53               PUSH EBX
 *  004414A3   53               PUSH EBX
 *  004414A4   53               PUSH EBX
 *  004414A5   53               PUSH EBX
 *  004414A6   8D4C24 48        LEA ECX,DWORD PTR SS:[ESP+0x48]
 *  004414AA   E8 916DFDFF      CALL .00418240
 *  004414AF   EB 06            JMP SHORT .004414B7
 *  004414B1   8B4F 24          MOV ECX,DWORD PTR DS:[EDI+0x24]
 *  004414B4   8B0481           MOV EAX,DWORD PTR DS:[ECX+EAX*4]
 *  004414B7   8B48 04          MOV ECX,DWORD PTR DS:[EAX+0x4]
 *  004414BA   8B10             MOV EDX,DWORD PTR DS:[EAX]
 *  004414BC   894C24 24        MOV DWORD PTR SS:[ESP+0x24],ECX
 *  004414C0   895424 20        MOV DWORD PTR SS:[ESP+0x20],EDX
 *  004414C4   8B50 08          MOV EDX,DWORD PTR DS:[EAX+0x8]
 *  004414C7   8B40 0C          MOV EAX,DWORD PTR DS:[EAX+0xC]
 *  004414CA   8D4C24 10        LEA ECX,DWORD PTR SS:[ESP+0x10]
 *  004414CE   895424 28        MOV DWORD PTR SS:[ESP+0x28],EDX
 *  004414D2   51               PUSH ECX
 *  004414D3   8BCE             MOV ECX,ESI
 *  004414D5   894424 30        MOV DWORD PTR SS:[ESP+0x30],EAX
 *  004414D9   E8 52F3FFFF      CALL .00440830
 *  004414DE   8B5424 50        MOV EDX,DWORD PTR SS:[ESP+0x50]
 *  004414E2   33C9             XOR ECX,ECX
 *  004414E4   894C24 78        MOV DWORD PTR SS:[ESP+0x78],ECX
 *  004414E8   B8 B0B64900      MOV EAX,.0049B6B0
 *  004414ED   3B10             CMP EDX,DWORD PTR DS:[EAX]
 *  004414EF   7E 0B            JLE SHORT .004414FC
 *  004414F1   83C0 04          ADD EAX,0x4
 *  004414F4   41               INC ECX
 *  004414F5   3D C0B64900      CMP EAX,.0049B6C0
 *  004414FA  ^72 F1            JB SHORT .004414ED
 *  004414FC   8B5424 48        MOV EDX,DWORD PTR SS:[ESP+0x48]
 *  00441500   8D4424 18        LEA EAX,DWORD PTR SS:[ESP+0x18]
 *  00441504   894C24 78        MOV DWORD PTR SS:[ESP+0x78],ECX
 *  00441508   8B4C8E 3C        MOV ECX,DWORD PTR DS:[ESI+ECX*4+0x3C]
 *  0044150C   52               PUSH EDX
 *  0044150D   50               PUSH EAX
 *  0044150E   E8 3D34FCFF      CALL .00404950
 *  00441513   8B46 38          MOV EAX,DWORD PTR DS:[ESI+0x38]
 *  00441516   895C24 70        MOV DWORD PTR SS:[ESP+0x70],EBX
 *  0044151A   3BC3             CMP EAX,EBX
 *  0044151C   0F84 F9000000    JE .0044161B
 *  00441522   8B50 08          MOV EDX,DWORD PTR DS:[EAX+0x8]
 *  00441525   8B4E 78          MOV ECX,DWORD PTR DS:[ESI+0x78]
 *  00441528   3BCA             CMP ECX,EDX
 *  0044152A   0F8D EB000000    JGE .0044161B
 *  00441530   8B50 04          MOV EDX,DWORD PTR DS:[EAX+0x4]
 *  00441533   8B4424 10        MOV EAX,DWORD PTR SS:[ESP+0x10]
 *  00441537   8B7E 74          MOV EDI,DWORD PTR DS:[ESI+0x74]
 *  0044153A   8B2C8A           MOV EBP,DWORD PTR DS:[EDX+ECX*4]
 *  0044153D   8B4C24 18        MOV ECX,DWORD PTR SS:[ESP+0x18]
 *  00441541   897C24 7C        MOV DWORD PTR SS:[ESP+0x7C],EDI
 *  00441545   8B55 00          MOV EDX,DWORD PTR SS:[EBP]
 *  00441548   8D1C01           LEA EBX,DWORD PTR DS:[ECX+EAX]
 *  0044154B   8BCD             MOV ECX,EBP
 *  0044154D   FF52 08          CALL DWORD PTR DS:[EDX+0x8]
 *  00441550   3BF8             CMP EDI,EAX
 *  00441552   0F8D C3000000    JGE .0044161B
 *  00441558   EB 04            JMP SHORT .0044155E
 *  0044155A   8B7C24 7C        MOV EDI,DWORD PTR SS:[ESP+0x7C]
 *  0044155E   8B45 00          MOV EAX,DWORD PTR SS:[EBP]
 *  00441561   57               PUSH EDI
 *  00441562   8BCD             MOV ECX,EBP
 *  00441564   FF50 04          CALL DWORD PTR DS:[EAX+0x4]
 *  00441567   8BF8             MOV EDI,EAX
 *  00441569   8BCF             MOV ECX,EDI
 *  0044156B   8B17             MOV EDX,DWORD PTR DS:[EDI]
 *  0044156D   FF52 0C          CALL DWORD PTR DS:[EDX+0xC]
 *  00441570   85C0             TEST EAX,EAX
 *  00441572   0F84 A3000000    JE .0044161B
 *  00441578   8B07             MOV EAX,DWORD PTR DS:[EDI]
 *  0044157A   8D4C24 6C        LEA ECX,DWORD PTR SS:[ESP+0x6C]
 *  0044157E   51               PUSH ECX
 *  0044157F   8BCF             MOV ECX,EDI
 *  00441581   FF50 10          CALL DWORD PTR DS:[EAX+0x10]
 *  00441584   8B5424 6C        MOV EDX,DWORD PTR SS:[ESP+0x6C]
 *  00441588   8B4C24 78        MOV ECX,DWORD PTR SS:[ESP+0x78]
 *  0044158C   8D4424 30        LEA EAX,DWORD PTR SS:[ESP+0x30]
 *  00441590   52               PUSH EDX
 *  00441591   8B4C8E 3C        MOV ECX,DWORD PTR DS:[ESI+ECX*4+0x3C]
 *  00441595   50               PUSH EAX
 *  00441596   C64424 6C 01     MOV BYTE PTR SS:[ESP+0x6C],0x1
 *  0044159B   E8 B033FCFF      CALL .00404950
 *  004415A0   8B10             MOV EDX,DWORD PTR DS:[EAX]
 *  004415A2   8B86 E4030000    MOV EAX,DWORD PTR DS:[ESI+0x3E4]
 *  004415A8   03DA             ADD EBX,EDX
 *  004415AA   8B5424 6C        MOV EDX,DWORD PTR SS:[ESP+0x6C]
 *  004415AE   52               PUSH EDX
 *  004415AF   50               PUSH EAX
 *  004415B0   E8 BB020000      CALL .00441870
 *  004415B5   83C4 08          ADD ESP,0x8
 *  004415B8   85C0             TEST EAX,EAX
 *  004415BA   74 08            JE SHORT .004415C4
 *  004415BC   3B5C24 28        CMP EBX,DWORD PTR SS:[ESP+0x28]
 *  004415C0   7F 43            JG SHORT .00441605
 *  004415C2   EB 18            JMP SHORT .004415DC
 *  004415C4   8B4C24 6C        MOV ECX,DWORD PTR SS:[ESP+0x6C]
 *  004415C8   8B86 E0030000    MOV EAX,DWORD PTR DS:[ESI+0x3E0]
 *  004415CE   51               PUSH ECX
 *  004415CF   50               PUSH EAX
 *  004415D0   E8 9B020000      CALL .00441870
 *  004415D5   83C4 08          ADD ESP,0x8
 *  004415D8   85C0             TEST EAX,EAX
 *  004415DA   74 31            JE SHORT .0044160D
 *  004415DC   8D4C24 6C        LEA ECX,DWORD PTR SS:[ESP+0x6C]
 *  004415E0   C64424 64 00     MOV BYTE PTR SS:[ESP+0x64],0x0
 *  004415E5   E8 404F0400      CALL .0048652A
 *  004415EA   8B7C24 7C        MOV EDI,DWORD PTR SS:[ESP+0x7C]
 *  004415EE   8B55 00          MOV EDX,DWORD PTR SS:[EBP]
 *  004415F1   47               INC EDI
 *  004415F2   8BCD             MOV ECX,EBP
 *  004415F4   897C24 7C        MOV DWORD PTR SS:[ESP+0x7C],EDI
 *  004415F8   FF52 08          CALL DWORD PTR DS:[EDX+0x8]
 *  004415FB   3BF8             CMP EDI,EAX
 *  004415FD  ^0F8C 57FFFFFF    JL .0044155A
 *  00441603   EB 16            JMP SHORT .0044161B
 *  00441605   C74424 70 010000>MOV DWORD PTR SS:[ESP+0x70],0x1
 *  0044160D   8D4C24 6C        LEA ECX,DWORD PTR SS:[ESP+0x6C]
 *  00441611   C64424 64 00     MOV BYTE PTR SS:[ESP+0x64],0x0
 *  00441616   E8 0F4F0400      CALL .0048652A
 *  0044161B   8B4424 10        MOV EAX,DWORD PTR SS:[ESP+0x10]
 *  0044161F   8B4C24 18        MOV ECX,DWORD PTR SS:[ESP+0x18]
 *  00441623   03C8             ADD ECX,EAX
 *  00441625   8B4424 28        MOV EAX,DWORD PTR SS:[ESP+0x28]
 *  00441629   3BC8             CMP ECX,EAX
 *  0044162B   7E 18            JLE SHORT .00441645
 *  0044162D   8B5424 48        MOV EDX,DWORD PTR SS:[ESP+0x48]
 *  00441631   8B86 E0030000    MOV EAX,DWORD PTR DS:[ESI+0x3E0]
 *  00441637   52               PUSH EDX
 *  00441638   50               PUSH EAX
 *  00441639   E8 32020000      CALL .00441870
 *  0044163E   83C4 08          ADD ESP,0x8
 *  00441641   85C0             TEST EAX,EAX
 *  00441643   74 08            JE SHORT .0044164D
 *  00441645   8B4424 70        MOV EAX,DWORD PTR SS:[ESP+0x70]
 *  00441649   85C0             TEST EAX,EAX
 *  0044164B   74 3F            JE SHORT .0044168C
 *  0044164D   8B8E 08020000    MOV ECX,DWORD PTR DS:[ESI+0x208]
 *  00441653   41               INC ECX
 *  00441654   8BC1             MOV EAX,ECX
 *  00441656   898E 08020000    MOV DWORD PTR DS:[ESI+0x208],ECX
 *  0044165C   83F8 20          CMP EAX,0x20
 *  0044165F   0F8D 40010000    JGE .004417A5
 *  00441665   83EC 10          SUB ESP,0x10
 *  00441668   8B15 D0B04A00    MOV EDX,DWORD PTR DS:[0x4AB0D0]
 *  0044166E   8BDC             MOV EBX,ESP
 *  00441670   33C0             XOR EAX,EAX
 *  00441672   8B3D D4B04A00    MOV EDI,DWORD PTR DS:[0x4AB0D4]
 *  00441678   33C9             XOR ECX,ECX
 *  0044167A   8903             MOV DWORD PTR DS:[EBX],EAX
 *  0044167C   894B 04          MOV DWORD PTR DS:[EBX+0x4],ECX
 *  0044167F   8BCE             MOV ECX,ESI
 *  00441681   8953 08          MOV DWORD PTR DS:[EBX+0x8],EDX
 *  00441684   897B 0C          MOV DWORD PTR DS:[EBX+0xC],EDI
 *  00441687   E8 7418FCFF      CALL .00402F00
 *  0044168C   8B86 08020000    MOV EAX,DWORD PTR DS:[ESI+0x208]
 *  00441692   6A 00            PUSH 0x0
 *  00441694   8D0CC5 00000000  LEA ECX,DWORD PTR DS:[EAX*8]
 *  0044169B   2BC8             SUB ECX,EAX
 *  0044169D   8B948E 78040000  MOV EDX,DWORD PTR DS:[ESI+ECX*4+0x478]
 *  004416A4   8DAC8E 70040000  LEA EBP,DWORD PTR DS:[ESI+ECX*4+0x470]
 *  004416AB   52               PUSH EDX
 *  004416AC   8BCD             MOV ECX,EBP
 *  004416AE   E8 7D8A0000      CALL .0044A130
 *  004416B3   8BD8             MOV EBX,EAX
 *  004416B5   8D4424 48        LEA EAX,DWORD PTR SS:[ESP+0x48]
 *  004416B9   50               PUSH EAX
 *  004416BA   8D7B 08          LEA EDI,DWORD PTR DS:[EBX+0x8]
 *  004416BD   8BCF             MOV ECX,EDI
 *  004416BF   E8 534F0400      CALL .00486617
 *  004416C4   8D4C24 4C        LEA ECX,DWORD PTR SS:[ESP+0x4C]
 *  004416C8   51               PUSH ECX
 *  004416C9   8D4F 04          LEA ECX,DWORD PTR DS:[EDI+0x4]
 *  004416CC   E8 464F0400      CALL .00486617
 *  004416D1   8B5424 50        MOV EDX,DWORD PTR SS:[ESP+0x50]
 *  004416D5   8D4C24 58        LEA ECX,DWORD PTR SS:[ESP+0x58]
 *  004416D9   8957 08          MOV DWORD PTR DS:[EDI+0x8],EDX
 *  004416DC   8B4424 54        MOV EAX,DWORD PTR SS:[ESP+0x54]
 *  004416E0   51               PUSH ECX
 *  004416E1   8D4F 10          LEA ECX,DWORD PTR DS:[EDI+0x10]
 *  004416E4   8947 0C          MOV DWORD PTR DS:[EDI+0xC],EAX
 *  004416E7   E8 2B4F0400      CALL .00486617
 *  004416EC   8B45 08          MOV EAX,DWORD PTR SS:[EBP+0x8]
 *  004416EF   85C0             TEST EAX,EAX
 *  004416F1   74 04            JE SHORT .004416F7
 *  004416F3   8918             MOV DWORD PTR DS:[EAX],EBX
 *  004416F5   EB 03            JMP SHORT .004416FA
 *  004416F7   895D 04          MOV DWORD PTR SS:[EBP+0x4],EBX
 *  004416FA   83EC 10          SUB ESP,0x10
 *  004416FD   895D 08          MOV DWORD PTR SS:[EBP+0x8],EBX
 *  00441700   8B4424 20        MOV EAX,DWORD PTR SS:[ESP+0x20]
 *  00441704   8B5424 28        MOV EDX,DWORD PTR SS:[ESP+0x28]
 *  00441708   8B7C24 2C        MOV EDI,DWORD PTR SS:[ESP+0x2C]
 *  0044170C   8BDC             MOV EBX,ESP
 *  0044170E   8D4C02 02        LEA ECX,DWORD PTR DS:[EDX+EAX+0x2]
 *  00441712   8B5424 24        MOV EDX,DWORD PTR SS:[ESP+0x24]
 *  00441716   8903             MOV DWORD PTR DS:[EBX],EAX
 *  00441718   8D7C3A 02        LEA EDI,DWORD PTR DS:[EDX+EDI+0x2]
 *  0044171C   8953 04          MOV DWORD PTR DS:[EBX+0x4],EDX
 *  0044171F   894B 08          MOV DWORD PTR DS:[EBX+0x8],ECX
 *  00441722   8BCE             MOV ECX,ESI
 *  00441724   897B 0C          MOV DWORD PTR DS:[EBX+0xC],EDI
 *  00441727   E8 D417FCFF      CALL .00402F00
 *  0044172C   8B4424 4C        MOV EAX,DWORD PTR SS:[ESP+0x4C]
 *  00441730   8B48 F8          MOV ECX,DWORD PTR DS:[EAX-0x8]
 *  00441733   85C9             TEST ECX,ECX
 *  00441735   74 6E            JE SHORT .004417A5
 *  00441737   8B4E 3C          MOV ECX,DWORD PTR DS:[ESI+0x3C]
 *  0044173A   50               PUSH EAX
 *  0044173B   8D4424 24        LEA EAX,DWORD PTR SS:[ESP+0x24]
 *  0044173F   50               PUSH EAX
 *  00441740   E8 0B32FCFF      CALL .00404950
 *  00441745   8B5C24 20        MOV EBX,DWORD PTR SS:[ESP+0x20]
 *  00441749   8B4C24 18        MOV ECX,DWORD PTR SS:[ESP+0x18]
 *  0044174D   8B7C24 24        MOV EDI,DWORD PTR SS:[ESP+0x24]
 *  00441751   8BC3             MOV EAX,EBX
 *  00441753   2BC1             SUB EAX,ECX
 *  00441755   8BCF             MOV ECX,EDI
 *  00441757   99               CDQ
 *  00441758   2BC2             SUB EAX,EDX
 *  0044175A   8B5424 14        MOV EDX,DWORD PTR SS:[ESP+0x14]
 *  0044175E   F7D9             NEG ECX
 *  00441760   D1F8             SAR EAX,1
 *  00441762   03CA             ADD ECX,EDX
 *  00441764   8B5424 10        MOV EDX,DWORD PTR SS:[ESP+0x10]
 *  00441768   F7D8             NEG EAX
 *  0044176A   03C2             ADD EAX,EDX
 *  0044176C   83EC 10          SUB ESP,0x10
 *  0044176F   8D7C39 02        LEA EDI,DWORD PTR DS:[ECX+EDI+0x2]
 *  00441773   8D5418 02        LEA EDX,DWORD PTR DS:[EAX+EBX+0x2]
 *  00441777   8BDC             MOV EBX,ESP
 *  00441779   8903             MOV DWORD PTR DS:[EBX],EAX
 *  0044177B   894B 04          MOV DWORD PTR DS:[EBX+0x4],ECX
 *  0044177E   8BCE             MOV ECX,ESI
 *  00441780   8953 08          MOV DWORD PTR DS:[EBX+0x8],EDX
 *  00441783   897B 0C          MOV DWORD PTR DS:[EBX+0xC],EDI
 *  00441786   E8 7517FCFF      CALL .00402F00
 *  0044178B   EB 18            JMP SHORT .004417A5
 *  0044178D   8D41 29          LEA EAX,DWORD PTR DS:[ECX+0x29]
 *  00441790   8D14C5 00000000  LEA EDX,DWORD PTR DS:[EAX*8]
 *  00441797   2BD0             SUB EDX,EAX
 *  00441799   391C96           CMP DWORD PTR DS:[ESI+EDX*4],EBX
 *  0044179C   74 07            JE SHORT .004417A5
 *  0044179E   41               INC ECX
 *  0044179F   898E 08020000    MOV DWORD PTR DS:[ESI+0x208],ECX
 *  004417A5   8B86 E8020000    MOV EAX,DWORD PTR DS:[ESI+0x2E8]
 *  004417AB   33DB             XOR EBX,EBX
 *  004417AD   3BC3             CMP EAX,EBX
 *  004417AF   74 2A            JE SHORT .004417DB
 *  004417B1   399E C8030000    CMP DWORD PTR DS:[ESI+0x3C8],EBX
 *  004417B7   75 22            JNZ SHORT .004417DB
 *  004417B9   8B86 C4030000    MOV EAX,DWORD PTR DS:[ESI+0x3C4]
 *  004417BF   8BCE             MOV ECX,ESI
 *  004417C1   50               PUSH EAX
 *  004417C2   E8 89040000      CALL .00441C50
 *  004417C7   3B86 3C020000    CMP EAX,DWORD PTR DS:[ESI+0x23C]
 *  004417CD   74 06            JE SHORT .004417D5
 *  004417CF   8986 38020000    MOV DWORD PTR DS:[ESI+0x238],EAX
 *  004417D5   8986 3C020000    MOV DWORD PTR DS:[ESI+0x23C],EAX
 *  004417DB   399E 30020000    CMP DWORD PTR DS:[ESI+0x230],EBX
 *  004417E1   75 3C            JNZ SHORT .0044181F
 *  004417E3   8BCE             MOV ECX,ESI
 *  004417E5   E8 C6040000      CALL .00441CB0
 *  004417EA   85C0             TEST EAX,EAX
 *  004417EC   75 31            JNZ SHORT .0044181F
 *  004417EE   399E 18020000    CMP DWORD PTR DS:[ESI+0x218],EBX
 *  004417F4   74 29            JE SHORT .0044181F
 *  004417F6   83BE C4020000 64 CMP DWORD PTR DS:[ESI+0x2C4],0x64
 *  004417FD   74 20            JE SHORT .0044181F
 *  004417FF   8B86 08020000    MOV EAX,DWORD PTR DS:[ESI+0x208]
 *  00441805   83F8 20          CMP EAX,0x20
 *  00441808   7D 1D            JGE SHORT .00441827
 *  0044180A   83C0 29          ADD EAX,0x29
 *  0044180D   8D0CC5 00000000  LEA ECX,DWORD PTR DS:[EAX*8]
 *  00441814   2BC8             SUB ECX,EAX
 *  00441816   391C8E           CMP DWORD PTR DS:[ESI+ECX*4],EBX
 *  00441819   74 0C            JE SHORT .00441827
 *  0044181B   6A 01            PUSH 0x1
 *  0044181D   EB 01            JMP SHORT .00441820
 *  0044181F   53               PUSH EBX
 *  00441820   8BCE             MOV ECX,ESI
 *  00441822   E8 49C5FEFF      CALL .0042DD70
 *  00441827   8D4C24 58        LEA ECX,DWORD PTR SS:[ESP+0x58]
 *  0044182B   C74424 64 030000>MOV DWORD PTR SS:[ESP+0x64],0x3
 *  00441833   E8 F24C0400      CALL .0048652A
 *  00441838   8D4C24 4C        LEA ECX,DWORD PTR SS:[ESP+0x4C]
 *  0044183C   C64424 64 02     MOV BYTE PTR SS:[ESP+0x64],0x2
 *  00441841   E8 E44C0400      CALL .0048652A
 *  00441846   8D4C24 48        LEA ECX,DWORD PTR SS:[ESP+0x48]
 *  0044184A   C74424 64 FFFFFF>MOV DWORD PTR SS:[ESP+0x64],-0x1
 *  00441852   E8 D34C0400      CALL .0048652A
 *  00441857   8B4C24 5C        MOV ECX,DWORD PTR SS:[ESP+0x5C]
 *  0044185B   5F               POP EDI
 *  0044185C   5E               POP ESI
 *  0044185D   5D               POP EBP
 *  0044185E   64:890D 00000000 MOV DWORD PTR FS:[0],ECX
 *  00441865   5B               POP EBX
 *  00441866   83C4 58          ADD ESP,0x58
 *  00441869   C2 1400          RETN 0x14
 *  0044186C   90               NOP
 *  0044186D   90               NOP
 *  0044186E   90               NOP
 *  0044186F   90               NOP
 *
 *  Another sample game: 不条琸�界の探偵令嬢
 */
bool InsertAbelHook()
{
  // jichi: If this pattern failed again, try the following pattern instead:
  // 004413D3   894424 48        MOV DWORD PTR SS:[ESP+0x48],EAX
  // 004413D7   894424 4C        MOV DWORD PTR SS:[ESP+0x4C],EAX
  // 004413DB   894424 58        MOV DWORD PTR SS:[ESP+0x58],EAX

  const DWORD character[] = {0xc981d48a, 0xffffff00};
  if (DWORD j = SearchPattern(module_base_, module_limit_ - module_base_, character, sizeof(character))) {
    j += module_base_;
    for (DWORD i = j - 0x100; j > i; j--)
      if (*(WORD *)j == 0xff6a) {
        HookParam hp = {};
        hp.address = j;
        hp.offset = 4; // text in arg1
        hp.type = USING_STRING|NO_CONTEXT;
        ConsoleOutput("vnreng: INSERT Abel");
        //GROWL_DWORD(hp.address);
        NewHook(hp, "Abel");
        //RegisterEngineType(ENGINE_ABEL);
        return true;
      }
  }
  ConsoleOutput("vnreng:Abel: failed");
  return false;
}
bool InsertLiveDynamicHook(LPVOID addr, DWORD frame, DWORD stack)
{
  if (addr != ::GetGlyphOutlineA || !frame)
    return false;
  DWORD k = *(DWORD *)frame;
  k = *(DWORD *)(k + 4);
  if (*(BYTE *)(k - 5) != 0xe8)
    k = *(DWORD *)(frame + 4);
  DWORD j = k + *(DWORD *)(k - 4);
  if (j > module_base_ && j < module_limit_) {
    HookParam hp = {};
    hp.address = j;
    hp.offset = -0x10;
    hp.length_offset = 1;
    hp.type = BIG_ENDIAN;
    ConsoleOutput("vnreng: INSERT DynamicLive");
    NewHook(hp, "Live");
    //RegisterEngineType(ENGINE_LIVE);
    return true;
  }
  ConsoleOutput("vnreng:DynamicLive: failed");
  return true; // jichi 12/25/2013: return true
}
//void InsertLiveHook()
//{
//  ConsoleOutput("Probably Live. Wait for text.");
//  trigger_fun_=InsertLiveDynamicHook;
//  SwitchTrigger(true);
//}
bool InsertLiveHook()
{
  const BYTE ins[] = {0x64,0x89,0x20,0x8b,0x45,0x0c,0x50};
  ULONG addr = MemDbg::findBytes(ins, sizeof(ins), module_base_, module_limit_);
  if (!addr) {
    ConsoleOutput("vnreng:Live: pattern not found");
    return false;
  }
  HookParam hp = {};
  hp.address = addr;
  hp.offset = -0x10;
  hp.length_offset = 1;
  hp.type = BIG_ENDIAN;
  ConsoleOutput("vnreng: INSERT Live");
  NewHook(hp, "Live");
  //RegisterEngineType(ENGINE_LIVE);
  //else ConsoleOutput("Unknown Live engine");
  return true;
}

void InsertBrunsHook()
{
  if (IthCheckFile(L"libscr.dll")) {
    HookParam hp = {};
    hp.offset = 4;
    hp.length_offset = 1;
    hp.type = USING_UNICODE|MODULE_OFFSET|FUNCTION_OFFSET;
    // jichi 12/27/2013: This function does not work for the latest bruns games anymore
    hp.function = 0x8b24c7bc;
    //?push_back@?$basic_string@GU?$char_traits@G@std@@V?$allocator@G@2@@std@@QAEXG@Z
    if (IthCheckFile(L"msvcp90.dll"))
      hp.module = 0xc9c36a5b; // 3385027163
    else if (IthCheckFile(L"msvcp80.dll"))
      hp.module = 0xa9c36a5b; // 2848156251
    else if (IthCheckFile(L"msvcp100.dll")) // jichi 8/17/2013: MSVCRT 10.0 and 11.0
      hp.module = 0xb571d760; // 3044136800;
    else if (IthCheckFile(L"msvcp110.dll"))
      hp.module = 0xd571d760; // 3581007712;
    if (hp.module) {
      ConsoleOutput("vnreng: INSERT Brus#1");
      NewHook(hp, "Bruns");
    }
  }
  //else
  // jichi 12/21/2013: Keep both bruns hooks
  // The first one does not work for games like 「オーク・キングダマモン娘繁殖�豚人王~�anymore.
  {
    union {
      DWORD i;
      DWORD *id;
      WORD *iw;
      BYTE *ib;
    };
    DWORD k = module_limit_ - 4;
    for (i = module_base_ + 0x1000; i < k; i++) {
      if (*id != 0xff) //cmp reg,0xff
        continue;
      i += 4;
      if (*iw != 0x8f0f)
        continue;//jg
      i += 2;
      i += *id + 4;
      for (DWORD j = i + 0x40; i < j; i++) {
        if (*ib != 0xe8)
          continue;
        i++;
        DWORD t = i + 4 + *id;
        if (t > module_base_ && t <module_limit_) {
          i = t;
          for (j = i + 0x80; i < j; i++) {
            if (*ib != 0xe8)
              continue;
            i++;
            t = i + 4 + *id;
            if (t > module_base_ && t <module_limit_) {

              HookParam hp = {};
              hp.address = t;
              hp.offset = 4;
              hp.length_offset = 1;
              hp.type = USING_UNICODE|DATA_INDIRECT;
              ConsoleOutput("vnreng: INSERT Brus#2");
              NewHook(hp, "Bruns2");
              return;
            }
          }
          k = i; //Terminate outer loop.
          break; //Terminate inner loop.
        }
      }
    }
  }
  //ConsoleOutput("Unknown Bruns engine.");
  ConsoleOutput("vnreng:Brus: failed");
}

/**
 * jichi 8/18/2013:  QLIE identified by GameData/data0.pack
 *
 * The old hook cannot recognize new games.
 */

namespace { // unnamed QLIE
/**
 * jichi 8/18/2013: new QLIE hook
 * See: http://www.hongfire.com/forum/showthread.php/420362-QLIE-engine-Hcode
 *
 * Ins:
 * 55 8B EC 53 8B 5D 1C
 * - 55         push ebp    ; hook here
 * - 8bec       mov ebp, esp
 * - 53         push ebx
 * - 8B5d 1c    mov ebx, dword ptr ss:[ebp+1c]
 *
 * /HBN14*0@4CC2C4
 * - addr: 5030596  (0x4cc2c4)
 * - text_fun: 0x0
 * - function: 0
 * - hook_len: 0
 * - ind: 0
 * - length_offset: 1
 * - module: 0
 * - off: 20    (0x14)
 * - recover_len: 0
 * - split: 0
 * - split_ind: 0
 * - type: 1032 (0x408)
 */
bool InsertQLIE2Hook()
{
  const BYTE bytes[] = { // size = 7
    0x55,           // 55       push ebp    ; hook here
    0x8b,0xec,      // 8bec     mov ebp, esp
    0x53,           // 53       push ebx
    0x8b,0x5d, 0x1c // 8b5d 1c  mov ebx, dword ptr ss:[ebp+1c]
  };
  //enum { addr_offset = 0 }; // current instruction is the first one
  ULONG range = min(module_limit_ - module_base_, MAX_REL_ADDR);
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_base_ + range);
  if (!addr) {
    ConsoleOutput("vnreng:QLIE2: pattern not found");
    //ConsoleOutput("Not QLIE2");
    return false;
  }

  HookParam hp = {};
  hp.type = DATA_INDIRECT|NO_CONTEXT; // 0x408
  hp.length_offset = 1;
  hp.offset = 0x14;
  hp.address = addr;

  ConsoleOutput("vnreng: INSERT QLIE2");
  NewHook(hp, "QLiE2");
  //ConsoleOutput("QLIE2");
  return true;
}

// jichi: 8/18/2013: Change return type to bool
bool InsertQLIE1Hook()
{
  for (DWORD i = module_base_ + 0x1000; i < module_limit_ - 4; i++)
    if (*(DWORD *)i == 0x7ffe8347) { // inc edi, cmp esi,7f
      DWORD t = 0;
      for (DWORD j = i; j < i + 0x10; j++) {
        if (*(DWORD *)j == 0xa0) { // cmp esi,a0
          t = 1;
          break;
        }
      }
      if (t)
        for (DWORD j = i; j > i - 0x100; j--)
          if (*(DWORD *)j == 0x83ec8b55) { // push ebp, mov ebp,esp, sub esp,*
            HookParam hp = {};
            hp.address = j;
            hp.offset = 0x18;
            hp.split = -0x18;
            hp.length_offset = 1;
            hp.type = DATA_INDIRECT|USING_SPLIT;
            ConsoleOutput("vnreng: INSERT QLIE1");
            NewHook(hp, "QLiE");
            //RegisterEngineType(ENGINE_FRONTWING);
            return true;
          }
    }

  ConsoleOutput("vnreng:QLIE1: failed");
  //ConsoleOutput("Unknown QLIE engine");
  return false;
}

} // unnamed QLIE

// jichi 8/18/2013: Add new hook
bool InsertQLIEHook()
{ return InsertQLIE1Hook() || InsertQLIE2Hook(); }

/********************************************************************************************
CandySoft hook:
  Game folder contains many *.fpk. Engine name is SystemC.
  I haven't seen this engine in other company/brand.

  AGTH /X3 will hook lstrlenA. One thread is the exactly result we want.
  But the function call is difficult to located programmatically.
  I find a equivalent points which is more easy to search.
  The script processing function needs to find 0x5B'[',
  so there should a instruction like cmp reg,5B
  Find this position and navigate to function entry.
  The first parameter is the string pointer.
  This approach works fine with game later than つよきす2学�

  But the original つよき�is quite different. I handle this case separately.

********************************************************************************************/

namespace { // unnamed Candy

// jichi 8/23/2013: split into two different engines
//if (_wcsicmp(process_name_, L"systemc.exe")==0)
// Process name is "SystemC.exe"
bool InsertCandyHook1()
{
  for (DWORD i = module_base_ + 0x1000; i < module_limit_ - 4; i++)
    if ((*(DWORD *)i&0xffffff) == 0x24f980) // cmp cl,24
      for (DWORD j = i, k = i - 0x100; j > k; j--)
        if (*(DWORD *)j == 0xc0330a8a) { // mov cl,[edx]; xor eax,eax
          HookParam hp = {};
          hp.address = j;
          hp.offset = -0x10;    // jichi: text in ecx
          hp.type = USING_STRING;
          ConsoleOutput("vnreng: INSERT SystemC#1");
          NewHook(hp, "SystemC");
          //RegisterEngineType(ENGINE_CANDY);
          return true;
        }
  ConsoleOutput("vnreng:CandyHook1: failed");
  return false;
}

// jichi 8/23/2013: Process name is NOT "SystemC.exe"
bool InsertCandyHook2()
{
  for (DWORD i = module_base_ + 0x1000; i < module_limit_ - 4 ;i++)
    if (*(WORD *)i == 0x5b3c || // cmp al,0x5b
        (*(DWORD *)i & 0xfff8fc) == 0x5bf880) // cmp reg,0x5B
      for (DWORD j = i, k = i - 0x100; j > k; j--)
        if ((*(DWORD *)j & 0xffff) == 0x8b55) { // push ebp, mov ebp,esp, sub esp,*
          HookParam hp = {};
          hp.address = j;
          hp.offset = 4;    // jichi: text in arg1
          hp.type = USING_STRING;
          ConsoleOutput("vnreng: INSERT SystemC#2");
          NewHook(hp, "SystemC");
          //RegisterEngineType(ENGINE_CANDY);
          return true;
        }
  ConsoleOutput("vnreng:CandyHook2: failed");
  return false;
}

/** jichi 10/2/2013: CHECKPOINT
 *
 *  [5/31/2013] 恋もHもお勉強も、おまかせ�お姉ちも�部
 *  base = 0xf20000
 *  + シナリオ: /HSN-4@104A48:ANEBU.EXE
 *    - off: 4294967288 = 0xfffffff8 = -8
 ,    - type: 1025 = 0x401
 *  + 選択肢: /HSN-4@104FDD:ANEBU.EXE
 *    - off: 4294967288 = 0xfffffff8 = -8
 *    - type: 1089 = 0x441
 */
//bool InsertCandyHook3()
//{
//  return false; // CHECKPOINT
//  const BYTE ins[] = {
//    0x83,0xc4, 0x0c, // add esp,0xc ; hook here
//    0x0f,0xb6,0xc0,  // movzx eax,al
//    0x85,0xc0,       // test eax,eax
//    0x75, 0x0e       // jnz XXOO ; it must be 0xe, or there will be duplication
//  };
//  enum { addr_offset = 0 };
//  ULONG range = min(module_limit_ - module_base_, MAX_REL_ADDR);
//  ULONG reladdr = SearchPattern(module_base_, range, ins, sizeof(ins));
//  reladdr = 0x104a48;
//  GROWL_DWORD(module_base_);
//  //GROWL_DWORD3(reladdr, module_base_, range);
//  if (!reladdr)
//    return false;
//
//  HookParam hp = {};
//  hp.address = module_base_ + reladdr + addr_offset;
//  hp.offset = -8;
//  hp.type = USING_STRING|NO_CONTEXT;
//  NewHook(hp, "Candy");
//  return true;
//}

} // unnamed Candy

// jichi 10/2/2013: Add new candy hook
bool InsertCandyHook()
{
  //if (0 == _wcsicmp(process_name_, L"systemc.exe"))
  if (IthCheckFile(L"SystemC.exe"))
    return InsertCandyHook1();
  else
    return InsertCandyHook2();
    //bool b2 = InsertCandyHook2(),
    //     b3 = InsertCandyHook3();
    //return b2 || b3;
}

/********************************************************************************************
Apricot hook:
  Game folder contains arc.a*.
  This engine is heavily based on new DirectX interfaces.
  I can't find a good place where text is clean and not repeating.
  The game processes script encoded in UTF32-like format.
  I reversed the parsing algorithm of the game and implemented it partially.
  Only name and text data is needed.

********************************************************************************************/

/** jichi 2/15/2015: ApricoT
 *
 *  Sample game: イセカイ・ラヴァーズ�体験版
 *  Issue of the old game is that it uses esp as split, and hence has relative address
 *
 *  00978100   5b               pop ebx
 *  00978101   83c4 2c          add esp,0x2c
 *  00978104   c2 0400          retn 0x4
 *  00978107   33c0             xor eax,eax ; jichi: hook here
 *  00978109   bb 03000000      mov ebx,0x3
 *  0097810e   895c24 30        mov dword ptr ss:[esp+0x30],ebx
 *  00978112   894424 2c        mov dword ptr ss:[esp+0x2c],eax
 *  00978116   894424 1c        mov dword ptr ss:[esp+0x1c],eax
 *  0097811a   8b4e 34          mov ecx,dword ptr ds:[esi+0x34]
 *  0097811d   3b4e 3c          cmp ecx,dword ptr ds:[esi+0x3c]
 *  00978120   894424 3c        mov dword ptr ss:[esp+0x3c],eax
 *  00978124   7e 3b            jle short .00978161
 *  00978126   8b7e 3c          mov edi,dword ptr ds:[esi+0x3c]
 *  00978129   3b7e 34          cmp edi,dword ptr ds:[esi+0x34]
 *  0097812c   76 05            jbe short .00978133
 *  0097812e   e8 01db1500      call .00ad5c34
 *  00978133   837e 38 04       cmp dword ptr ds:[esi+0x38],0x4
 *  00978137   72 05            jb short .0097813e
 *  00978139   8b46 24          mov eax,dword ptr ds:[esi+0x24]
 *  0097813c   eb 03            jmp short .00978141
 *  0097813e   8d46 24          lea eax,dword ptr ds:[esi+0x24]
 *  00978141   8b3cb8           mov edi,dword ptr ds:[eax+edi*4]
 *  00978144   016e 3c          add dword ptr ds:[esi+0x3c],ebp
 *  00978147   57               push edi
 *  00978148   55               push ebp
 *  00978149   8d4c24 20        lea ecx,dword ptr ss:[esp+0x20]
 *  0097814d   e8 de05feff      call .00958730
 *
 *  Sample stack: baseaddr = 0c90000
 *  001aec2c   ede50fbb
 *  001aec30   0886064c
 *  001aec34   08860bd0
 *  001aec38   08860620
 *  001aec3c   00000000
 *  001aec40   00000000
 *  001aec44   08860bd0
 *  001aec48   001aee18
 *  001aec4c   08860620
 *  001aec50   00000000
 *  001aec54   00cb4408  return to .00cb4408 from .00c973e0
 *  001aec58   08860bd8
 *  001aec5c   00000000
 *  001aec60   001aefd8  pointer to next seh record
 *  001aec64   00e47d88  se handler
 *  001aec68   ffffffff
 *  001aec6c   00cb9f40  return to .00cb9f40 from .00cc8030 ; jichi: split here
 */
static void SpecialHookApricoT(DWORD esp_base, HookParam *, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  DWORD reg_esi = *(DWORD *)(esp_base - 0x20);
  DWORD base = *(DWORD *)(reg_esi + 0x24);
  DWORD index = *(DWORD *)(reg_esi + 0x3c);
  DWORD *script = (DWORD *)(base + index * 4);
  // jichi 2/14/2015
  // Change reg_esp to the return address
  //DWORD reg_esp = regof(esp, esp_base);
  //*split = reg_esp;
  //*split = regof(esp, esp_base);
  DWORD arg = argof(16, esp_base); // return address
  *split = arg > module_base_ ? arg - module_base_ : arg; // use relative split value
  //*split = argof(1, esp_base);
  if (script[0] == L'<') {
    DWORD *end;
    for (end = script; *end != L'>'; end++); // jichi 2/14/2015: i.e. = ::wcschr(script) or script
    switch (script[1]) {
    case L'N':
      if (script[2] == L'a' && script[3] == L'm' && script[4] == L'e') {
        buffer_index = 0;
        for (script += 5; script < end; script++)
          if (*script > 0x20)
            wc_buffer[buffer_index++] = *script & 0xFFFF;
        *len = buffer_index<<1;
        *data = (DWORD)wc_buffer;
        // jichi 1/4/2014: The way I save subconext is not able to distinguish the split value
        // Change to shift 16
        //*split |= 1 << 31;
        *split |= 1 << 16; // jichi: differentiate name and text script
      } break;
    case L'T':
      if (script[2] == L'e' && script[3] == L'x' && script[4] == L't') {
        buffer_index = 0;
        for (script += 5; script < end; script++) {
          if (*script > 0x40) {
            while (*script == L'{') {
              script++;
              while (*script!=L'\\') {
                wc_buffer[buffer_index++] = *script & 0xffff;
                script++;
              }
              while (*script++!=L'}');
            }
            wc_buffer[buffer_index++] = *script & 0xffff;
          }
        }
        *len = buffer_index << 1;
        *data = (DWORD)wc_buffer;
      } break;
    }
  }
}

bool InsertApricoTHook()
{
  for (DWORD i = module_base_ + 0x1000; i < module_limit_ - 4; i++)
    if ((*(DWORD *)i & 0xfff8fc) == 0x3cf880)  // cmp reg,0x3c
      for (DWORD j = i + 3, k = i + 0x100; j < k; j++)
        if ((*(DWORD *)j & 0xffffff) == 0x4c2) { // retn 4
          HookParam hp = {};
          hp.address = j + 3;
          hp.text_fun = SpecialHookApricoT;
          hp.type = USING_STRING|NO_CONTEXT|USING_UNICODE;
          ConsoleOutput("vnreng: INSERT ApricoT");
          //GROWL_DWORD3(hp.address, module_base_, module_limit_);
          NewHook(hp, "ApRicoT");
          //RegisterEngineType(ENGINE_APRICOT);
          // jichi 2/14/2015: disable cached GDI functions
          ConsoleOutput("vnreng:ApRicoT: disable GDI hooks");
          DisableGDIHooks();
          return true;
        }

  ConsoleOutput("vnreng:ApricoT: failed");
  return false;
}
void InsertStuffScriptHook()
{
  // BOOL GetTextExtentPoint32(
  //   _In_   HDC hdc,
  //   _In_   LPCTSTR lpString,
  //   _In_   int c,
  //   _Out_  LPSIZE lpSize
  // );
  HookParam hp = {};
  hp.address = (DWORD)::GetTextExtentPoint32A;
  hp.offset = 0x8; // arg2 lpString
  hp.split = -0x18; // jichi 8/12/2014: = -4 - pusha_esp_off
  hp.type = USING_STRING | USING_SPLIT;
  ConsoleOutput("vnreng: INSERT StuffScriptEngine");
  NewHook(hp, "StuffScriptEngine");
  //RegisterEngine(ENGINE_STUFFSCRIPT);
}
bool InsertTriangleHook()
{
  for (DWORD i = module_base_; i < module_limit_ - 4; i++)
    if ((*(DWORD *)i & 0xffffff) == 0x75403c) // cmp al,0x40; jne
      for (DWORD j = i + 4 + *(BYTE*)(i+3), k = j + 0x20; j < k; j++)
        if (*(BYTE*)j == 0xe8) {
          DWORD t = j + 5 + *(DWORD *)(j + 1);
          if (t > module_base_ && t < module_limit_) {
            HookParam hp = {};
            hp.address = t;
            hp.offset = 4;
            hp.type = USING_STRING;
            ConsoleOutput("vnreng: INSERT Triangle");
            NewHook(hp, "Triangle");
            //RegisterEngineType(ENGINE_TRIANGLE);
            return true;
          }
        }
  //ConsoleOutput("Old/Unknown Triangle engine.");
  ConsoleOutput("vnreng:Triangle: failed");
  return false;
}
bool InsertPensilHook()
{
  for (DWORD i = module_base_; i < module_limit_ - 4; i++)
    if (*(DWORD *)i == 0x6381) // cmp *,8163
      if (DWORD j = SafeFindEntryAligned(i, 0x100)) {
        HookParam hp = {};
        hp.address = j;
        hp.offset = 8;
        hp.length_offset = 1;
        ConsoleOutput("vnreng: INSERT Pensil");
        NewHook(hp, "Pensil");
        return true;
        //RegisterEngineType(ENGINE_PENSIL);
      }
  //ConsoleOutput("Unknown Pensil engine.");
  ConsoleOutput("vnreng:Pensil: failed");
  return false;
}
#if 0 // jich 3/8/2015: disabled
bool IsPensilSetup()
{
  HANDLE hFile = IthCreateFile(L"PSetup.exe", FILE_READ_DATA, FILE_SHARE_READ, FILE_OPEN);
  FILE_STANDARD_INFORMATION info;
  IO_STATUS_BLOCK ios;
  LPVOID buffer = nullptr;
  NtQueryInformationFile(hFile, &ios, &info, sizeof(info), FileStandardInformation);
  NtAllocateVirtualMemory(NtCurrentProcess(), &buffer, 0,
      &info.AllocationSize.LowPart, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
  NtReadFile(hFile, 0,0,0, &ios, buffer, info.EndOfFile.LowPart, 0, 0);
  NtClose(hFile);
  BYTE *b = (BYTE *)buffer;
  DWORD len = info.EndOfFile.LowPart & ~1;
  if (len == info.AllocationSize.LowPart)
    len -= 2;
  b[len] = 0;
  b[len + 1] = 0;
  bool ret = wcsstr((LPWSTR)buffer, L"PENSIL") || wcsstr((LPWSTR)buffer, L"Pensil");
  NtFreeVirtualMemory(NtCurrentProcess(), &buffer, &info.AllocationSize.LowPart, MEM_RELEASE);
  return ret;
}
#endif // if 0
namespace { // unnamed
void SpecialHookDebonosuScenario(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  DWORD retn = *(DWORD *)esp_base;
  if (*(WORD *)retn == 0xc483) // add esp, $  old Debonosu game
    hp->offset = 4; // text in arg1
  else // new Debonosu game
    hp->offset = -0x8; // text in ecx instead
  //hp->type ^= EXTERN_HOOK;
  hp->text_fun = nullptr;
  *data = *(DWORD*)(esp_base + hp->offset);
  *len = ::strlen((char*)*data);
  *split = FIXED_SPLIT_VALUE;
}
bool InsertDebonosuScenarioHook()
{
  DWORD fun;
  if (!GetFunctionAddr("lstrcatA", &fun, 0, 0, 0)) {
    ConsoleOutput("vnreng:Debonosu: failed to find lstrcatA");
    return false;
  }
  DWORD addr = Util::FindImportEntry(module_base_, fun);
  if (!addr) {
    ConsoleOutput("vnreng:Debonosu: lstrcatA is not called");
    return false;
  }
  DWORD search = 0x15ff | (addr << 16); // jichi 10/20/2014: call dword ptr ds
  addr >>= 16;
  for (DWORD i = module_base_; i < module_limit_ - 4; i++)
    if (*(DWORD *)i == search &&
        *(WORD *)(i + 4) == addr && // call dword ptr lstrcatA
        *(BYTE *)(i - 5) == 0x68) { // push $
      DWORD push = *(DWORD *)(i - 4);
      for (DWORD j = i + 6, k = j + 0x10; j < k; j++)
        if (*(BYTE *)j == 0xb8 &&
            *(DWORD *)(j + 1) == push)
          if (DWORD hook_addr = SafeFindEntryAligned(i, 0x200)) {
            HookParam hp = {};
            hp.address = hook_addr;
            hp.text_fun = SpecialHookDebonosuScenario;
            //hp.type = USING_STRING;
            hp.type = USING_STRING|NO_CONTEXT|USING_SPLIT|FIXING_SPLIT; // there is only one thread
            ConsoleOutput("vnreng: INSERT Debonosu");
            NewHook(hp, "Debonosu");
            //RegisterEngineType(ENGINE_DEBONOSU);
            ConsoleOutput("vnreng:Debonosu: disable GDI+ hooks");
            DisableGDIPlusHooks();
            return true;
          }
      }

  ConsoleOutput("vnreng:Debonosu: failed");
  //ConsoleOutput("Unknown Debonosu engine.");
  return false;
}
void SpecialHookDebonosuName(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  DWORD text = regof(ecx, esp_base);
  if (!text)
    return;
  *data = text;
  *len = ::strlen((LPCSTR)text);
  *split = FIXED_SPLIT_VALUE << 1;
}
bool InsertDebonosuNameHook()
{
  ULONG startAddress, stopAddress;
  if (!NtInspect::getProcessMemoryRange(&startAddress, &stopAddress)) { // need accurate stopAddress
    ConsoleOutput("vnreng:Silkys: failed to get memory range");
    return false;
  }
  const BYTE bytes[] = {
                     // 0032f659   32c0             xor al,al
                     // 0032f65b   5b               pop ebx
                     // 0032f65c   8be5             mov esp,ebp
                     // 0032f65e   5d               pop ebp
                     // 0032f65f   c3               retn
    0x55,            // 0032f660   55               push ebp    ; jichi: name text in ecx, which could be zero though
    0x8b,0xec,       // 0032f661   8bec             mov ebp,esp
    0x81,0xec, XX4,  // 0032f663   81ec 2c080000    sub esp,0x82c
    0x8b,0x45, 0x08, // 0032f669   8b45 08          mov eax,dword ptr ss:[ebp+0x8]
    0x53,            // 0032f66c   53               push ebx
    0x56,            // 0032f66d   56               push esi
    0x8b,0xf1,       // 0032f66e   8bf1             mov esi,ecx
    0x85,0xc0,       // 0032f670   85c0             test eax,eax
    0x8d,0x4d, 0xf0, // 0032f672   8d4d f0          lea ecx,dword ptr ss:[ebp-0x10]
    0x0f,0x45,0xc8,  // 0032f675   0f45c8           cmovne ecx,eax
    0x57             // 0032f678   57               push edi
  };
  ULONG addr = MemDbg::matchBytes(bytes, sizeof(bytes), startAddress, stopAddress);
  if (!addr) {
    ConsoleOutput("vnreng:DebonosuName: pattern NOT FOUND");
    return false;
  }
  HookParam hp = {};
  hp.address = addr;
  hp.text_fun = SpecialHookDebonosuName;
  //hp.type = USING_STRING;
  hp.type = USING_STRING|NO_CONTEXT|USING_SPLIT; //|FIXING_SPLIT; // there is only one thread
  ConsoleOutput("vnreng: INSERT DebonosuName");
  NewHook(hp, "DebonosuName");
  return true;
}

} // unnamed namespace

bool InsertDebonosuHook()
{
  bool ok = InsertDebonosuScenarioHook();
  if (ok)
    InsertDebonosuNameHook();
  return ok;
}

/* 7/8/2014: The engine name is supposed to be: AoiGameSystem Engine
 * See: http://capita.tistory.com/m/post/205
 *
 *  BUNNYBLACK Trial2 (SystemAoi4)
 *  baseaddr: 0x01d0000
 *
 *  1002472e   cc               int3
 *  1002472f   cc               int3
 *  10024730   55               push ebp    ; jichi: hook here
 *  10024731   8bec             mov ebp,esp
 *  10024733   51               push ecx
 *  10024734   c745 fc 00000000 mov dword ptr ss:[ebp-0x4],0x0
 *  1002473b   8b45 08          mov eax,dword ptr ss:[ebp+0x8]
 *  1002473e   0fb708           movzx ecx,word ptr ds:[eax]
 *  10024741   85c9             test ecx,ecx
 *  10024743   74 34            je short _8.10024779
 *  10024745   6a 00            push 0x0
 *  10024747   6a 00            push 0x0
 *  10024749   6a 01            push 0x1
 *  1002474b   8b55 14          mov edx,dword ptr ss:[ebp+0x14]
 *  1002474e   52               push edx
 *  1002474f   0fb645 10        movzx eax,byte ptr ss:[ebp+0x10]
 *  10024753   50               push eax
 *  10024754   0fb74d 0c        movzx ecx,word ptr ss:[ebp+0xc]
 *  10024758   51               push ecx
 *  10024759   8b55 08          mov edx,dword ptr ss:[ebp+0x8]
 *  1002475c   52               push edx
 *  1002475d   e8 8eddffff      call _8.100224f0
 *  10024762   83c4 1c          add esp,0x1c
 *  10024765   8945 fc          mov dword ptr ss:[ebp-0x4],eax
 *  10024768   8b45 1c          mov eax,dword ptr ss:[ebp+0x1c]
 *  1002476b   50               push eax
 *  1002476c   8b4d 18          mov ecx,dword ptr ss:[ebp+0x18]
 *  1002476f   51               push ecx
 *  10024770   8b55 fc          mov edx,dword ptr ss:[ebp-0x4]
 *  10024773   52               push edx
 *  10024774   e8 77c6ffff      call _8.10020df0
 *  10024779   8b45 fc          mov eax,dword ptr ss:[ebp-0x4]
 *  1002477c   8be5             mov esp,ebp
 *  1002477e   5d               pop ebp
 *  1002477f   c2 1800          retn 0x18
 *  10024782   cc               int3
 *  10024783   cc               int3
 *  10024784   cc               int3
 *
 *  2/12/2015 jichi: SystemAoi5
 *
 *  Note that BUNNYBLACK 3 also has SystemAoi5 version 4.1
 *
 *  Hooked to PgsvTd.dll for all SystemAoi engine, which contains GDI functions.
 *  - Old: AoiLib.dll from DrawTextExA
 *  - SystemAoi4: Aoi4.dll from DrawTextExW
 *  - SystemAoi5: Aoi5.dll from GetGlyphOutlineW
 *
 *  Logic:
 *  - Find GDI function (DrawTextExW, etc.) used to paint text in PgsvTd.dll
 *  - Then search the function call stack, to find where the exe module invoke PgsvTd
 *  - Finally insert to the call address, and text is on the top of the stack.
 *
 *  Sample hooked call in 悪魔娘�看板料理 Aoi5
 *
 *  00B6D085   56               PUSH ESI
 *  00B6D086   52               PUSH EDX
 *  00B6D087   51               PUSH ECX
 *  00B6D088   68 9E630000      PUSH 0x639E
 *  00B6D08D   50               PUSH EAX
 *  00B6D08E   FF15 54D0BC00    CALL DWORD PTR DS:[0xBCD054]             ; _12.0039E890, jichi: hook here
 *  00B6D094   8B57 20          MOV EDX,DWORD PTR DS:[EDI+0x20]
 *  00B6D097   89049A           MOV DWORD PTR DS:[EDX+EBX*4],EAX
 *  00B6D09A   8B4F 20          MOV ECX,DWORD PTR DS:[EDI+0x20]
 *  00B6D09D   8B1499           MOV EDX,DWORD PTR DS:[ECX+EBX*4]
 *  00B6D0A0   8D85 50FDFFFF    LEA EAX,DWORD PTR SS:[EBP-0x2B0]
 *  00B6D0A6   50               PUSH EAX
 *  00B6D0A7   52               PUSH EDX
 *  00B6D0A8   FF15 18D0BC00    CALL DWORD PTR DS:[0xBCD018]             ; _12.003A14A0
 *
 *  Special hook is needed, since the utf16 text is like this:
 *  [f9S30e0u] が、それ�人間相手�話�� */
namespace { // unnamed
void SpecialHookSystemAoi(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  *split = 0; // 8/3/2014 jichi: split is zero, so return address is used as split
  if (hp->type & USING_UNICODE) {
    LPCWSTR wcs = (LPWSTR)argof(1, esp_base); // jichi: text on the top of the stack
    size_t size = ::wcslen(wcs);
    for (DWORD i = 0; i < size; i++)
      if (wcs[i] == L'>' || wcs[i] == L']') { // skip leading ] for scenario and > for name threads
        i++;
        if (wcs[i] == 0x3000) // \u3000
          i++;
        *data = (DWORD)(wcs + i);
        size -= i;
        *len = size * 2; // * 2 for wstring
        return;
      }
  } else {
    LPCSTR cs = (LPCSTR)argof(1, esp_base); // jichi: text on the top of the stack
    size_t size = ::strlen(cs);
    for (DWORD i = 0; i < size; i++)
      if (cs[i] == '>' || cs[i] == ']') {
        i++;
        if ((unsigned char)cs[i] == 0x81 && cs[i+1] == 0x40) // \u3000
          i += 2;
        *data = (DWORD)(cs + i);
        size -= i;
        *len = size;
        return;
      }
  }
}

int GetSystemAoiVersion() // return result is cached
{
  static int ret = 0;
  if (!ret) {
    if (IthCheckFile(L"Aoi4.dll"))
      ret = 4;
    else if (IthCheckFile(L"Aoi5.dll"))
      ret = 5;
    else if (IthCheckFile(L"Aoi6.dll")) // not exist yet, for future version
      ret = 6;
    else if (IthCheckFile(L"Aoi7.dll")) // not exist yet, for future version
      ret = 7;
    else // AoiLib.dll, etc
      ret = 3;
  }
  return ret;
}

bool InsertSystemAoiDynamicHook(LPVOID addr, DWORD frame, DWORD stack)
{
  int version = GetSystemAoiVersion();
  bool utf16 = true;
  if (addr == ::DrawTextExA) // < 4
    utf16 = false;
  if (addr == ::DrawTextExW) // 4~5
    ; // pass
  else if (addr == ::GetGlyphOutlineW && version >= 5)
    ; // pass
  else
    return false;

  DWORD high, low;
  Util::GetCodeRange(module_base_, &low, &high);

  // jichi 2/15/2015: Traverse the stack to dynamically find the ancestor call from the main module
  const DWORD stop = (stack & 0xffff0000) + 0x10000; // range to traverse the stack
  for (DWORD i = stack; i < stop; i += 4) {
    DWORD k = *(DWORD *)i;
    if (k > low && k < high && // jichi: if the stack address falls into the code region of the main exe module
        ((*(WORD *)(k - 6) == 0x15ff) || *(BYTE *)(k - 5) == 0xe8)) { // jichi 10/20/2014: call dword ptr ds

      HookParam hp = {};
      hp.offset = 0x4; // target text is on the top of the stack
      hp.text_fun = SpecialHookSystemAoi; // need to remove garbage
      hp.type = utf16 ? USING_UNICODE : USING_STRING;

      i = *(DWORD *)(k - 4); // get function call address
      if (*(DWORD *)(k - 5) == 0xe8) // short jump
        hp.address = i + k;
      else
        hp.address = *(DWORD *)i; // jichi: long jump, this is what is happening in Aoi5
      //NewHook(hp, "SofthouseChara");
      //GROWL_DWORD(hp.address); // BUNNYBLACK: 0x10024730, base 0x01d0000
      if (hp.address) {
        ConsoleOutput("vnreng: INSERT SystemAoi");
        if (addr == ::GetGlyphOutlineW)
          NewHook(hp, "SystemAoi2"); // jichi 2/12/2015
        else
          NewHook(hp, "SystemAoi"); // jichi 7/8/2014: renamed, see: ja.wikipedia.org/wiki/ソフトハウスキャラ
        ConsoleOutput("vnreng:SystemAoi: disable GDI hooks");
        DisableGDIHooks();
      } else
        ConsoleOutput("vnreng: failed to detect SystemAoi");
      //RegisterEngineType(ENGINE_SOFTHOUSE);
      return true;
    }
  }
  ConsoleOutput("vnreng:SystemAoi: failed");
  return true; // jichi 12/25/2013: return true
}

bool InsertSystemAoiDynamic()
{
  ConsoleOutput("vnreng: DYNAMIC SystemAoi");
  //ConsoleOutput("Probably SoftHouseChara. Wait for text.");
  trigger_fun_ = InsertSystemAoiDynamicHook;
  SwitchTrigger(true);
  return true;
}

ULONG findAoiProc(HMODULE hModule, LPCSTR functionName, int minParamNum = 0, int maxParamNum = 10)
{
  for (int i = minParamNum; i < maxParamNum; i++) {
    std::string sig; // function signature name, such as _AgsSpriteCreateText@20
    sig.push_back('_');
    sig += functionName;
    sig.push_back('@');
    sig += std::to_string(4ll * i);
    if (auto proc = ::GetProcAddress(hModule, sig.c_str()))
      return (ULONG)proc;
  }
  return 0;
}

// jichi 7/26/2015: Backport logic in vnragent to vnrhook
bool InsertSystemAoiStatic(HMODULE hModule, bool wideChar) // attach scenario
{
  ULONG addr = findAoiProc(hModule, "AgsSpriteCreateText", 1);
  if (!addr) {
    ConsoleOutput("vnreng:SystemAoiStatic: function found");
    return false;
  }
  HookParam hp = {};
  hp.address = addr;
  hp.offset = 4 * 1; // arg1
  //hp.split = 4 * 2; // arg2
  hp.text_fun = SpecialHookSystemAoi;
  hp.type = wideChar ? USING_UNICODE : USING_STRING;
  //hp.type |= NO_CONTEXT|USING_SPLIT|SPLIT_INDIRECT;
  ConsoleOutput("vnreng: INSERT static SystemAoi");
  if (wideChar)
    NewHook(hp, "SystemAoiW");
  else
    NewHook(hp, "SystemAoiA");
  ConsoleOutput("vnreng:SystemAoiStatic: disable GDI hooks");
  DisableGDIHooks();
  return true;
}
} // unnamed namespace

bool InsertSystemAoiHook() // this function always returns true
{
  HMODULE hModule = ::GetModuleHandleA("Ags.dll");
  bool wideChar = true;
  if (hModule) // Aoi <= 3
    wideChar = false;
  else { // Aoi >= 4
    hModule = ::GetModuleHandleA("Ags5.dll");
    if (!hModule)
      hModule = ::GetModuleHandleA("Ags4.dll");
  }
  return hModule && InsertSystemAoiStatic(hModule, wideChar)
      || InsertSystemAoiDynamic();
}

static void SpecialHookCaramelBox(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  DWORD reg_ecx = *(DWORD*)(esp_base + hp->offset);
  BYTE *ptr = (BYTE *)reg_ecx;
  buffer_index = 0;
  while (ptr[0])
    if (ptr[0] == 0x28) { // Furigana format: (Kanji,Furi)
      ptr++;
      while (ptr[0]!=0x2c) //Copy Kanji
        text_buffer[buffer_index++] = *ptr++;
      while (ptr[0]!=0x29) // Skip Furi
        ptr++;
      ptr++;
    } else if (ptr[0] == 0x5c)
      ptr +=2;
    else {
      text_buffer[buffer_index++] = ptr[0];
      if (LeadByteTable[ptr[0]] == 2) {
        ptr++;
        text_buffer[buffer_index++] = ptr[0];
      }
      ptr++;
    }

  *len = buffer_index;
  *data = (DWORD)text_buffer;
  *split = 0; // 8/3/2014 jichi: use return address as split
}
// jichi 10/1/2013: Change return type to bool
bool InsertCaramelBoxHook()
{
  union { DWORD i; BYTE* pb; WORD* pw; DWORD *pd; };
  DWORD reg = -1;
  for (i = module_base_ + 0x1000; i < module_limit_ - 4; i++) {
    if (*pd == 0x7ff3d) // cmp eax, 7ff
      reg = 0;
    else if ((*pd & 0xfffff8fc) == 0x07fff880) // cmp reg, 7ff
      reg = pb[1] & 0x7;

    if (reg == -1)
      continue;

    DWORD flag = 0;
    if (*(pb - 6) == 3) { //add reg, [ebp+$disp_32]
      if (*(pb - 5) == (0x85 | (reg << 3)))
        flag = 1;
    } else if (*(pb - 3) == 3) { // add reg, [ebp+$disp_8]
      if (*(pb - 2) == (0x45 | (reg << 3)))
        flag = 1;
    } else if (*(pb - 2) == 3) { // add reg, reg
      if (((*(pb - 1) >> 3) & 7)== reg)
        flag = 1;
    }
    reg = -1;
    if (flag) {
      for (DWORD j = i, k = i - 0x100; j > k; j--) {
        if ((*(DWORD *)j & 0xffff00ff) == 0x1000b8) { // mov eax,10??
          HookParam hp = {};
          hp.address = j & ~0xf;
          hp.text_fun = SpecialHookCaramelBox;
          hp.type = USING_STRING;
          for (i &= ~0xffff; i < module_limit_ - 4; i++)
            if (pb[0] == 0xe8) {
              pb++;
              if (pd[0] + i + 4 == hp.address) {
                pb += 4;
                if ((pd[0] & 0xffffff) == 0x04c483)
                  hp.offset = 4;
                else hp.offset = -0xc;
                break;
              }
            }

          if (hp.offset == 0) {
            ConsoleOutput("vnreng:CaramelBox: failed, zero off");
            return false;
          }
          ConsoleOutput("vnreng: INSERT CaramelBox");
          NewHook(hp, "CaramelBox");
          //RegisterEngineType(ENGINE_CARAMEL);
          return true;
        }
      }
    }
  }
  ConsoleOutput("vnreng:CaramelBox: failed");
  return false;
//_unknown_engine:
  //ConsoleOutput("Unknown CarmelBox engine.");
}

/**
 *  jichi 10/12/2014
 *  P.S.: Another approach
 *  See: http://tieba.baidu.com/p/2425786155
 *  Quote:
 *  I guess this post should go in here. I got sick of AGTH throwing a fit when entering the menus in Wolf RPG games, so I did some debugging. This is tested and working properly with lots of games. If you find one that isn't covered then please PM me and I'll look into it.
 *
 *  Wolf RPG H-code - Use whichever closest matches your Game.exe
 *  /HBN*0@454C6C (2010/10/09 : 2,344KB : v1.31)
 *  /HBN*0@46BA03 (2011/11/22 : 2,700KB : v2.01)
 *  /HBN*0@470CEA (2012/05/07 : 3,020KB : v2.02)
 *  /HBN*0@470D5A (2012/06/10 : 3,020KB : v2.02a)
 *
 *  ith_p.cc:Ith::parseHookCode: enter: code = "/HBN*0@470CEA"
 *  - addr: 4656362 ,
 *  - length_offset: 1
 *  - type: 1032 = 0x408
 *
 *  Use /HB instead of /HBN if you want to split dialogue text and menu text into separate threads.
 *  Also set the repetition trace parameters in AGTH higher or it won't work properly with text-heavy menus. 64 x 16 seems to work fine.
 *
 *  Issues:
 *  AGTH still causes a bit of lag when translating menus if you have a lot of skills or items.
 *  Using ITH avoids this problem, but it sometimes has issues with repetition detection which can be fixed by quickly deselecting and reselecting the game window; Personally I find this preferable to menu and battle slowdown that AGTH sometimes causes, but then my PC is pretty slow so you might not have that problem.
 *
 *  Minimising the AGTH/ITH window generally makes the game run a bit smoother as windows doesn't need to keep scrolling the text box as new text is added.
 *
 *  RPG Maker VX H-code:
 *  Most games are detected automatically and if not then by using the AGTH /X or /X2 or /X3 parameters.
 *
 *  Games that use TRGSSX.dll may have issues with detection (especially with ITH).
 *  If TRGSSX.dll is included with the game then this code should work:
 *  /HQN@D3CF:TRGSSX.dll
 *
 *  With this code, using AGTH to start the process will not work. You must start the game normally and then hook the process afterwards.
 *  ITH has this functionality built into the interface. AGTH requires the /PN command line argument, for example:
 *  agth /PNGame.exe /HQN@D3CF:TRGSSX.dll /C
 *
 *  Again, drop the N to split dialogue and menu text into separate threads.
 */
namespace { // WolfRPG
// jichi 10/13/2013: restored
bool InsertOldWolfHook()
{
  //__asm int 3   // debug
  // jichi 10/12/2013:
  // Step 1: find the address of GetTextMetricsA
  // Step 2: find where this function is called
  // Step 3: search "sub esp, XX" after where it is called
  enum { sub_esp = 0xec81 }; // jichi: caller pattern: sub esp = 0x81,0xec
  if (DWORD c1 = Util::FindCallAndEntryAbs((DWORD)GetTextMetricsA, module_limit_ - module_base_, module_base_, sub_esp))
    if (DWORD c2 = Util::FindCallOrJmpRel(c1, module_limit_ - module_base_, module_base_, 0)) {
      union {
        DWORD i;
        WORD *k;
      };
      DWORD j;
      for (i = c2 - 0x100, j = c2 - 0x400; i > j; i--)
        if (*k == 0xec83) { // jichi 10/12/2013: 83 EC XX   sub esp, XX  See: http://lists.cs.uiuc.edu/pipermail/llvm-commits/Week-of-Mon-20120312.txt
          HookParam hp = {};
          hp.address = i;
          hp.offset = -0xc;
          hp.split = -0x18;
          hp.type = DATA_INDIRECT|USING_SPLIT;
          hp.length_offset = 1;
          //GROWL_DWORD(hp.address); // jichi 6/5/2014: 淫乱勀��フィのRPG = 0x50a400
          ConsoleOutput("vnreng: INSERT WolfRPG");
          NewHook(hp, "WolfRPG");
          return true;
        }
    }

  //ConsoleOutput("Unknown WolfRPG engine.");
  ConsoleOutput("vnreng:WolfRPG: failed");
  return false;
}


struct TextListElement // ecx, this structure saved a list of element
{
  DWORD flag1; // should be zero when text is valid
  LPSTR text;
  DWORD flag2;
  DWORD flag3;
  DWORD flag4;
  int size,
      capacity; // 0xe8, capacity of the data including \0

  bool isValid() const
  {
    return flag1 == 0 && flag2 == 0 && flag3 == 0 && flag4 == 0
        && size > 0 && size < capacity
        && !::IsBadReadPtr(text, capacity) && size == ::strlen(text);
        //&& (quint8)*text > 127;
  }
};
void SpecialHookWolf2(DWORD esp_base, HookParam *, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  auto self = (TextListElement *)regof(ecx, esp_base); // ecx is actually a list of element
  if (self && self->isValid()) {
    *data = (DWORD)self->text;
    *len = self->size;
  }
}

#if 0
// jichi 6/11/2015: See embed translation source code
bool InsertWolf2Hook()
{
  ULONG startAddress, stopAddress;
  if (!NtInspect::getProcessMemoryRange(&startAddress, &stopAddress)) { // need accurate stopAddress
    ConsoleOutput("vnreng:WolfRPG2: failed to get memory range");
    return false;
  }
  ULONG addr = MemDbg::findCallerAddressAfterInt3((ULONG)::CharNextA, startAddress, stopAddress);
  if (!addr) {
    ConsoleOutput("vnreng:WolfRPG2: failed to find target function");
    return false;
  }

  HookParam hp = {};
  hp.address = addr;
  hp.text_fun = SpecialHookWolf2;
  hp.type = USING_STRING;
  ConsoleOutput("vnreng: INSERT WolfRPG2");
  NewHook(hp, "WolfRPG2");
  return true;
}
#endif // 0

} // WolfRPG namespace

bool InsertWolfHook()
{
  return InsertOldWolfHook();
}

bool InsertIGSDynamicHook(LPVOID addr, DWORD frame, DWORD stack)
{
  if (addr != GetGlyphOutlineW)
    return false;
  DWORD i;
  i = *(DWORD *)frame;
  i = *(DWORD *)(i+4);
  DWORD j, k;
  if (SafeFillRange(L"mscorlib.ni.dll", &j, &k)) {
    while (*(BYTE *)i != 0xe8)
      i++;
    DWORD t = *(DWORD *)(i + 1) + i + 5;
    if (t>j && t<k) {
      HookParam hp = {};
      hp.address = t;
      hp.offset = -0x10;
      hp.split = -0x18;
      hp.length_offset = 1;
      hp.type = USING_UNICODE|USING_SPLIT;
      ConsoleOutput("vnreng: INSERT IronGameSystem");
      NewHook(hp, "IronGameSystem");
      //ConsoleOutput("IGS - Please set text(ヂ�ス� display speed(表示速度) to fastest(瞬�");
      //RegisterEngineType(ENGINE_IGS);
      return true;
    }
  }
  ConsoleOutput("vnreng:IGS: failed");
  return true; // jichi 12/25/2013: return true
}
void InsertIronGameSystemHook()
{
  //ConsoleOutput("Probably IronGameSystem. Wait for text.");
  trigger_fun_ = InsertIGSDynamicHook;
  SwitchTrigger(true);
  ConsoleOutput("vnreng: TRIGGER IronGameSystem");
}

/********************************************************************************************
AkabeiSoft2Try hook:
  Game folder contains YaneSDK.dll. Maybe we should call the engine Yane(屋� = roof)?
  This engine is based on .NET framework. This really makes it troublesome to locate a
  valid hook address. The problem is that the engine file merely contains bytecode for
  the CLR. Real meaningful object code is generated dynamically and the address is randomized.
  Therefore the easiest method is to brute force search whole address space. While it's not necessary
  to completely search the whole address space, since non-executable pages can be excluded first.
  The generated code sections do not belong to any module(exe/dll), hence they do not have
  a section name. So we can also exclude executable pages from all modules. At last, the code
  section should be long(>0x2000). The remain address space should be several MBs in size and
  can be examined in reasonable time(less than 0.1s for P8400 Win7x64).
  Characteristic sequence is 0F B7 44 50 0C, stands for movzx eax, word ptr [edx*2 + eax + C].
  Obviously this instruction extracts one unicode character from a string.
  A main shortcoming is that the code is not generated if it hasn't been used yet.
  So if you are in title screen this approach will fail.

********************************************************************************************/
namespace { // unnamed
MEMORY_WORKING_SET_LIST *GetWorkingSet()
{
  DWORD len,retl;
  NTSTATUS status;
  LPVOID buffer = 0;
  len = 0x4000;
  status = NtAllocateVirtualMemory(NtCurrentProcess(), &buffer, 0, &len, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
  if (!NT_SUCCESS(status)) return 0;
  status = NtQueryVirtualMemory(NtCurrentProcess(), 0, MemoryWorkingSetList, buffer, len, &retl);
  if (status == STATUS_INFO_LENGTH_MISMATCH) {
    len = *(DWORD*)buffer;
    len = ((len << 2) & 0xfffff000) + 0x4000;
    retl = 0;
    NtFreeVirtualMemory(NtCurrentProcess(), &buffer, &retl, MEM_RELEASE);
    buffer = 0;
    status = NtAllocateVirtualMemory(NtCurrentProcess(), &buffer, 0, &len, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
    if (!NT_SUCCESS(status)) return 0;
    status = NtQueryVirtualMemory(NtCurrentProcess(), 0, MemoryWorkingSetList, buffer, len, &retl);
    if (!NT_SUCCESS(status)) return 0;
    return (MEMORY_WORKING_SET_LIST*)buffer;
  } else {
    retl = 0;
    NtFreeVirtualMemory(NtCurrentProcess(), &buffer, &retl, MEM_RELEASE);
    return 0;
  }

}
typedef struct _NSTRING
{
  PVOID vfTable;
  DWORD lenWithNull;
  DWORD lenWithoutNull;
  WCHAR str[1];
} NSTRING;

// qsort correctly identifies overflow.
int cmp(const void * a, const void * b)
{ return *(int*)a - *(int*)b; }

void SpecialHookAB2Try(DWORD esp_base, HookParam *, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  //DWORD test = *(DWORD*)(esp_base - 0x10);
  DWORD edx = regof(edx, esp_base);
  if (edx != 0)
    return;

  //NSTRING *s = *(NSTRING **)(esp_base - 8);
  if (const NSTRING *s = (NSTRING *)regof(eax, esp_base)) {
    *len = s->lenWithoutNull << 1;
    *data = (DWORD)s->str;
    //*split = 0;
    *split = FIXED_SPLIT_VALUE; // 8/3/2014 jichi: change to single threads
  }
}

BOOL FindCharacteristInstruction(MEMORY_WORKING_SET_LIST *list)
{
  DWORD base, size;
  DWORD i, j, k, addr, retl;
  NTSTATUS status;
  ::qsort(&list->WorkingSetList, list->NumberOfPages, 4, cmp);
  base = list->WorkingSetList[0];
  size = 0x1000;
  for (i = 1; i < list->NumberOfPages; i++) {
    if ((list->WorkingSetList[i] & 2) == 0)
      continue;
    if (list->WorkingSetList[i] >> 31)
      break;
    if (base + size == list->WorkingSetList[i])
      size += 0x1000;
    else {
      if (size > 0x2000) {
        addr = base & ~0xfff;
        status = NtQueryVirtualMemory(NtCurrentProcess(),(PVOID)addr,
            MemorySectionName,text_buffer_prev,0x1000,&retl);
        if (!NT_SUCCESS(status)) {
          k = addr + size - 4;
          for (j = addr; j < k; j++) {
            if (*(DWORD*)j == 0x5044b70f) {
              if (*(WORD*)(j + 4) == 0x890c) { // movzx eax, word ptr [edx*2 + eax + 0xC]; wchar = string[i];
                HookParam hp = {};
                hp.address = j;
                hp.text_fun = SpecialHookAB2Try;
                hp.type = USING_STRING|NO_CONTEXT|USING_UNICODE;
                ConsoleOutput("vnreng: INSERT AB2Try");
                NewHook(hp, "AB2Try");
                //ConsoleOutput("Please adjust text speed to fastest/immediate.");
                //RegisterEngineType(ENGINE_AB2T);
                return TRUE;
              }
            }
          }
        }
      }
      size = 0x1000;
      base = list->WorkingSetList[i];
    }
  }
  return FALSE;
}
} // unnamed namespace
bool InsertAB2TryHook()
{
  MEMORY_WORKING_SET_LIST *list = GetWorkingSet();
  if (!list) {
    ConsoleOutput("vnreng:AB2Try: cannot find working list");
    return false;
  }
  bool ret = FindCharacteristInstruction(list);
  if (ret)
    ConsoleOutput("vnreng:AB2Try: found characteristic sequence");
  else
    ConsoleOutput("vnreng:AB2Try: cannot find characteristic sequence");
  //L"Make sure you have start the game and have seen some text on the screen.");
  DWORD size = 0;
  NtFreeVirtualMemory(NtCurrentProcess(), (PVOID *)&list, &size, MEM_RELEASE);
  return ret;
}

/********************************************************************************************
C4 hook: (Contributed by Stomp)
  Game folder contains C4.EXE or XEX.EXE.
********************************************************************************************/
bool InsertC4Hook()
{
  const BYTE bytes[] = { 0x8a, 0x10, 0x40, 0x80, 0xfa, 0x5f, 0x88, 0x15 };
  //enum { addr_offset = 0 };
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_limit_);
  if (!addr) {
    ConsoleOutput("vnreng:C4: pattern not found");
    return false;
  }
  HookParam hp = {};
  hp.address = addr;
  hp.offset = -0x8;
  hp.type = DATA_INDIRECT|NO_CONTEXT;
  hp.length_offset = 1;
  ConsoleOutput("vnreng: INSERT C4");
  NewHook(hp, "C4");
  //RegisterEngineType(ENGINE_C4);
  return true;
}

/** 1/18/2015 jichi Add new WillPlus
 *  The old hook no longer works for new game.
 *  Sample game: [150129] [honeybee] RE:BIRTHDAY SONG
 *
 *  Note, WillPlus engine is migrating to UTF16 using GetGlyphOutlineW such as:
 *      [141218] [Guily] 手�めにされる九人の堕女
 *  This engine does not work for GetGlyphOutlineW, which, however, does not need a H-code.
 *
 *  See: http://sakuradite.com/topic/615
 *
 *  There WillPlus games have many hookable threads.
 *  But it is kind of important to find the best one.
 *
 *  By inserting hw point:
 *  - There is a clean text thread with fixed memory address.
 *    However, it cannot extract character name like GetGlyphOutlineA.
 *  - This is a non-clean text thread, but it contains garbage such as %LC.
 *
 *  By backtracking from GetGlyphOutlineA:
 *  - GetGlyphOutlineA sometimes can extract all text, sometimes not.
 *  - There are two GetGlyphOutlineA functions.
 *    Both of them are called statically in the same function.
 *    That function is hooked.
 *
 *  Hooked function:
 *  0041820c   cc               int3
 *  0041820d   cc               int3
 *  0041820e   cc               int3
 *  0041820f   cc               int3
 *  00418210   81ec b4000000    sub esp,0xb4
 *  00418216   8b8424 c4000000  mov eax,dword ptr ss:[esp+0xc4]
 *  0041821d   53               push ebx
 *  0041821e   8b9c24 d0000000  mov ebx,dword ptr ss:[esp+0xd0]
 *  00418225   55               push ebp
 *  00418226   33ed             xor ebp,ebp
 *  00418228   56               push esi
 *  00418229   8bb424 dc000000  mov esi,dword ptr ss:[esp+0xdc]
 *  00418230   03c3             add eax,ebx
 *  00418232   57               push edi
 *  00418233   8bbc24 d8000000  mov edi,dword ptr ss:[esp+0xd8]
 *  0041823a   896c24 14        mov dword ptr ss:[esp+0x14],ebp
 *  0041823e   894424 4c        mov dword ptr ss:[esp+0x4c],eax
 *  00418242   896c24 24        mov dword ptr ss:[esp+0x24],ebp
 *  00418246   39ac24 e8000000  cmp dword ptr ss:[esp+0xe8],ebp
 *  0041824d   75 29            jnz short .00418278
 *  0041824f   c74424 24 010000>mov dword ptr ss:[esp+0x24],0x1
 *
 *  ...
 *
 *  00418400   56               push esi
 *  00418401   52               push edx
 *  00418402   ff15 64c04b00    call dword ptr ds:[0x4bc064]             ; gdi32.getglyphoutlinea
 *  00418408   8bf8             mov edi,eax
 *
 *  The old WillPlus engine can also be inserted to the new games.
 *  But it has no effects.
 *
 *  A split value is used to get saving message out.
 *
 *  Runtime stack for the scenario thread:
 *  0012d9ec   00417371  return to .00417371 from .00418210
 *  0012d9f0   00000003  1
 *  0012d9f4   00000000  2
 *  0012d9f8   00000130  3
 *  0012d9fc   0000001a  4
 *  0012da00   0000000b  5
 *  0012da04   00000016  6
 *  0012da08   0092fc00  .0092fc00 ms gothic ; jichi: here's font
 *  0012da0c   00500aa0  .00500aa0 shun ; jichi: text is here in arg8
 *  0012da10   0217dcc0
 *
 *  Runtime stack for name:
 *  0012d9ec   00417371  return to .00417371 from .00418210
 *  0012d9f0   00000003
 *  0012d9f4   00000000
 *  0012d9f8   00000130
 *  0012d9fc   0000001a
 *  0012da00   0000000b
 *  0012da04   00000016
 *  0012da08   0092fc00  .0092fc00
 *  0012da0c   00500aa0  .00500aa0
 *  0012da10   0217dcc0
 *  0012da14   00000000
 *  0012da18   00000000
 *
 *  Runtime stack for non-dialog scenario text.
 *  0012e5bc   00438c1b  return to .00438c1b from .00418210
 *  0012e5c0   00000006
 *  0012e5c4   00000000
 *  0012e5c8   000001ae
 *  0012e5cc   000000c8
 *  0012e5d0   0000000c
 *  0012e5d4   00000018
 *  0012e5d8   0092fc00  .0092fc00
 *  0012e5dc   0012e628
 *  0012e5e0   0b0d0020
 *  0012e5e4   004fda98  .004fda98
 *
 *  Runtime stack for saving message
 *  0012ed44   00426003  return to .00426003 from .00418210
 *  0012ed48   000003c7
 *  0012ed4c   00000000
 *  0012ed50   000000d8
 *  0012ed54   0000012f
 *  0012ed58   00000008
 *  0012ed5c   00000010
 *  0012ed60   0092fc00  .0092fc00
 *  0012ed64   00951d88  ascii "2015/01/18"
 */

namespace { // unnamed
#if 0
static bool InsertWillPlusHook2() // jichi 1/18/2015: Add new hook
{
  ULONG startAddress, stopAddress;
  if (!NtInspect::getProcessMemoryRange(&startAddress, &stopAddress)) { // need accurate stopAddress
    ConsoleOutput("vnreng:WillPlus2: failed to get memory range");
    return false;
  }

  // The following won't work after inserting WillPlus1, which also produces int3
  //ULONG addr = MemDbg::findCallerAddressAfterInt3((DWORD)::GetGlyphOutlineA, startAddress, stopAddress);

  // 00418210   81ec b4000000    sub esp,0xb4
  enum { sub_esp = 0xec81 }; // jichi: caller pattern: sub esp = 0x81,0xec
  ULONG addr = MemDbg::findCallerAddress((ULONG)::GetGlyphOutlineA, sub_esp, startAddress, stopAddress);
  if (!addr) {
    ConsoleOutput("vnreng:WillPlus2: could not find caller of GetGlyphOutlineA");
    return false;
  }

  HookParam hp = {};
  hp.address = addr;
  hp.offset = 4 * 8; // arg8, address of text

  // Strategy 1: Use caller's address as split
  //hp.type = USING_STRING; // merge different scenario threads

  // Strategy 2: use arg1 as split
  hp.type = USING_STRING|NO_CONTEXT|USING_SPLIT; // merge different scenario threads
  hp.split = 4 * 1; // arg1 as split to get rid of saving message

  // Strategy 3: merge all threads
  //hp.type = USING_STRING|NO_CONTEXT|FIXING_SPLIT; // merge different scenario threads

  ConsoleOutput("vnreng: INSERT WillPlus2");
  NewHook(hp, "WillPlus2");
  return true;
}
#endif // 0

void SpecialHookWillPlus(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  //static DWORD detect_offset; // jichi 1/18/2015: this makes sure it only runs once
  //if (detect_offset)
  //  return;
  DWORD i,l;
  union {
    DWORD retn;
    WORD *pw;
    BYTE *pb;
  };
  retn = *(DWORD *)esp_base; // jichi 1/18/2015: dynamically find function return address
  i = 0;
  while (*pw != 0xc483) { // add esp, $
    l = ::disasm(pb);
    if (++i == 5)
      //ConsoleOutput("Fail to detect offset.");
      break;
    retn += l;
  }
  // jichi 2/11/2015: Check baddaddr which might crash the game on Windows XP.
  if (*pw == 0xc483 && !::IsBadReadPtr((LPCVOID)(pb + 2), 1) && !::IsBadReadPtr((LPCVOID)(*(pb + 2) - 8), 1)) {
    ConsoleOutput("vnreng: WillPlus1 pattern found");
    // jichi 1/18/2015:
    // By studying [honeybee] RE:BIRTHDAY SONG, it seems the scenario text is at fixed address
    // This offset might be used to find fixed address
    // However, this method cannot extract character name like GetGlyphOutlineA
    hp->offset = *(pb + 2) - 8;

    // Still extract the first text
    //hp->type ^= EXTERN_HOOK;
    char *str = *(char **)(esp_base + hp->offset);
    *data = (DWORD)str;
    *len = ::strlen(str);
    *split = 0; // 8/3/2014 jichi: use return address as split

  } else { // jichi 1/19/2015: Try willplus2
    ConsoleOutput("vnreng: WillPlus1 pattern not found, try WillPlus2 instead");
    hp->offset = 4 * 8; // arg8, address of text
    hp->type = USING_STRING|NO_CONTEXT|USING_SPLIT; // merge different scenario threads
    hp->split = 4 * 1; // arg1 as split to get rid of saving message
    // The first text is skipped here
    //char *str = *(char **)(esp_base + hp->offset);
    //*data = (DWORD)str;
    //*len = ::strlen(str);
  }
  hp->text_fun = nullptr; // stop using text_fun any more
  //detect_offset = 1;
}

// Although the new hook also works for the old game, the old hook is still used by default for compatibility
bool InsertOldWillPlusHook()
{
  //__debugbreak();
  enum { sub_esp = 0xec81 }; // jichi: caller pattern: sub esp = 0x81,0xec byte
  ULONG addr = MemDbg::findCallerAddress((ULONG)::GetGlyphOutlineA, sub_esp, module_base_, module_limit_);
  if (!addr) {
    ConsoleOutput("vnreng:WillPlus: function call not found");
    return false;
  }

  HookParam hp = {};
  hp.address = addr;
  hp.text_fun = SpecialHookWillPlus;
  hp.type = USING_STRING;
  ConsoleOutput("vnreng: INSERT WillPlus");
  NewHook(hp, "WillPlus");
  //RegisterEngineType(ENGINE_WILLPLUS);
  return true;
}

const char *_willplus_trim_a(const char *text, size_t *size)
{
  int textSize = ::strlen(text);
  int prefix = 0;
  if (text[0] == '%') {
    while (prefix < textSize - 1 && text[prefix] == '%' && ::isupper(text[prefix+1])) {
      prefix += 2;
      while (::isupper(text[prefix]))
        prefix++;
    }
  }
  {
    int pos = textSize;
    for (int i = textSize - 1; i >= prefix; i--) {
      char ch = text[i];
      if (::isupper(ch))
        ;
      else if (ch == '%')
        pos = i;
      else
        break;
    }
    int suffix = textSize - pos;
    if (size)
      *size = textSize - prefix - suffix;
  }
  return text + prefix;
}

const wchar_t *_willplus_trim_w(const wchar_t *text, size_t *size)
{
  int textSize = ::wcslen(text);
  int prefix = 0;
  if (text[0] == '%') {
    while (prefix < textSize - 1 && text[prefix] == '%' && ::isupper(text[prefix+1])) {
      prefix += 2;
      while (::isupper(text[prefix]))
        prefix++;
    }
  }
  {
    int pos = textSize;
    for (int i = textSize - 1; i >= prefix; i--) {
      wchar_t ch = text[i];
      if (::isupper(ch))
        ;
      else if (ch == '%')
        pos = i;
      else
        break;
    }
    int suffix = textSize - pos;
    if (size)
      *size = textSize - prefix - suffix;
  }
  return text + prefix;
}

void SpecialHookWillPlusA(DWORD esp_base, HookParam *, BYTE index, DWORD *data, DWORD *split, DWORD *len)
{
  auto text = (LPCSTR)regof(eax, esp_base);
  if (!text)
    return;
  if (index) // index == 1 is name
    text -= 1024;
  if (!*text)
    return;
  text = _willplus_trim_a(text, (size_t *)len);
  *data = (DWORD)text;
  *split = FIXED_SPLIT_VALUE << index;
}

bool InsertWillPlusAHook()
{
  const BYTE bytes[] = {
    0x81,0xec, 0x14,0x08,0x00,0x00 // 0042B5E0   81EC 14080000    SUB ESP,0x814	; jichi: text in eax, name in eax - 1024, able to copy
  };
  DWORD addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_limit_);
  if (!addr) {
    ConsoleOutput("vnreng:WillPlusA: pattern not found");
    return false;
  }
  HookParam hp = {};
  hp.address = addr;
  hp.text_fun = SpecialHookWillPlusA;
  hp.type = NO_CONTEXT;
  hp.extra_text_count = 1;
  hp.filter_fun = NewLineStringFilter; // remove two characters of "\\n"
  ConsoleOutput("vnreng: INSERT WillPlusA");
  NewHook(hp, "WillPlusA");
  return true;
}

void SpecialHookWillPlusW(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  auto text = (LPCWSTR)regof(ecx, esp_base);
  if (!text || !*text)
    return;
  text = _willplus_trim_w(text, (size_t *)len);
  *len *= 2;
  *data = (DWORD)text;
  *split = FIXED_SPLIT_VALUE << hp->user_value;
}

bool InsertWillPlusWHook()
{
  const BYTE bytes1[] = { // scenario
    0x83,0xc0, 0x20,     // 00452b02   83c0 20     add eax,0x20    ; jichi: hook before here, text in ecx
    0x33,0xd2,           // 00452b05   33d2        xor edx,edx
    0x8b,0xc1,           // 00452b07   8bc1        mov eax,ecx
    0xc7,0x84,0x24, 0xe0,0x01,0x00,0x00, 0x07,0x00,0x00,0x00  // 00452b09   c78424 e0010000 07000000      mov dword ptr ss:[esp+0x1e0],0x7
                                                              // 00452b14   c78424 dc010000 00000000      mov dword ptr ss:[esp+0x1dc],0x0
  };
  const BYTE bytes2[] = { // name
    0x33,0xdb,   // 00453521   33db                            xor ebx,ebx  ; jichi: hook here, text in ecx
    0x33,0xd2,   // 00453523   33d2                            xor edx,edx
    0x8b,0xc1,   // 00453525   8bc1                            mov eax,ecx
    0xc7,0x84,0x24, 0x88,0x00,0x00,0x00, 0x07,0x00,0x00,0x00 // 00453527   c78424 88000000 07000000        mov dword ptr ss:[esp+0x88],0x7
                                                             // 00453532   899c24 84000000                 mov dword ptr ss:[esp+0x84],ebx
  };
  const BYTE *bytes[] = {bytes1, bytes2};
  const size_t sizes[] = {sizeof(bytes1), sizeof(bytes2)};
  for (int i = 0; i < 2; i++) {
    DWORD addr = MemDbg::findBytes(bytes[i], sizes[i], module_base_, module_limit_);
    if (!addr) {
      ConsoleOutput("vnreng:WillPlusW: pattern not found");
      return false;
    }
    HookParam hp = {};
    hp.address = addr;
    hp.text_fun = SpecialHookWillPlusW;
    hp.type = NO_CONTEXT;
    hp.user_value = i;
    hp.filter_fun = NewLineWideStringFilter; // remove two characters of "\\n"
    ConsoleOutput("vnreng: INSERT WillPlusW");
    NewHook(hp, "WillPlusW");
  }
  return true;
}

} // unnamed namespace

bool InsertWillPlusHook()
{
  bool ok = InsertOldWillPlusHook();
  ok = InsertWillPlusWHook() || InsertWillPlusAHook() || ok;
  return ok;
}

/** jichi 9/14/2013
 *  TanukiSoft (*.tac)
 *
 *  Seems to be broken for new games in 2012 such like となり�
 *
 *  微少女: /HSN4@004983E0
 *  This is the same hook as ITH
 *  - addr: 4817888 (0x4983e0)
 *  - text_fun: 0x0
 *  - off: 4
 *  - type: 1025 (0x401)
 *
 *  隣り�ぷ�さ� /HSN-8@200FE7:TONARINO.EXE
 *  - addr: 2101223 (0x200fe7)
 *  - module: 2343491905 (0x8baed941)
 *  - off: 4294967284 = 0xfffffff4 = -0xc
 *  - type: 1089 (0x441)
 */
bool InsertTanukiHook()
{
  ConsoleOutput("vnreng: trying TanukiSoft");
  for (DWORD i = module_base_; i < module_limit_ - 4; i++)
    if (*(DWORD *)i == 0x8140)
      if (DWORD j = SafeFindEntryAligned(i, 0x400)) { // jichi 9/14/2013: might crash the game without admin priv
        //GROWL_DWORD2(i, j);
        HookParam hp = {};
        hp.address = j;
        hp.offset = 4;
        hp.type = USING_STRING | NO_CONTEXT;
        ConsoleOutput("vnreng: INSERT TanukiSoft");
        NewHook(hp, "TanukiSoft");
        return true;
      }

  //ConsoleOutput("Unknown TanukiSoft engine.");
  ConsoleOutput("vnreng:TanukiSoft: failed");
  return false;
}
static void SpecialHookRyokucha(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  const DWORD *base = (const DWORD *)esp_base;
  for (DWORD i = 1; i < 5; i++) {
    DWORD j = base[i];
    if ((j >> 16) == 0 && (j >> 8)) {
      hp->offset = i << 2;
      *data = j;
      *len = 2;
      //hp->type &= ~EXTERN_HOOK;
      hp->text_fun = nullptr;
      return;
    }
  }
  *len = 0;
}
bool InsertRyokuchaDynamicHook(LPVOID addr, DWORD frame, DWORD stack)
{
  if (addr != ::GetGlyphOutlineA)
    return false;
  bool flag;
  DWORD insert_addr;
  __asm
  {
    mov eax,fs:[0]
    mov eax,[eax]
    mov eax,[eax] //Step up SEH chain for 2 nodes.
    mov ecx,[eax + 0xC]
    mov eax,[eax + 4]
    add ecx,[ecx - 4]
    mov insert_addr,ecx
    cmp eax,[ecx + 3]
    sete al
    mov flag,al
  }
  if (flag) {
    HookParam hp = {};
    hp.address = insert_addr;
    hp.length_offset = 1;
    hp.text_fun = SpecialHookRyokucha;
    hp.type = BIG_ENDIAN;
    ConsoleOutput("vnreng: INSERT StudioRyokucha");
    NewHook(hp, "StudioRyokucha");
    return true;
  }
  //else ConsoleOutput("Unknown Ryokucha engine.");
  ConsoleOutput("vnreng:StudioRyokucha: failed");
  return true;
}
void InsertRyokuchaHook()
{
  //ConsoleOutput("Probably Ryokucha. Wait for text.");
  trigger_fun_ = InsertRyokuchaDynamicHook;
  SwitchTrigger(true);
  ConsoleOutput("vnreng: TRIGGER Ryokucha");
}

/**
 *  jichi 5/11/2014: Hook to the beginning of a function
 *
 *  Executable description shows "AVGEngineV2"
 *
 *  Cached wrong text can also be found in GetGlyphOutlineW.
 *
 *  4/27/2015 old logic:
 *  1. find the following location
 *     00A78144   66:833C70 00     CMP WORD PTR DS:[EAX+ESI*2],0x0
 *     i.e. 0x66833C7000
 *     There are several matches, the first one is used.
 *  2. find the first push operation after it
 *  3. find the function call after push, and hook to it
 *     The text is in the arg1, which is character by character
 *
 *  But in the new game since ウルスラグ� there the function call is not immediately after 0x66833C7000 any more.
 *  My own way to find the function to hook is as follows:
 *  1. find the following location
 *     00A78144   66:833C70 00     CMP WORD PTR DS:[EAX+ESI*2],0x0
 *     i.e. 0x66833C7000
 *     There are several matches, the first one is used.
 *  2. Use Ollydbg to debug step by step until the first function call is encountered
 *     Then, the text character is directly on the stack
 *
 *  Here's an example of Demonion II (reladdr = 0x18c540):
 *  The text is displayed character by character.
 *  sub_58C540 proc near
 *  arg_0 = dword ptr  8 // LPCSTR with 1 character
 *
 *  0138C540  /$ 55             PUSH EBP
 *  0138C541  |. 8BEC           MOV EBP,ESP
 *  0138C543  |. 83E4 F8        AND ESP,0xFFFFFFF8
 *  0138C546  |. 8B43 0C        MOV EAX,DWORD PTR DS:[EBX+0xC]
 *  0138C549  |. 83EC 08        SUB ESP,0x8
 *  0138C54C  |. 56             PUSH ESI
 *  0138C54D  |. 57             PUSH EDI
 *  0138C54E  |. 85C0           TEST EAX,EAX
 *  0138C550  |. 75 04          JNZ SHORT demonion.0138C556
 *  0138C552  |. 33F6           XOR ESI,ESI
 *  0138C554  |. EB 18          JMP SHORT demonion.0138C56E
 *  0138C556  |> 8B4B 14        MOV ECX,DWORD PTR DS:[EBX+0x14]
 *  0138C559  |. 2BC8           SUB ECX,EAX
 *  0138C55B  |. B8 93244992    MOV EAX,0x92492493
 *  0138C560  |. F7E9           IMUL ECX
 *  0138C562  |. 03D1           ADD EDX,ECX
 *  0138C564  |. C1FA 04        SAR EDX,0x4
 *  0138C567  |. 8BF2           MOV ESI,EDX
 *  0138C569  |. C1EE 1F        SHR ESI,0x1F
 *  0138C56C  |. 03F2           ADD ESI,EDX
 *  0138C56E  |> 8B7B 10        MOV EDI,DWORD PTR DS:[EBX+0x10]
 *  0138C571  |. 8BCF           MOV ECX,EDI
 *  0138C573  |. 2B4B 0C        SUB ECX,DWORD PTR DS:[EBX+0xC]
 *  0138C576  |. B8 93244992    MOV EAX,0x92492493
 *  0138C57B  |. F7E9           IMUL ECX
 *  0138C57D  |. 03D1           ADD EDX,ECX
 *  0138C57F  |. C1FA 04        SAR EDX,0x4
 *  0138C582  |. 8BC2           MOV EAX,EDX
 *  0138C584  |. C1E8 1F        SHR EAX,0x1F
 *  0138C587  |. 03C2           ADD EAX,EDX
 *  0138C589  |. 3BC6           CMP EAX,ESI
 *  0138C58B  |. 73 2F          JNB SHORT demonion.0138C5BC
 *  0138C58D  |. C64424 08 00   MOV BYTE PTR SS:[ESP+0x8],0x0
 *  0138C592  |. 8B4C24 08      MOV ECX,DWORD PTR SS:[ESP+0x8]
 *  0138C596  |. 8B5424 08      MOV EDX,DWORD PTR SS:[ESP+0x8]
 *  0138C59A  |. 51             PUSH ECX
 *  0138C59B  |. 8B4D 08        MOV ECX,DWORD PTR SS:[EBP+0x8]
 *  0138C59E  |. 52             PUSH EDX
 *  0138C59F  |. B8 01000000    MOV EAX,0x1
 *  0138C5A4  |. 8BD7           MOV EDX,EDI
 *  0138C5A6  |. E8 F50E0000    CALL demonion.0138D4A0
 *  0138C5AB  |. 83C4 08        ADD ESP,0x8
 *  0138C5AE  |. 83C7 1C        ADD EDI,0x1C
 *  0138C5B1  |. 897B 10        MOV DWORD PTR DS:[EBX+0x10],EDI
 *  0138C5B4  |. 5F             POP EDI
 *  0138C5B5  |. 5E             POP ESI
 *  0138C5B6  |. 8BE5           MOV ESP,EBP
 *  0138C5B8  |. 5D             POP EBP
 *  0138C5B9  |. C2 0400        RETN 0x4
 *  0138C5BC  |> 397B 0C        CMP DWORD PTR DS:[EBX+0xC],EDI
 *  0138C5BF  |. 76 05          JBE SHORT demonion.0138C5C6
 *  0138C5C1  |. E8 1B060D00    CALL demonion.0145CBE1
 *  0138C5C6  |> 8B03           MOV EAX,DWORD PTR DS:[EBX]
 *  0138C5C8  |. 57             PUSH EDI                                 ; /Arg4
 *  0138C5C9  |. 50             PUSH EAX                                 ; |Arg3
 *  0138C5CA  |. 8B45 08        MOV EAX,DWORD PTR SS:[EBP+0x8]           ; |
 *  0138C5CD  |. 50             PUSH EAX                                 ; |Arg2
 *  0138C5CE  |. 8D4C24 14      LEA ECX,DWORD PTR SS:[ESP+0x14]          ; |
 *  0138C5D2  |. 51             PUSH ECX                                 ; |Arg1
 *  0138C5D3  |. 8BC3           MOV EAX,EBX                              ; |
 *  0138C5D5  |. E8 D6010000    CALL demonion.0138C7B0                   ; \demonion.0138C7B0
 *  0138C5DA  |. 5F             POP EDI
 *  0138C5DB  |. 5E             POP ESI
 *  0138C5DC  |. 8BE5           MOV ESP,EBP
 *  0138C5DE  |. 5D             POP EBP
 *  0138C5DF  \. C2 0400        RETN 0x4
 *
 *  4/26/2015  ウルスラグ� *  base = 0xa30000, old hook addr = 0xbe6360
 *
 *  00A7813A   EB 02            JMP SHORT .00A7813E
 *  00A7813C   8BC7             MOV EAX,EDI
 *  00A7813E   8BB3 E4020000    MOV ESI,DWORD PTR DS:[EBX+0x2E4]
 *  00A78144   66:833C70 00     CMP WORD PTR DS:[EAX+ESI*2],0x0  ; jich: here's the first found segment
 *  00A78149   74 36            JE SHORT .00A78181
 *  00A7814B   837F 14 08       CMP DWORD PTR DS:[EDI+0x14],0x8
 *  00A7814F   72 08            JB SHORT .00A78159
 *  00A78151   8B07             MOV EAX,DWORD PTR DS:[EDI]
 *
 *  00A7883A   24 3C            AND AL,0x3C
 *  00A7883C   50               PUSH EAX
 *  00A7883D   C74424 4C 000000>MOV DWORD PTR SS:[ESP+0x4C],0x0
 *  00A78845   0F5B             ???                                      ; Unknown command
 *  00A78847   C9               LEAVE
 *  00A78848   F3:0F114424 44   MOVSS DWORD PTR SS:[ESP+0x44],XMM0
 *  00A7884E   F3:0F114C24 48   MOVSS DWORD PTR SS:[ESP+0x48],XMM1
 *  00A78854   E8 37040000      CALL .00A78C90  ; jichi: here's the target function to hook to, text char on the stack[0]
 *  00A78859   A1 888EDD00      MOV EAX,DWORD PTR DS:[0xDD8E88]
 *  00A7885E   A8 01            TEST AL,0x1
 *  00A78860   75 30            JNZ SHORT .00A78892
 *  00A78862   83C8 01          OR EAX,0x1
 *  00A78865   A3 888EDD00      MOV DWORD PTR DS:[0xDD8E88],EAX
 *
 *  Here's the new function call:
 *  00A78C8A   CC               INT3
 *  00A78C8B   CC               INT3
 *  00A78C8C   CC               INT3
 *  00A78C8D   CC               INT3
 *  00A78C8E   CC               INT3
 *  00A78C8F   CC               INT3
 *  00A78C90   55               PUSH EBP
 *  00A78C91   8BEC             MOV EBP,ESP
 *  00A78C93   56               PUSH ESI
 *  00A78C94   8BF1             MOV ESI,ECX
 *  00A78C96   57               PUSH EDI
 *  00A78C97   8B7D 08          MOV EDI,DWORD PTR SS:[EBP+0x8]
 *  00A78C9A   8B4E 04          MOV ECX,DWORD PTR DS:[ESI+0x4]
 *  00A78C9D   3BF9             CMP EDI,ECX
 *  00A78C9F   73 76            JNB SHORT .00A78D17
 *  00A78CA1   8B06             MOV EAX,DWORD PTR DS:[ESI]
 *  00A78CA3   3BC7             CMP EAX,EDI
 *  00A78CA5   77 70            JA SHORT .00A78D17
 *  00A78CA7   2BF8             SUB EDI,EAX
 *  00A78CA9   B8 93244992      MOV EAX,0x92492493
 *  00A78CAE   F7EF             IMUL EDI
 *  00A78CB0   03D7             ADD EDX,EDI
 *  00A78CB2   C1FA 04          SAR EDX,0x4
 *  00A78CB5   8BFA             MOV EDI,EDX
 *  00A78CB7   C1EF 1F          SHR EDI,0x1F
 *  00A78CBA   03FA             ADD EDI,EDX
 *  00A78CBC   3B4E 08          CMP ECX,DWORD PTR DS:[ESI+0x8]
 *  00A78CBF   75 09            JNZ SHORT .00A78CCA
 *  00A78CC1   6A 01            PUSH 0x1
 *  00A78CC3   8BCE             MOV ECX,ESI
 *  00A78CC5   E8 36030000      CALL .00A79000
 *  00A78CCA   8B56 04          MOV EDX,DWORD PTR DS:[ESI+0x4]
 *  00A78CCD   8D0CFD 00000000  LEA ECX,DWORD PTR DS:[EDI*8]
 *  00A78CD4   2BCF             SUB ECX,EDI
 *  00A78CD6   8B3E             MOV EDI,DWORD PTR DS:[ESI]
 *  00A78CD8   85D2             TEST EDX,EDX
 *  00A78CDA   74 7B            JE SHORT .00A78D57
 *  00A78CDC   66:8B048F        MOV AX,WORD PTR DS:[EDI+ECX*4]
 *  00A78CE0   66:8902          MOV WORD PTR DS:[EDX],AX
 *  00A78CE3   8B448F 04        MOV EAX,DWORD PTR DS:[EDI+ECX*4+0x4]
 *  00A78CE7   8942 04          MOV DWORD PTR DS:[EDX+0x4],EAX
 *  00A78CEA   8B448F 08        MOV EAX,DWORD PTR DS:[EDI+ECX*4+0x8]
 *  00A78CEE   8942 08          MOV DWORD PTR DS:[EDX+0x8],EAX
 *  00A78CF1   8B448F 0C        MOV EAX,DWORD PTR DS:[EDI+ECX*4+0xC]
 *  00A78CF5   8942 0C          MOV DWORD PTR DS:[EDX+0xC],EAX
 *  00A78CF8   C742 10 00000000 MOV DWORD PTR DS:[EDX+0x10],0x0
 *  00A78CFF   8B448F 14        MOV EAX,DWORD PTR DS:[EDI+ECX*4+0x14]
 *  00A78D03   8942 14          MOV DWORD PTR DS:[EDX+0x14],EAX
 *  00A78D06   8A448F 18        MOV AL,BYTE PTR DS:[EDI+ECX*4+0x18]
 *  00A78D0A   8842 18          MOV BYTE PTR DS:[EDX+0x18],AL
 *  00A78D0D   8346 04 1C       ADD DWORD PTR DS:[ESI+0x4],0x1C
 *  00A78D11   5F               POP EDI
 *  00A78D12   5E               POP ESI
 *  00A78D13   5D               POP EBP
 *  00A78D14   C2 0400          RETN 0x4
 *  00A78D17   3B4E 08          CMP ECX,DWORD PTR DS:[ESI+0x8]
 *  00A78D1A   75 09            JNZ SHORT .00A78D25
 *  00A78D1C   6A 01            PUSH 0x1
 *  00A78D1E   8BCE             MOV ECX,ESI
 *  00A78D20   E8 DB020000      CALL .00A79000
 *  00A78D25   8B4E 04          MOV ECX,DWORD PTR DS:[ESI+0x4]
 *  00A78D28   85C9             TEST ECX,ECX
 *  00A78D2A   74 2B            JE SHORT .00A78D57
 *  00A78D2C   66:8B07          MOV AX,WORD PTR DS:[EDI]
 *  00A78D2F   66:8901          MOV WORD PTR DS:[ECX],AX
 *  00A78D32   8B47 04          MOV EAX,DWORD PTR DS:[EDI+0x4]
 *  00A78D35   8941 04          MOV DWORD PTR DS:[ECX+0x4],EAX
 *  00A78D38   8B47 08          MOV EAX,DWORD PTR DS:[EDI+0x8]
 *  00A78D3B   8941 08          MOV DWORD PTR DS:[ECX+0x8],EAX
 *  00A78D3E   8B47 0C          MOV EAX,DWORD PTR DS:[EDI+0xC]
 *  00A78D41   8941 0C          MOV DWORD PTR DS:[ECX+0xC],EAX
 *  00A78D44   C741 10 00000000 MOV DWORD PTR DS:[ECX+0x10],0x0
 *  00A78D4B   8B47 14          MOV EAX,DWORD PTR DS:[EDI+0x14]
 *  00A78D4E   8941 14          MOV DWORD PTR DS:[ECX+0x14],EAX
 *  00A78D51   8A47 18          MOV AL,BYTE PTR DS:[EDI+0x18]
 *  00A78D54   8841 18          MOV BYTE PTR DS:[ECX+0x18],AL
 *  00A78D57   8346 04 1C       ADD DWORD PTR DS:[ESI+0x4],0x1C
 *  00A78D5B   5F               POP EDI
 *  00A78D5C   5E               POP ESI
 *  00A78D5D   5D               POP EBP
 *  00A78D5E   C2 0400          RETN 0x4
 *  00A78D61   CC               INT3
 *  00A78D62   CC               INT3
 *  00A78D63   CC               INT3
 *  00A78D64   CC               INT3
 *  00A78D65   CC               INT3
 */
static bool InsertGXP1Hook()
{
  union {
    DWORD i;
    DWORD *id;
    BYTE *ib;
  };
  //__asm int 3
  for (i = module_base_ + 0x1000; i < module_limit_ - 4; i++) {
    // jichi example:
    // 00A78144   66:833C70 00     CMP WORD PTR DS:[EAX+ESI*2],0x0

    //find cmp word ptr [esi*2+eax],0
    if (*id != 0x703c8366)
      continue;
    i += 4;
    if (*ib != 0)
      continue;
    i++;
    DWORD j = i + 0x200;
    j = j < (module_limit_ - 8) ? j : (module_limit_ - 8);

    DWORD flag = false;
    while (i < j) {
      DWORD k = disasm(ib);
      if (k == 0)
        break;
      if (k == 1 && (*ib & 0xf8) == 0x50) { // push reg
        flag = true;
        break;
      }
      i += k;
    }
    if (flag)
      while (i < j) {
        if (*ib == 0xe8) { // jichi: find first long call after the push operation
          i++;
          DWORD addr = *id + i + 4;
          if (addr > module_base_ && addr < module_limit_) {
            HookParam hp = {};
            hp.address = addr;
            //hp.type = USING_UNICODE|DATA_INDIRECT;
            hp.type = USING_UNICODE|DATA_INDIRECT|NO_CONTEXT|FIXING_SPLIT; // jichi 4/25/2015: Fixing split
            hp.length_offset = 1;
            hp.offset = 4;

            //GROWL_DWORD3(hp.address, module_base_, hp.address - module_base_);

            //DWORD call = Util::FindCallAndEntryAbs(hp.address, module_limit_ - module_base_, module_base_, 0xec81); // zero
            //DWORD call = Util::FindCallAndEntryAbs(hp.address, module_limit_ - module_base_, module_base_, 0xec83); // zero
            //DWORD call = Util::FindCallAndEntryAbs(hp.address, module_limit_ - module_base_, module_base_, 0xec8b55); // zero
            //GROWL_DWORD3(call, module_base_, call - module_base_);

            ConsoleOutput("vnreng: INSERT GXP");
            NewHook(hp, "GXP");

            // jichi 5/13/2015: Disable hooking to GetGlyphOutlineW
            // FIXME: GetGlyphOutlineW can extract name, but GXP cannot
            ConsoleOutput("vnreng:GXP: disable GDI hooks");
            DisableGDIHooks();
            return true;
          }
        }
        i++;
      }
  }
  //ConsoleOutput("Unknown GXP engine.");
  ConsoleOutput("vnreng:GXP: failed");
  return false;
}

static bool InsertGXP2Hook()
{
  ULONG startAddress, stopAddress;
  if (!NtInspect::getProcessMemoryRange(&startAddress, &stopAddress)) {
    ConsoleOutput("vnreng:GXP2: failed to get memory range");
    return false;
  }

  // pattern = 0x0f5bc9f30f11442444f30f114c2448e8
  const BYTE bytes[] = {
     0x0f,0x5b,                      // 00A78845   0F5B             ???                                      ; Unknown command
     0xc9,                           // 00A78847   C9               LEAVE
     0xf3,0x0f,0x11,0x44,0x24, 0x44, // 00A78848   F3:0F114424 44   MOVSS DWORD PTR SS:[ESP+0x44],XMM0
     0xf3,0x0f,0x11,0x4c,0x24, 0x48, // 00A7884E   F3:0F114C24 48   MOVSS DWORD PTR SS:[ESP+0x48],XMM1
     0xe8 //37040000                 // 00A78854   E8 37040000      CALL .00A78C90  ; jichi: here's the target function to hook to, text char on the stack[0]
  };
  enum { addr_offset = sizeof(bytes) - 1 }; // 0x00a78854 - 0x00a78845
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress);
  if (!addr) {
    ConsoleOutput("vnreng:GXP2: pattern not found");
    return false;
  }

  HookParam hp = {};
  hp.address = addr + addr_offset;
  hp.type = USING_UNICODE|NO_CONTEXT|DATA_INDIRECT|FIXING_SPLIT;
  hp.length_offset = 1;
  ConsoleOutput("vnreng: INSERT GXP2");
  NewHook(hp, "GXP2");
  ConsoleOutput("vnreng:GXP: disable GDI hooks");
  DisableGDIHooks();
  return true;
}

bool InsertGXPHook()
{
  // GXP1 and GXP2 are harmless to each other
  bool ok = InsertGXP1Hook();
  ok = InsertGXP2Hook() || ok;
  return ok;
}

namespace { // unnamed, for Anex86
BYTE JIS_tableH[0x80] = {
  0x00,0x81,0x81,0x82,0x82,0x83,0x83,0x84,
  0x84,0x85,0x85,0x86,0x86,0x87,0x87,0x88,
  0x88,0x89,0x89,0x8a,0x8a,0x8b,0x8b,0x8c,
  0x8c,0x8d,0x8d,0x8e,0x8e,0x8f,0x8f,0x90,
  0x90,0x91,0x91,0x92,0x92,0x93,0x93,0x94,
  0x94,0x95,0x95,0x96,0x96,0x97,0x97,0x98,
  0x98,0x99,0x99,0x9a,0x9a,0x9b,0x9b,0x9c,
  0x9c,0x9d,0x9d,0x9e,0x9e,0xdf,0xdf,0xe0,
  0xe0,0xe1,0xe1,0xe2,0xe2,0xe3,0xe3,0xe4,
  0xe4,0xe5,0xe5,0xe6,0xe6,0xe7,0xe7,0xe8,
  0xe8,0xe9,0xe9,0xea,0xea,0xeb,0xeb,0xec,
  0xec,0xed,0xed,0xee,0xee,0xef,0xef,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,0x00,0x00,0x00,0x00,0x00
};

BYTE JIS_tableL[0x80] = {
  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,0x00,0x00,0x00,0x00,
  0x00,0x40,0x41,0x42,0x43,0x44,0x45,0x46,
  0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,
  0x4f,0x50,0x51,0x52,0x53,0x54,0x55,0x56,
  0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,
  0x5f,0x60,0x61,0x62,0x63,0x64,0x65,0x66,
  0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,
  0x6f,0x70,0x71,0x72,0x73,0x74,0x75,0x76,
  0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,
  0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,
  0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
  0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,
  0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x00,
};

void SpecialHookAnex86(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  __asm
  {
    mov eax, esp_base
    mov ecx, [eax - 0xC]
    cmp byte ptr [ecx + 0xE], 0
    jnz _fin
    movzx ebx, byte ptr [ecx + 0xC] ; Low byte
    movzx edx, byte ptr [ecx + 0xD] ; High byte
    test edx,edx
    jnz _jis_char
    mov eax,data
    mov [eax],ebx
    mov eax, len
    mov [eax], 1
    jmp _fin
_jis_char:
    cmp ebx,0x7E
    ja _fin
    cmp edx,0x7E
    ja _fin
    test dl,1
    lea eax, [ebx + 0x7E]
    movzx ecx, byte ptr [JIS_tableL + ebx]
    cmovnz eax, ecx
    mov ah, byte ptr [JIS_tableH + edx]
    ror ax,8
    mov ecx, data
    mov [ecx], eax
    mov ecx, len
    mov [ecx], 2
_fin:
  }

}
} // unnamed namespace
bool InsertAnex86Hook()
{
  const DWORD dwords[] = {0x618ac033,0x0d418a0c}; // jichi 12/25/2013: Remove static keyword
  for (DWORD i = module_base_ + 0x1000; i < module_limit_ - 8; i++)
    if (*(DWORD *)i == dwords[0])
      if (*(DWORD *)(i + 4) == dwords[1]) {
        HookParam hp = {};
        hp.address = i;
        hp.text_fun = SpecialHookAnex86;
        //hp.type = EXTERN_HOOK;
        hp.length_offset = 1;
        ConsoleOutput("vnreng: INSERT Anex86");
        NewHook(hp, "Anex86");
        return true;
      }
  ConsoleOutput("vnreng:Anex86: failed");
  return false;
}

/**
 *  jichi 9/5/2013: NEXTON games with aInfo.db
 *  Sample games:
 *  - /HA-C@4D69E:InnocentBullet.exe (イノセントバレッ�)
 *  - /HA-C@40414C:ImoutoBancho.exe (妹番長)
 *
 *  See: http://ja.wikipedia.org/wiki/ネクストン
 *  See (CaoNiMaGeBi): http://tieba.baidu.com/p/2576241908
 *
 *  Old:
 *  md5 = 85ac031f2539e1827d9a1d9fbde4023d
 *  hcode = /HA-C@40414C:ImoutoBancho.exe
 *  - addr: 4211020 (0x40414c)
 *  - module: 1051997988 (0x3eb43724)
 *  - length_offset: 1
 *  - off: 4294967280 (0xfffffff0) = -0x10
 *  - split: 0
 *  - type: 68 (0x44)
 *
 *  New (11/7/2013):
 *  /HA-20:4@583DE:MN2.EXE (NEW)
 *  - addr: 361438 (0x583de)
 *  - module: 3436540819
 *  - length_offset: 1
 *  - off: 4294967260 (0xffffffdc) = -0x24
 *  - split: 4
 *  - type: 84 (0x54)
 */

bool InsertNextonHook()
{
#if 0
  // 0x8944241885c00f84
  const BYTE bytes[] = {
    //0xe8 //??,??,??,??,      00804147   e8 24d90100      call imoutoba.00821a70
    0x89,0x44,0x24, 0x18,   // 0080414c   894424 18        mov dword ptr ss:[esp+0x18],eax; hook here
    0x85,0xc0,              // 00804150   85c0             test eax,eax
    0x0f,0x84               // 00804152  ^0f84 c0feffff    je imoutoba.00804018
  };
  //enum { addr_offset = 0 };
  ULONG addr = module_base_; //- sizeof(bytes);
  do {
    addr += sizeof(bytes); // ++ so that each time return diff address
    ULONG range = min(module_limit_ - addr, MAX_REL_ADDR);
    addr = MemDbg::findBytes(bytes, sizeof(bytes), addr, addr + range);
    if (!addr) {
      ConsoleOutput("vnreng:NEXTON: pattern not exist");
      return false;
    }

    //const BYTE hook_ins[] = {
    //  0x57,       // 00804144   57               push edi
    //  0x8b,0xc3,  // 00804145   8bc3             mov eax,ebx
    //  0xe8 //??,??,??,??,      00804147   e8 24d90100      call imoutoba.00821a70
    //};
  } while(0xe8c38b57 != *(DWORD *)(addr - 8));
#endif // 0
  const BYTE bytes[] = {
    0x57,                   // 0044d696   57               push edi
    0x8b,0xc3,              // 0044d697   8bc3             mov eax,ebx
    0xe8, XX4,              // 0044d699   e8 6249fdff      call .00422000
    0x89,0x44,0x24, 0x18,   // 0044d69e   894424 18        mov dword ptr ss:[esp+0x18],eax ; jichi: this is the ith hook point
    0x85,0xc0,              // 0044d6a2   85c0             test eax,eax
    0x0f,0x84 //c2feffff    // 0044d6a4  ^0f84 c2feffff    je .0044d56c
  };
  enum { addr_offset = 0x0044d69e - 0x0044d696 }; // = 8
  ULONG startAddress, stopAddress;
  if (!NtInspect::getProcessMemoryRange(&startAddress, &stopAddress)) {
    ConsoleOutput("vnreng:NEXTON: failed to get memory range");
    return false;
  }
  ULONG addr = MemDbg::matchBytes(bytes, sizeof(bytes), startAddress, stopAddress);
  if (!addr) {
    ConsoleOutput("vnreng:NEXTON: pattern not exist");
    return false;
  }

  //addr = MemDbg::findEnclosingAlignedFunction(addr); // range is around 50, use 80
  //if (!addr) {
  //  ConsoleOutput("vnreng:NEXTON: enclosing function not found");
  //  return false;
  //}

  //GROWL_DWORD3(module_base_, addr, *(DWORD *)(addr-8));
  //HookParam hp = {};
  //hp.address = addr;
  //hp.offset = 4; // text in arg1
  //hp.split = 4;

  HookParam hp = {};
  hp.address = addr + addr_offset;
  hp.length_offset = 1;
  //hp.offset = -0x10;
  //hp.type = BIG_ENDIAN; // 4

  // 魔王のくせに生イキ�っ �今度は性戦ぽ  // CheatEngine search for byte array: 8944241885C00F84
  //addr = 0x4583de; // wrong
  //addr = 0x5460ba;
  //addr = 0x5f3d8a;
  //addr = 0x768776;
  //addr = 0x7a5319;

  hp.offset = -0x24;
  hp.split = 4;
  hp.type = BIG_ENDIAN|USING_SPLIT; // 0x54

  // Indirect is needed for new games,
  // Such as: /HA-C*0@4583DE for 「魔王のくせに生イキ�っ���  //hp.type = BIG_ENDIAN|DATA_INDIRECT; // 12
  //hp.type = USING_UNICODE;
  //GROWL_DWORD3(addr, -hp.offset, hp.type);

  ConsoleOutput("vnreng: INSERT NEXTON");
  NewHook(hp, "NEXTON");

  //ConsoleOutput("vnreng:NEXTON: disable GDI hooks"); // There are no GDI functions hooked though
  //DisableGDIHooks(); // disable GetGlyphOutlineA
  return true;
}

/** jichi 8/17/2014 Nexton1
 *  Sample games:
 *  - [Nomad][071026] 淫烙�巫女 Trial
 *
 *  Debug method: text are prefetched into memory. Add break point to it.
 *
 *  GetGlyphOutlineA is called, but no correct text.
 *
 *  There are so many good hooks. The shortest function was picked,as follows:
 *  0041974e   cc               int3
 *  0041974f   cc               int3
 *  00419750   56               push esi    ; jichi: hook here, text in arg1
 *  00419751   8b7424 08        mov esi,dword ptr ss:[esp+0x8]
 *  00419755   8bc6             mov eax,esi
 *  00419757   57               push edi
 *  00419758   8d78 01          lea edi,dword ptr ds:[eax+0x1]
 *  0041975b   eb 03            jmp short inrakutr.00419760
 *  0041975d   8d49 00          lea ecx,dword ptr ds:[ecx]
 *  00419760   8a10             mov dl,byte ptr ds:[eax] ; jichi: eax is the text
 *  00419762   83c0 01          add eax,0x1
 *  00419765   84d2             test dl,dl
 *  00419767  ^75 f7            jnz short inrakutr.00419760
 *  00419769   2bc7             sub eax,edi
 *  0041976b   50               push eax
 *  0041976c   56               push esi
 *  0041976d   83c1 04          add ecx,0x4
 *  00419770   e8 eb85feff      call inrakutr.00401d60
 *  00419775   5f               pop edi
 *  00419776   5e               pop esi
 *  00419777   c2 0400          retn 0x4
 *  0041977a   cc               int3
 *  0041977b   cc               int3
 *  0041977c   cc               int3
 *
 *  Runtime stack: this function takes two arguments. Text address is in arg1.
 *
 *  Other possible hooks are as follows:
 *  00460caf   53               push ebx
 *  00460cb0   c700 16000000    mov dword ptr ds:[eax],0x16
 *  00460cb6   e8 39feffff      call inrakutr.00460af4
 *  00460cbb   83c4 14          add esp,0x14
 *  00460cbe   385d fc          cmp byte ptr ss:[ebp-0x4],bl
 *  00460cc1   74 07            je short inrakutr.00460cca
 *  00460cc3   8b45 f8          mov eax,dword ptr ss:[ebp-0x8]
 *  00460cc6   8360 70 fd       and dword ptr ds:[eax+0x70],0xfffffffd
 *  00460cca   33c0             xor eax,eax
 *  00460ccc   eb 2c            jmp short inrakutr.00460cfa
 *  00460cce   0fb601           movzx eax,byte ptr ds:[ecx] ; jichi: here, ecx
 *  00460cd1   8b55 f4          mov edx,dword ptr ss:[ebp-0xc]
 *  00460cd4   f64410 1d 04     test byte ptr ds:[eax+edx+0x1d],0x4
 *  00460cd9   74 0e            je short inrakutr.00460ce9
 *  00460cdb   8d51 01          lea edx,dword ptr ds:[ecx+0x1]
 *  00460cde   381a             cmp byte ptr ds:[edx],bl
 *  00460ce0   74 07            je short inrakutr.00460ce9
 *  00460ce2   c1e0 08          shl eax,0x8
 *  00460ce5   8bf0             mov esi,eax
 *  00460ce7   8bca             mov ecx,edx
 *  00460ce9   0fb601           movzx eax,byte ptr ds:[ecx]
 *  00460cec   03c6             add eax,esi
 *  00460cee   385d fc          cmp byte ptr ss:[ebp-0x4],bl
 *  00460cf1   74 07            je short inrakutr.00460cfa
 *  00460cf3   8b4d f8          mov ecx,dword ptr ss:[ebp-0x8]
 *  00460cf6   8361 70 fd       and dword ptr ds:[ecx+0x70],0xfffffffd
 *  00460cfa   5e               pop esi
 *  00460cfb   5b               pop ebx
 *  00460cfc   c9               leave
 *  00460cfd   c3               retn
 *
 *  00460d41   74 05            je short inrakutr.00460d48
 *  00460d43   381e             cmp byte ptr ds:[esi],bl
 *  00460d45   74 01            je short inrakutr.00460d48
 *  00460d47   46               inc esi
 *  00460d48   8bc6             mov eax,esi
 *  00460d4a   5e               pop esi
 *  00460d4b   5b               pop ebx
 *  00460d4c   c3               retn
 *  00460d4d   56               push esi
 *  00460d4e   8b7424 08        mov esi,dword ptr ss:[esp+0x8]
 *  00460d52   0fb606           movzx eax,byte ptr ds:[esi] ; jichi: esi & ebp
 *  00460d55   50               push eax
 *  00460d56   e8 80fcffff      call inrakutr.004609db
 *  00460d5b   85c0             test eax,eax
 *  00460d5d   59               pop ecx
 *  00460d5e   74 0b            je short inrakutr.00460d6b
 *  00460d60   807e 01 00       cmp byte ptr ds:[esi+0x1],0x0
 *  00460d64   74 05            je short inrakutr.00460d6b
 *  00460d66   6a 02            push 0x2
 *  00460d68   58               pop eax
 *  00460d69   5e               pop esi
 *  00460d6a   c3               retn
 *
 *  00460d1d   53               push ebx
 *  00460d1e   53               push ebx
 *  00460d1f   53               push ebx
 *  00460d20   53               push ebx
 *  00460d21   53               push ebx
 *  00460d22   c700 16000000    mov dword ptr ds:[eax],0x16
 *  00460d28   e8 c7fdffff      call inrakutr.00460af4
 *  00460d2d   83c4 14          add esp,0x14
 *  00460d30   33c0             xor eax,eax
 *  00460d32   eb 16            jmp short inrakutr.00460d4a
 *  00460d34   0fb606           movzx eax,byte ptr ds:[esi] ; jichi: esi, ebp
 *  00460d37   50               push eax
 *  00460d38   e8 9efcffff      call inrakutr.004609db
 *  00460d3d   46               inc esi
 *  00460d3e   85c0             test eax,eax
 *  00460d40   59               pop ecx
 *  00460d41   74 05            je short inrakutr.00460d48
 *  00460d43   381e             cmp byte ptr ds:[esi],bl
 *  00460d45   74 01            je short inrakutr.00460d48
 *  00460d47   46               inc esi
 *
 *  0042c59f   cc               int3
 *  0042c5a0   56               push esi
 *  0042c5a1   8bf1             mov esi,ecx
 *  0042c5a3   8b86 cc650000    mov eax,dword ptr ds:[esi+0x65cc]
 *  0042c5a9   8b50 1c          mov edx,dword ptr ds:[eax+0x1c]
 *  0042c5ac   57               push edi
 *  0042c5ad   8b7c24 0c        mov edi,dword ptr ss:[esp+0xc]
 *  0042c5b1   8d8e cc650000    lea ecx,dword ptr ds:[esi+0x65cc]
 *  0042c5b7   57               push edi
 *  0042c5b8   ffd2             call edx
 *  0042c5ba   8bc7             mov eax,edi
 *  0042c5bc   8d50 01          lea edx,dword ptr ds:[eax+0x1]
 *  0042c5bf   90               nop
 *  0042c5c0   8a08             mov cl,byte ptr ds:[eax] ; jichi: here eax
 *  0042c5c2   83c0 01          add eax,0x1
 *  0042c5c5   84c9             test cl,cl
 *  0042c5c7  ^75 f7            jnz short inrakutr.0042c5c0
 *  0042c5c9   2bc2             sub eax,edx
 *  0042c5cb   50               push eax
 *  0042c5cc   57               push edi
 *  0042c5cd   8d8e 24660000    lea ecx,dword ptr ds:[esi+0x6624]
 *  0042c5d3   e8 8857fdff      call inrakutr.00401d60
 *  0042c5d8   8b86 b4660000    mov eax,dword ptr ds:[esi+0x66b4]
 *  0042c5de   85c0             test eax,eax
 *  0042c5e0   74 0d            je short inrakutr.0042c5ef
 *  0042c5e2   8b8e b8660000    mov ecx,dword ptr ds:[esi+0x66b8]
 *  0042c5e8   2bc8             sub ecx,eax
 *  0042c5ea   c1f9 02          sar ecx,0x2
 *  0042c5ed   75 05            jnz short inrakutr.0042c5f4
 *  0042c5ef   e8 24450300      call inrakutr.00460b18
 *  0042c5f4   8b96 b4660000    mov edx,dword ptr ds:[esi+0x66b4]
 *  0042c5fa   8b0a             mov ecx,dword ptr ds:[edx]
 *  0042c5fc   8b01             mov eax,dword ptr ds:[ecx]
 *  0042c5fe   8b50 30          mov edx,dword ptr ds:[eax+0x30]
 *  0042c601   ffd2             call edx
 *  0042c603   8b06             mov eax,dword ptr ds:[esi]
 *  0042c605   8b90 f8000000    mov edx,dword ptr ds:[eax+0xf8]
 *  0042c60b   6a 00            push 0x0
 *  0042c60d   68 c3164a00      push inrakutr.004a16c3
 *  0042c612   57               push edi
 *  0042c613   8bce             mov ecx,esi
 *  0042c615   ffd2             call edx
 *  0042c617   5f               pop edi
 *  0042c618   5e               pop esi
 *  0042c619   c2 0400          retn 0x4
 *  0042c61c   cc               int3
 *
 *  0041974e   cc               int3
 *  0041974f   cc               int3
 *  00419750   56               push esi
 *  00419751   8b7424 08        mov esi,dword ptr ss:[esp+0x8]
 *  00419755   8bc6             mov eax,esi
 *  00419757   57               push edi
 *  00419758   8d78 01          lea edi,dword ptr ds:[eax+0x1]
 *  0041975b   eb 03            jmp short inrakutr.00419760
 *  0041975d   8d49 00          lea ecx,dword ptr ds:[ecx]
 *  00419760   8a10             mov dl,byte ptr ds:[eax] ; jichi: eax
 *  00419762   83c0 01          add eax,0x1
 *  00419765   84d2             test dl,dl
 *  00419767  ^75 f7            jnz short inrakutr.00419760
 *  00419769   2bc7             sub eax,edi
 *  0041976b   50               push eax
 *  0041976c   56               push esi
 *  0041976d   83c1 04          add ecx,0x4
 *  00419770   e8 eb85feff      call inrakutr.00401d60
 *  00419775   5f               pop edi
 *  00419776   5e               pop esi
 *  00419777   c2 0400          retn 0x4
 *  0041977a   cc               int3
 *  0041977b   cc               int3
 *  0041977c   cc               int3
 *
 *  0042c731   57               push edi
 *  0042c732   ffd0             call eax
 *  0042c734   8bc7             mov eax,edi
 *  0042c736   8d50 01          lea edx,dword ptr ds:[eax+0x1]
 *  0042c739   8da424 00000000  lea esp,dword ptr ss:[esp]
 *  0042c740   8a08             mov cl,byte ptr ds:[eax] ; jichi: eax
 *  0042c742   83c0 01          add eax,0x1
 *  0042c745   84c9             test cl,cl
 *  0042c747  ^75 f7            jnz short inrakutr.0042c740
 *  0042c749   2bc2             sub eax,edx
 *  0042c74b   8bf8             mov edi,eax
 *  0042c74d   e8 fe1d0100      call inrakutr.0043e550
 *  0042c752   8b0d 187f4c00    mov ecx,dword ptr ds:[0x4c7f18]
 *  0042c758   8b11             mov edx,dword ptr ds:[ecx]
 *  0042c75a   8b42 70          mov eax,dword ptr ds:[edx+0x70]
 *  0042c75d   ffd0             call eax
 *  0042c75f   83c0 0a          add eax,0xa
 *  0042c762   0fafc7           imul eax,edi
 *  0042c765   5f               pop edi
 *  0042c766   8986 60660000    mov dword ptr ds:[esi+0x6660],eax
 */
bool InsertNexton1Hook()
{
  // Use accurate stopAddress in case of running out of memory
  // Since the file pattern for Nexton1 is not accurate.
  ULONG startAddress, stopAddress;
  if (!NtInspect::getProcessMemoryRange(&startAddress, &stopAddress)) {
    ConsoleOutput("vnreng:NEXTON1: failed to get memory range");
    return false;
  }
  const BYTE bytes[] = {
    0x56,                  // 00419750   56               push esi    ; jichi: hook here, text in arg1
    0x8b,0x74,0x24, 0x08,  // 00419751   8b7424 08        mov esi,dword ptr ss:[esp+0x8]
    0x8b,0xc6,             // 00419755   8bc6             mov eax,esi
    0x57,                  // 00419757   57               push edi
    0x8d,0x78, 0x01,       // 00419758   8d78 01          lea edi,dword ptr ds:[eax+0x1]
    0xeb, 0x03,            // 0041975b   eb 03            jmp short inrakutr.00419760
    0x8d,0x49, 0x00,       // 0041975d   8d49 00          lea ecx,dword ptr ds:[ecx]
    0x8a,0x10,             // 00419760   8a10             mov dl,byte ptr ds:[eax] ; jichi: eax is the text
    0x83,0xc0, 0x01,       // 00419762   83c0 01          add eax,0x1
    0x84,0xd2,             // 00419765   84d2             test dl,dl
    0x75, 0xf7,            // 00419767  ^75 f7            jnz short inrakutr.00419760
    0x2b,0xc7,             // 00419769   2bc7             sub eax,edi
    0x50,                  // 0041976b   50               push eax
    0x56,                  // 0041976c   56               push esi
    0x83,0xc1, 0x04        // 0041976d   83c1 04          add ecx,0x4
    //0xe8, XX4,           // 00419770   e8 eb85feff      call inrakutr.00401d60
    //0x5f,                // 00419775   5f               pop edi
    //0x5e,                // 00419776   5e               pop esi
    //0xc2, 0x04,0x00      // 00419777   c2 0400          retn 0x4
  };
  enum { addr_offset = 0 }; // distance to the beginning of the function
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress);
  //GROWL_DWORD(addr); // supposed to be 0x4010e0
  if (!addr) {
    ConsoleOutput("vnreng:NEXTON1: pattern not found");
    return false;
  }
  //GROWL_DWORD(addr);

  HookParam hp = {};
  hp.address = addr + addr_offset;
  //hp.length_offset = 1;
  hp.offset = 4; // [esp+4] == arg0
  hp.type = USING_STRING;
  ConsoleOutput("vnreng: INSERT NEXTON1");
  NewHook(hp, "NEXTON1");
  return true;
}

/**
 *  jichi 9/16/2013: a-unicorn / gesen18
 *  See (CaoNiMaGeBi): http://tieba.baidu.com/p/2586681823
 *  Pattern: 2bce8bf8
 *      2bce      sub ecx,esi ; hook here
 *      8bf8      mov eds,eax
 *      8bd1      mov edx,ecx
 *
 *  /HBN-20*0@xxoo
 *  - length_offset: 1
 *  - off: 4294967260 (0xffffffdc)
 *  - type: 1032 (0x408)
 */
bool InsertUnicornHook()
{
  // pattern: 2bce8bf8
  const BYTE bytes[] = {
    0x2b,0xce,  // sub ecx,esi ; hook here
    0x8b,0xf8   // mov edi,eax
  };
  //enum { addr_offset = 0 };
  ULONG range = min(module_limit_ - module_base_, MAX_REL_ADDR);
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_base_ + range);
  if (!addr) {
    ConsoleOutput("vnreng:Unicorn: pattern not exist");
    return false;
  }

  HookParam hp = {};
  hp.type = NO_CONTEXT | DATA_INDIRECT;
  hp.length_offset = 1;
  hp.offset = -0x24; // jichi: text in edi
  hp.address = addr;

  //index = SearchPattern(module_base_, size,ins, sizeof(ins));
  //GROWL_DWORD2(base, index);

  ConsoleOutput("vnreng: INSERT Unicorn");
  NewHook(hp, "Unicorn");
  return true;
}

/**
 *  jichi 10/1/2013: Artemis Engine
 *  See: http://www.ies-net.com/
 *  See (CaoNiMaGeBi): http://tieba.baidu.com/p/2625537737
 *  Pattern:
 *     650a2f 83c4 0c   add esp,0xc ; hook here
 *     650a32 0fb6c0    movzx eax,al
 *     650a35 85c0      test eax,eax
 *     0fb6c0 75 0e     jnz short tsugokaz.0065a47
 *
 *  Wrong: 0x400000 + 0x7c574
 *
 *  //Example: [130927]妹スパイラル /HBN-8*0:14@65589F
 *  Example: ヂ�ウノイイ家�Trial /HBN-8*0:14@650A2F
 *  Note: 0x650a2f > 40000(base) + 20000(limit)
 *  - addr: 0x650a2f
 *  - text_fun: 0x0
 *  - function: 0
 *  - hook_len: 0
 *  - ind: 0
 *  - length_offset: 1
 *  - module: 0
 *  - off: 4294967284 = 0xfffffff4 = -0xc
 *  - recover_len: 0
 *  - split: 20 = 0x14
 *  - split_ind: 0
 *  - type: 1048 = 0x418
 *
 *  @CaoNiMaGeBi:
 *  RECENT GAMES:
 *    [130927]妹スパイラル /HBN-8*0:14@65589F
 *    [130927]サ�ライホルモン
 *    [131025]ヂ�ウノイイ家�/HBN-8*0:14@650A2F (for trial version)
 *    CLIENT ORGANIZAIONS:
 *    CROWD
 *    D:drive.
 *    Hands-Aid Corporation
 *    iMel株式会社
 *    SHANNON
 *    SkyFish
 *    SNACK-FACTORY
 *    team flap
 *    Zodiac
 *    くらむちめ�� *    まかろんソフト
 *    アイヂ�アファクトリー株式会社
 *    カラクリズ�
 *    合赼�社ファーストリー�
 *    有限会社ウルクスへブン
 *    有限会社ロータス
 *    株式会社CUCURI
 *    株式会社アバン
 *    株式会社インタラクヂ�ブブレインズ
 *    株式会社ウィンヂ�ール
 *    株式会社エヴァンジェ
 *    株式会社ポニーキャニオン
 *    株式会社大福エンターヂ�ンメン� */
bool InsertArtemis1Hook()
{
  const BYTE bytes[] = {
    0x83,0xc4, 0x0c, // add esp,0xc ; hook here
    0x0f,0xb6,0xc0,  // movzx eax,al
    0x85,0xc0,       // test eax,eax
    0x75, 0x0e       // jnz XXOO ; it must be 0xe, or there will be duplication
  };
  //enum { addr_offset = 0 };
  ULONG range = min(module_limit_ - module_base_, MAX_REL_ADDR);
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_base_ + range);
  //GROWL_DWORD3(reladdr, module_base_, range);
  if (!addr) {
    ConsoleOutput("vnreng:Artemis1: pattern not exist");
    return false;
  }

  HookParam hp = {};
  hp.address = addr;
  hp.length_offset = 1;
  hp.offset = -0xc;
  hp.split = 0x14;
  hp.type = NO_CONTEXT|DATA_INDIRECT|USING_SPLIT; // 0x418

  //hp.address = 0x650a2f;
  //GROWL_DWORD(hp.address);

  ConsoleOutput("vnreng: INSERT Artemis1");
  NewHook(hp, "Artemis1");
  //ConsoleOutput("Artemis1");
  return true;
}

bool InsertArtemis2Hook()
{
  const BYTE bytes[] = {
    0x55, 0x8B, 0xEC, 0x83, 0xE4, 0xF8, 0x6A, 0xFF, 0x68, XX4, 0x64, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x50, 0x83, 0xEC, 0x28, 0xA1, XX4, 0x33, 0xC4, 0x89, 0x44, 0x24, 0x20, 0x53, 0x56, 0x57, 0xA1, XX4, 0x33, 0xC4, 0x50, 0x8D, 0x44, 0x24, 0x38, 0x64, 0xA3, 0x00, 0x00, 0x00, 0x00, 0x8B, 0xF1, 0x8B, 0x5D, 0x08, 0x8B, 0x4D, 0x0C
  };
  enum { addr_offset = 0 }; // distance to the beginning of the function, which is 0x55 (push ebp)
  ULONG range = min(module_limit_ - module_base_, MAX_REL_ADDR);
  ULONG addr = MemDbg::matchBytes(bytes, sizeof(bytes), module_base_, module_base_ + range);
  if (!addr) {
    ConsoleOutput("vnreng:Artemis2: pattern not found");
    return false;
  }
  addr += addr_offset;
  enum { push_ebp = 0x55 }; // beginning of the function
  if (*(BYTE *)addr != push_ebp) {
    ConsoleOutput("vnreng:Artemis2: beginning of the function not found");
    return false;
  }

  HookParam hp = {};
  hp.address = addr;
  hp.offset = 4;
  hp.type = USING_STRING|NO_CONTEXT;

  ConsoleOutput("vnreng: INSERT Artemis2");
  NewHook(hp, "Artemis2");
  //ConsoleOutput("Artemis2");
  return true;
}

bool InsertArtemisHook()
{ return InsertArtemis1Hook() ||  InsertArtemis2Hook(); }

/**
 *  jichi 1/2/2014: Taskforce2 Engine
 *
 *  Examples:
 *  神�仮)-カミサマカヂ�カリ- 路地裏繚乱編 (1.1)
 *  /HS-8@178872:Taskforce2.exe
 *
 *  00578819   . 50             push eax                                 ; |arg1
 *  0057881a   . c745 f4 cc636b>mov dword ptr ss:[ebp-0xc],taskforc.006b>; |
 *  00578821   . e8 31870000    call taskforc.00580f57                   ; \taskforc.00580f57
 *  00578826   . cc             int3
 *  00578827  /$ 8b4c24 04      mov ecx,dword ptr ss:[esp+0x4]
 *  0057882b  |. 53             push ebx
 *  0057882c  |. 33db           xor ebx,ebx
 *  0057882e  |. 3bcb           cmp ecx,ebx
 *  00578830  |. 56             push esi
 *  00578831  |. 57             push edi
 *  00578832  |. 74 08          je short taskforc.0057883c
 *  00578834  |. 8b7c24 14      mov edi,dword ptr ss:[esp+0x14]
 *  00578838  |. 3bfb           cmp edi,ebx
 *  0057883a  |. 77 1b          ja short taskforc.00578857
 *  0057883c  |> e8 28360000    call taskforc.0057be69
 *  00578841  |. 6a 16          push 0x16
 *  00578843  |. 5e             pop esi
 *  00578844  |. 8930           mov dword ptr ds:[eax],esi
 *  00578846  |> 53             push ebx
 *  00578847  |. 53             push ebx
 *  00578848  |. 53             push ebx
 *  00578849  |. 53             push ebx
 *  0057884a  |. 53             push ebx
 *  0057884b  |. e8 6a050000    call taskforc.00578dba
 *  00578850  |. 83c4 14        add esp,0x14
 *  00578853  |. 8bc6           mov eax,esi
 *  00578855  |. eb 31          jmp short taskforc.00578888
 *  00578857  |> 8b7424 18      mov esi,dword ptr ss:[esp+0x18]
 *  0057885b  |. 3bf3           cmp esi,ebx
 *  0057885d  |. 75 04          jnz short taskforc.00578863
 *  0057885f  |. 8819           mov byte ptr ds:[ecx],bl
 *  00578861  |.^eb d9          jmp short taskforc.0057883c
 *  00578863  |> 8bd1           mov edx,ecx
 *  00578865  |> 8a06           /mov al,byte ptr ds:[esi]
 *  00578867  |. 8802           |mov byte ptr ds:[edx],al
 *  00578869  |. 42             |inc edx
 *  0057886a  |. 46             |inc esi
 *  0057886b  |. 3ac3           |cmp al,bl
 *  0057886d  |. 74 03          |je short taskforc.00578872
 *  0057886f  |. 4f             |dec edi
 *  00578870  |.^75 f3          \jnz short taskforc.00578865
 *  00578872  |> 3bfb           cmp edi,ebx ; jichi: hook here
 *  00578874  |. 75 10          jnz short taskforc.00578886
 *  00578876  |. 8819           mov byte ptr ds:[ecx],bl
 *  00578878  |. e8 ec350000    call taskforc.0057be69
 *  0057887d  |. 6a 22          push 0x22
 *  0057887f  |. 59             pop ecx
 *  00578880  |. 8908           mov dword ptr ds:[eax],ecx
 *  00578882  |. 8bf1           mov esi,ecx
 *  00578884  |.^eb c0          jmp short taskforc.00578846
 *  00578886  |> 33c0           xor eax,eax
 *  00578888  |> 5f             pop edi
 *  00578889  |. 5e             pop esi
 *  0057888a  |. 5b             pop ebx
 *  0057888b  \. c3             retn
 *
 *  [131129] [Digital Cute] オトメスイッ� -OtomeSwitch- �彼が持ってる彼女のリモコン(1.1)
 *  /HS-8@1948E9:Taskforce2.exe
 *  - addr: 0x1948e9
 *  - off: 4294967284 (0xfffffff4 = -0xc)
 *  - type: 65  (0x41)
 *
 *  00594890   . 50             push eax                                 ; |arg1
 *  00594891   . c745 f4 64c56d>mov dword ptr ss:[ebp-0xc],taskforc.006d>; |
 *  00594898   . e8 88880000    call taskforc.0059d125                   ; \taskforc.0059d125
 *  0059489d   . cc             int3
 *  0059489e  /$ 8b4c24 04      mov ecx,dword ptr ss:[esp+0x4]
 *  005948a2  |. 53             push ebx
 *  005948a3  |. 33db           xor ebx,ebx
 *  005948a5  |. 3bcb           cmp ecx,ebx
 *  005948a7  |. 56             push esi
 *  005948a8  |. 57             push edi
 *  005948a9  |. 74 08          je short taskforc.005948b3
 *  005948ab  |. 8b7c24 14      mov edi,dword ptr ss:[esp+0x14]
 *  005948af  |. 3bfb           cmp edi,ebx
 *  005948b1  |. 77 1b          ja short taskforc.005948ce
 *  005948b3  |> e8 91350000    call taskforc.00597e49
 *  005948b8  |. 6a 16          push 0x16
 *  005948ba  |. 5e             pop esi
 *  005948bb  |. 8930           mov dword ptr ds:[eax],esi
 *  005948bd  |> 53             push ebx
 *  005948be  |. 53             push ebx
 *  005948bf  |. 53             push ebx
 *  005948c0  |. 53             push ebx
 *  005948c1  |. 53             push ebx
 *  005948c2  |. e8 7e010000    call taskforc.00594a45
 *  005948c7  |. 83c4 14        add esp,0x14
 *  005948ca  |. 8bc6           mov eax,esi
 *  005948cc  |. eb 31          jmp short taskforc.005948ff
 *  005948ce  |> 8b7424 18      mov esi,dword ptr ss:[esp+0x18]
 *  005948d2  |. 3bf3           cmp esi,ebx
 *  005948d4  |. 75 04          jnz short taskforc.005948da
 *  005948d6  |. 8819           mov byte ptr ds:[ecx],bl
 *  005948d8  |.^eb d9          jmp short taskforc.005948b3
 *  005948da  |> 8bd1           mov edx,ecx
 *  005948dc  |> 8a06           /mov al,byte ptr ds:[esi]
 *  005948de  |. 8802           |mov byte ptr ds:[edx],al
 *  005948e0  |. 42             |inc edx
 *  005948e1  |. 46             |inc esi
 *  005948e2  |. 3ac3           |cmp al,bl
 *  005948e4  |. 74 03          |je short taskforc.005948e9
 *  005948e6  |. 4f             |dec edi
 *  005948e7  |.^75 f3          \jnz short taskforc.005948dc
 *  005948e9  |> 3bfb           cmp edi,ebx ; jichi: hook here
 *  005948eb  |. 75 10          jnz short taskforc.005948fd
 *  005948ed  |. 8819           mov byte ptr ds:[ecx],bl
 *  005948ef  |. e8 55350000    call taskforc.00597e49
 *  005948f4  |. 6a 22          push 0x22
 *  005948f6  |. 59             pop ecx
 *  005948f7  |. 8908           mov dword ptr ds:[eax],ecx
 *  005948f9  |. 8bf1           mov esi,ecx
 *  005948fb  |.^eb c0          jmp short taskforc.005948bd
 *  005948fd  |> 33c0           xor eax,eax
 *  005948ff  |> 5f             pop edi
 *  00594900  |. 5e             pop esi
 *  00594901  |. 5b             pop ebx
 *  00594902  \. c3             retn
 *
 *  Use this if that hook fails, try this one for future engines:
 *  /HS0@44CADA
 */
bool InsertTaskforce2Hook()
{
  const BYTE bytes[] = {
    0x88,0x02,  // 005948de  |. 8802           |mov byte ptr ds:[edx],al
    0x42,       // 005948e0  |. 42             |inc edx
    0x46,       // 005948e1  |. 46             |inc esi
    0x3a,0xc3,  // 005948e2  |. 3ac3           |cmp al,bl
    0x74, 0x03, // 005948e4  |. 74 03          |je short taskforc.005948e9
    0x4f,       // 005948e6  |. 4f             |dec edi
    0x75, 0xf3, // 005948e7  |.^75 f3          \jnz short taskforc.005948dc
    0x3b,0xfb   // 005948e9  |> 3bfb           cmp edi,ebx ; jichi: hook here
  };
  enum { addr_offset = sizeof(bytes) - 2 };
  ULONG range = min(module_limit_ - module_base_, MAX_REL_ADDR);
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_base_ + range);
  //GROWL_DWORD3(reladdr, module_base_, range);
  if (!addr) {
    ConsoleOutput("vnreng:Taskforce2: pattern not exist");
    //return false;
  }

  HookParam hp = {};
  hp.address = addr + addr_offset;
  hp.offset = -0xc; // text in ecx
  hp.type = BIG_ENDIAN|USING_STRING; // 0x41

  //GROWL_DWORD(hp.address);
  //hp.address = 0x1948e9 + module_base_;

  ConsoleOutput("vnreng: INSERT Taskforce2");
  NewHook(hp, "Taskforce2");
  return true;
}

namespace { // unnamed Rejet
/**
 *  jichi 12/22/2013: Rejet
 *  See (CaoNiMaGeBi): http://www.hongfire.com/forum/printthread.php?t=36807&pp=40&page=172
 *  See (CaoNiMaGeBi): http://tieba.baidu.com/p/2506179113
 *  Pattern: 2bce8bf8
 *      2bce      sub ecx,esi ; hook here
 *      8bf8      mov eds,eax
 *      8bd1      mov edx,ecx
 *
 *  Examples:
 *  - Type1: ドットカレシ-We're 8bit Lovers!: /HBN-4*0@A5332:DotKareshi.exe
 *    length_offset: 1
 *    off: 0xfffffff8 (-0x8)
 *    type: 1096 (0x448)
 *
 *    module_base_ = 10e0000 (variant)
 *    hook_addr = module_base_ + reladdr = 0xe55332
 *    01185311   . FFF0           PUSH EAX  ; beginning of a new function
 *    01185313   . 0FC111         XADD DWORD PTR DS:[ECX],EDX
 *    01185316   . 4A             DEC EDX
 *    01185317   . 85D2           TEST EDX,EDX
 *    01185319   . 0F8F 45020000  JG DotKares.01185564
 *    0118531F   . 8B08           MOV ECX,DWORD PTR DS:[EAX]
 *    01185321   . 8B11           MOV EDX,DWORD PTR DS:[ECX]
 *    01185323   . 50             PUSH EAX
 *    01185324   . 8B42 04        MOV EAX,DWORD PTR DS:[EDX+0x4]
 *    01185327   . FFD0           CALL EAX
 *    01185329   . E9 36020000    JMP DotKares.01185564
 *    0118532E   . 8B7424 20      MOV ESI,DWORD PTR SS:[ESP+0x20]
 *    01185332   . E8 99A9FBFF    CALL DotKares.0113FCD0    ; hook here
 *    01185337   . 8BF0           MOV ESI,EAX
 *    01185339   . 8D4C24 14      LEA ECX,DWORD PTR SS:[ESP+0x14]
 *    0118533D   . 3BF7           CMP ESI,EDI
 *    0118533F   . 0F84 1A020000  JE DotKares.0118555F
 *    01185345   . 51             PUSH ECX                                 ; /Arg2
 *    01185346   . 68 E4FE5501    PUSH DotKares.0155FEE4                   ; |Arg1 = 0155FEE4
 *    0118534B   . E8 1023F9FF    CALL DotKares.01117660                   ; \DotKares.00377660
 *    01185350   . 83C4 08        ADD ESP,0x8
 *    01185353   . 84C0           TEST AL,AL
 *
 *  - Type2: ドットカレシ-We're 8bit Lovers! II: /HBN-8*0@A7AF9:dotkareshi.exe
 *    off: 4294967284 (0xfffffff4 = -0xc)
 *    length_offset: 1
 *    type: 1096 (0x448)
 *
 *    module_base_: 0x12b0000
 *
 *    01357ad2   . fff0           push eax ; beginning of a new function
 *    01357ad4   . 0fc111         xadd dword ptr ds:[ecx],edx
 *    01357ad7   . 4a             dec edx
 *    01357ad8   . 85d2           test edx,edx
 *    01357ada   . 7f 0a          jg short dotkares.01357ae6
 *    01357adc   . 8b08           mov ecx,dword ptr ds:[eax]
 *    01357ade   . 8b11           mov edx,dword ptr ds:[ecx]
 *    01357ae0   . 50             push eax
 *    01357ae1   . 8b42 04        mov eax,dword ptr ds:[edx+0x4]
 *    01357ae4   . ffd0           call eax
 *    01357ae6   > 8b4c24 14      mov ecx,dword ptr ss:[esp+0x14]
 *    01357aea   . 33ff           xor edi,edi
 *    01357aec   . 3979 f4        cmp dword ptr ds:[ecx-0xc],edi
 *    01357aef   . 0f84 1e020000  je dotkares.01357d13
 *    01357af5   . 8b7424 20      mov esi,dword ptr ss:[esp+0x20]
 *    01357af9   . e8 7283fbff    call dotkares.0130fe70    ; jichi: hook here
 *    01357afe   . 8bf0           mov esi,eax
 *    01357b00   . 3bf7           cmp esi,edi
 *    01357b02   . 0f84 0b020000  je dotkares.01357d13
 *    01357b08   . 8d5424 14      lea edx,dword ptr ss:[esp+0x14]
 *    01357b0c   . 52             push edx                                 ; /arg2
 *    01357b0d   . 68 cc9f7501    push dotkares.01759fcc                   ; |arg1 = 01759fcc
 *    01357b12   . e8 e9f9f8ff    call dotkares.012e7500                   ; \dotkares.012c7500
 *    01357b17   . 83c4 08        add esp,0x8
 *    01357b1a   . 84c0           test al,al
 *    01357b1c   . 74 1d          je short dotkares.01357b3b
 *    01357b1e   . 8d46 64        lea eax,dword ptr ds:[esi+0x64]
 *    01357b21   . e8 bad0f8ff    call dotkares.012e4be0
 *    01357b26   . 68 28a17501    push dotkares.0175a128                   ; /arg1 = 0175a128 ascii "<br>"
 *
 *  - Type2: Tiny×MACHINEGUN: /HBN-8*0@4CEB8:TinyMachinegun.exe
 *    module_base_: 0x12f0000
 *    There are two possible places to hook
 *
 *    0133cea0   . fff0           push eax ; beginning of a new function
 *    0133cea2   . 0fc111         xadd dword ptr ds:[ecx],edx
 *    0133cea5   . 4a             dec edx
 *    0133cea6   . 85d2           test edx,edx
 *    0133cea8   . 7f 0a          jg short tinymach.0133ceb4
 *    0133ceaa   . 8b08           mov ecx,dword ptr ds:[eax]
 *    0133ceac   . 8b11           mov edx,dword ptr ds:[ecx]
 *    0133ceae   . 50             push eax
 *    0133ceaf   . 8b42 04        mov eax,dword ptr ds:[edx+0x4]
 *    0133ceb2   . ffd0           call eax
 *    0133ceb4   > 8b4c24 14      mov ecx,dword ptr ss:[esp+0x14]
 *    0133ceb8   . 33db           xor ebx,ebx               ; jichi: hook here
 *    0133ceba   . 3959 f4        cmp dword ptr ds:[ecx-0xc],ebx
 *    0133cebd   . 0f84 d4010000  je tinymach.0133d097
 *    0133cec3   . 8b7424 20      mov esi,dword ptr ss:[esp+0x20]
 *    0133cec7   . e8 f4f90100    call tinymach.0135c8c0     ; jichi: or hook here
 *    0133cecc   . 8bf0           mov esi,eax
 *    0133cece   . 3bf3           cmp esi,ebx
 *    0133ced0   . 0f84 c1010000  je tinymach.0133d097
 *    0133ced6   . 8d5424 14      lea edx,dword ptr ss:[esp+0x14]
 *    0133ceda   . 52             push edx                                 ; /arg2
 *    0133cedb   . 68 44847d01    push tinymach.017d8444                   ; |arg1 = 017d8444
 *    0133cee0   . e8 eb5bfdff    call tinymach.01312ad0                   ; \tinymach.011b2ad0
 *
 *  - Type 3: 剣が君: /HBN-8*0@B357D:KenGaKimi.exe
 *
 *    01113550   . fff0           push eax
 *    01113552   . 0fc111         xadd dword ptr ds:[ecx],edx
 *    01113555   . 4a             dec edx
 *    01113556   . 85d2           test edx,edx
 *    01113558   . 7f 0a          jg short kengakim.01113564
 *    0111355a   . 8b08           mov ecx,dword ptr ds:[eax]
 *    0111355c   . 8b11           mov edx,dword ptr ds:[ecx]
 *    0111355e   . 50             push eax
 *    0111355f   . 8b42 04        mov eax,dword ptr ds:[edx+0x4]
 *    01113562   . ffd0           call eax
 *    01113564     8b4c24 14      mov ecx,dword ptr ss:[esp+0x14]
 *    01113568     33ff           xor edi,edi
 *    0111356a     3979 f4        cmp dword ptr ds:[ecx-0xc],edi
 *    0111356d     0f84 09020000  je kengakim.0111377c
 *    01113573     8d5424 14      lea edx,dword ptr ss:[esp+0x14]
 *    01113577     52             push edx
 *    01113578     68 dc6a5401    push kengakim.01546adc
 *    0111357d     e8 3eaff6ff    call kengakim.0107e4c0    ; hook here
 */
bool FindRejetHook(LPCVOID pattern, DWORD pattern_size, DWORD hook_off, DWORD hook_offset, LPCSTR hook_name = "Rejet")
{
  // Offset to the function call from the beginning of the function
  //enum { addr_offset = 0x21 }; // Type1: hex(0x01185332-0x01185311)
  //const BYTE pattern[] = {    // Type1: Function start
  //  0xff,0xf0,      // 01185311   . fff0           push eax  ; beginning of a new function
  //  0x0f,0xc1,0x11, // 01185313   . 0fc111         xadd dword ptr ds:[ecx],edx
  //  0x4a,           // 01185316   . 4a             dec edx
  //  0x85,0xd2,      // 01185317   . 85d2           test edx,edx
  //  0x0f,0x8f       // 01185319   . 0f8f 45020000  jg DotKares.01185564
  //};
  //GROWL_DWORD(module_base_);
  ULONG addr = module_base_; //- sizeof(pattern);
  do {
    //addr += sizeof(pattern); // ++ so that each time return diff address
    ULONG range = min(module_limit_ - addr, MAX_REL_ADDR);
    addr = MemDbg::findBytes(pattern, pattern_size, addr, addr + range);
    if (!addr) {
      //ITH_MSG(L"failed");
      ConsoleOutput("vnreng:Rejet: pattern not found");
      return false;
    }

    addr += hook_off;
    //GROWL_DWORD(addr);
    //GROWL_DWORD(*(DWORD *)(addr-3));
    //const BYTE hook_ins[] = {
    //  /*0x8b,*/0x74,0x24, 0x20,  //    mov esi,dword ptr ss:[esp+0x20]
    //  0xe8 //??,??,??,??, 01357af9  e8 7283fbff call DotKares.0130fe70 ; jichi: hook here
    //};
  } while(0xe8202474 != *(DWORD *)(addr - 3));

  ConsoleOutput("vnreng: INSERT Rejet");
  HookParam hp = {};
  hp.address = addr; //- 0xf;
  hp.type = NO_CONTEXT|DATA_INDIRECT|FIXING_SPLIT;
  hp.length_offset = 1;
  hp.offset = hook_offset;
  //hp.offset = -0x8; // Type1
  //hp.offset = -0xc; // Type2

  NewHook(hp, hook_name);
  return true;
}
bool InsertRejetHook1() // This type of hook has varied hook address
{
  const BYTE bytes[] = {  // Type1: Function start
    0xff,0xf0,          // 01185311   . fff0           push eax  ; beginning of a new function
    0x0f,0xc1,0x11,     // 01185313   . 0fc111         xadd dword ptr ds:[ecx],edx
    0x4a,               // 01185316   . 4a             dec edx
    0x85,0xd2,          // 01185317   . 85d2           test edx,edx
    0x0f,0x8f           // 01185319   . 0f8f 45020000  jg DotKares.01185564
  };
  // Offset to the function call from the beginning of the function
  enum { addr_offset = 0x21 }; // Type1: hex(0x01185332-0x01185311)
  enum { hook_offset = -0x8 }; // hook parameter
  return FindRejetHook(bytes, sizeof(bytes), addr_offset, hook_offset);
}
bool InsertRejetHook2() // This type of hook has static hook address
{
  const BYTE bytes[] = {   // Type2 Function start
    0xff,0xf0,           //   01357ad2   fff0           push eax
    0x0f,0xc1,0x11,      //   01357ad4   0fc111         xadd dword ptr ds:[ecx],edx
    0x4a,                //   01357ad7   4a             dec edx
    0x85,0xd2,           //   01357ad8   85d2           test edx,edx
    0x7f, 0x0a,          //   01357ada   7f 0a          jg short DotKares.01357ae6
    0x8b,0x08,           //   01357adc   8b08           mov ecx,dword ptr ds:[eax]
    0x8b,0x11,           //   01357ade   8b11           mov edx,dword ptr ds:[ecx]
    0x50,                //   01357ae0   50             push eax
    0x8b,0x42, 0x04,     //   01357ae1   8b42 04        mov eax,dword ptr ds:[edx+0x4]
    0xff,0xd0,           //   01357ae4   ffd0           call eax
    0x8b,0x4c,0x24, 0x14 //   01357ae6   8b4c24 14      mov ecx,dword ptr ss:[esp+0x14]
  };
  // Offset to the function call from the beginning of the function
  enum { addr_offset = 0x27 }; // Type2: hex(0x0133CEC7-0x0133CEA0) = hex(0x01357af9-0x1357ad2)
  enum { hook_offset = -0xc }; // hook parameter
  return FindRejetHook(bytes, sizeof(bytes), addr_offset, hook_offset);
}
bool InsertRejetHook3() // jichi 12/28/2013: add for 剣が君
{
  // The following pattern is the same as type2
  const BYTE bytes[] = {   // Type2 Function start
    0xff,0xf0,           //   01357ad2   fff0           push eax
    0x0f,0xc1,0x11,      //   01357ad4   0fc111         xadd dword ptr ds:[ecx],edx
    0x4a,                //   01357ad7   4a             dec edx
    0x85,0xd2,           //   01357ad8   85d2           test edx,edx
    0x7f, 0x0a,          //   01357ada   7f 0a          jg short DotKares.01357ae6
    0x8b,0x08,           //   01357adc   8b08           mov ecx,dword ptr ds:[eax]
    0x8b,0x11,           //   01357ade   8b11           mov edx,dword ptr ds:[ecx]
    0x50,                //   01357ae0   50             push eax
    0x8b,0x42, 0x04,     //   01357ae1   8b42 04        mov eax,dword ptr ds:[edx+0x4]
    0xff,0xd0,           //   01357ae4   ffd0           call eax
    0x8b,0x4c,0x24, 0x14 //   01357ae6   8b4c24 14      mov ecx,dword ptr ss:[esp+0x14]
  };
  // Offset to the function call from the beginning of the function
  //enum { addr_offset = 0x27 }; // Type2: hex(0x0133CEC7-0x0133CEA0) = hex(0x01357af9-0x1357ad2)
  enum { hook_offset = -0xc }; // hook parameter
  ULONG addr = module_base_; //- sizeof(bytes);
  while (true) {
    //addr += sizeof(bytes); // ++ so that each time return diff address
    ULONG range = min(module_limit_ - addr, MAX_REL_ADDR);
    addr = MemDbg::findBytes(bytes, sizeof(bytes), addr, addr + range);
    if (!addr) {
      //ITH_MSG(L"failed");
      ConsoleOutput("vnreng:Rejet: pattern not found");
      return false;
    }
    addr += sizeof(bytes);
    // Push and call at once, i.e. push (0x68) and call (0xe8)
    // 01185345     52             push edx
    // 01185346   . 68 e4fe5501    push dotkares.0155fee4                   ; |arg1 = 0155fee4
    // 0118534b   . e8 1023f9ff    call dotkares.01117660                   ; \dotkares.00377660
    enum { start = 0x10, stop = 0x50 };
    // Different from FindRejetHook
    DWORD i;
    for (i = start; i < stop; i++)
      if (*(WORD *)(addr + i - 1) == 0x6852 && *(BYTE *)(addr + i + 5) == 0xe8) // 0118534B-01185346
        break;
    if (i < stop) {
      addr += i;
      break;
    }
  } //while(0xe8202474 != *(DWORD *)(addr - 3));

  //GROWL_DWORD(addr - module_base_); // = 0xb3578 for 剣が君

  ConsoleOutput("vnreng: INSERT Rejet");
  // The same as type2
  HookParam hp = {};
  hp.address = addr; //- 0xf;
  hp.type = NO_CONTEXT|DATA_INDIRECT|FIXING_SPLIT;
  hp.length_offset = 1;
  hp.offset = hook_offset;
  //hp.offset = -0x8; // Type1
  //hp.offset = -0xc; // Type2

  NewHook(hp, "Rejet");
  return true;
}
} // unnamed Rejet

bool InsertRejetHook()
{ return InsertRejetHook2() || InsertRejetHook1() || InsertRejetHook3(); } // insert hook2 first, since 2's pattern seems to be more unique

/**
 *  jichi 4/1/2014: Insert AU hook
 *  Sample games:
 *  英雼�戦姫: /HBN-8*4@4AD807
 *  英雼�戦姫GOLD: /HB-8*4@4ADB50 (alternative)
 *
 *  /HBN-8*4@4AD807
 *  - addr: 4904967 = 0x4ad807
 *  - ind: 4
 *  - length_offset: 1
 *  - off: 4294967284 = 0xfffffff4 = -0xc
 *  - type: 1032 = 0x408
 *
 *  004ad76a  |. ff50 04        |call dword ptr ds:[eax+0x4]
 *  004ad76d  |. 48             |dec eax                                 ;  switch (cases 1..a)
 *  004ad76e  |. 83f8 09        |cmp eax,0x9
 *  004ad771  |. 0f87 37020000  |ja 英雼�戦.004ad9ae
 *  004ad777  |. ff2485 2cda4a0>|jmp dword ptr ds:[eax*4+0x4ada2c]
 *  004ad77e  |> 83bf c4000000 >|cmp dword ptr ds:[edi+0xc4],0x1         ;  case 1 of switch 004ad76d
 *  004ad785  |. 75 35          |jnz short 英雼�戦.004ad7bc
 *  004ad787  |. 39af c8000000  |cmp dword ptr ds:[edi+0xc8],ebp
 *  004ad78d  |. 72 08          |jb short 英雼�戦.004ad797
 *  004ad78f  |. 8b87 b4000000  |mov eax,dword ptr ds:[edi+0xb4]
 *  004ad795  |. eb 06          |jmp short 英雼�戦.004ad79d
 *  004ad797  |> 8d87 b4000000  |lea eax,dword ptr ds:[edi+0xb4]
 *  004ad79d  |> 0fb608         |movzx ecx,byte ptr ds:[eax]
 *  004ad7a0  |. 51             |push ecx
 *  004ad7a1  |. e8 d15b2a00    |call 英雼�戦.00753377
 *  004ad7a6  |. 83c4 04        |add esp,0x4
 *  004ad7a9  |. 85c0           |test eax,eax
 *  004ad7ab  |. 74 0f          |je short 英雼�戦.004ad7bc
 *  004ad7ad  |. 8d5424 20      |lea edx,dword ptr ss:[esp+0x20]
 *  004ad7b1  |. 52             |push edx
 *  004ad7b2  |. b9 88567a00    |mov ecx,英雼�戦.007a5688
 *  004ad7b7  |. e8 a40cf6ff    |call 英雼�戦.0040e460
 *  004ad7bc  |> 8b8424 e400000>|mov eax,dword ptr ss:[esp+0xe4]
 *  004ad7c3  |. 8a48 01        |mov cl,byte ptr ds:[eax+0x1]
 *  004ad7c6  |. 84c9           |test cl,cl
 *  004ad7c8  |. 75 2e          |jnz short 英雼�戦.004ad7f8
 *  004ad7ca  |. 8d9f b0000000  |lea ebx,dword ptr ds:[edi+0xb0]
 *  004ad7d0  |. be ac6e7a00    |mov esi,英雼�戦.007a6eac
 *  004ad7d5  |. 8bcb           |mov ecx,ebx
 *  004ad7d7  |. e8 e40af6ff    |call 英雼�戦.0040e2c0
 *  004ad7dc  |. 84c0           |test al,al
 *  004ad7de  |. 0f84 ca010000  |je 英雼�戦.004ad9ae
 *  004ad7e4  |. be a86e7a00    |mov esi,英雼�戦.007a6ea8
 *  004ad7e9  |. 8bcb           |mov ecx,ebx
 *  004ad7eb  |. e8 d00af6ff    |call 英雼�戦.0040e2c0
 *  004ad7f0  |. 84c0           |test al,al
 *  004ad7f2  |. 0f84 b6010000  |je 英雼�戦.004ad9ae
 *  004ad7f8  |> 6a 00          |push 0x0
 *  004ad7fa  |. 8d8f b0000000  |lea ecx,dword ptr ds:[edi+0xb0]
 *  004ad800  |. 83c8 ff        |or eax,0xffffffff
 *  004ad803  |. 8d5c24 24      |lea ebx,dword ptr ss:[esp+0x24]
 *  004ad807  |. e8 740cf6ff    |call 英雼�戦.0040e480     ; jichi: hook here
 *  004ad80c  |. e9 9d010000    |jmp 英雼�戦.004ad9ae
 *  004ad811  |> 8b8c24 e400000>|mov ecx,dword ptr ss:[esp+0xe4]         ;  case 4 of switch 004ad76d
 *  004ad818  |. 8039 00        |cmp byte ptr ds:[ecx],0x0
 *  004ad81b  |. 0f84 8d010000  |je 英雼�戦.004ad9ae
 *  004ad821  |. b8 04000000    |mov eax,0x4
 *  004ad826  |. b9 c86e7a00    |mov ecx,英雼�戦.007a6ec8                   ;  ascii "<br>"
 *  004ad82b  |. 8d5424 20      |lea edx,dword ptr ss:[esp+0x20]
 *  004ad82f  |. e8 3c0df6ff    |call 英雼�戦.0040e570
 *  004ad834  |. e9 75010000    |jmp 英雼�戦.004ad9ae
 *  004ad839  |> 8bbf b4000000  |mov edi,dword ptr ds:[edi+0xb4]         ;  case 5 of switch 004ad76d
 */
bool InsertTencoHook()
{
  const BYTE bytes[] = {
    0x6a, 0x00,                     // 004ad7f8  |> 6a 00          |push 0x0
    0x8d,0x8f, 0xb0,0x00,0x00,0x00, // 004ad7fa  |. 8d8f b0000000  |lea ecx,dword ptr ds:[edi+0xb0]
    0x83,0xc8, 0xff,                // 004ad800  |. 83c8 ff        |or eax,0xffffffff
    0x8d,0x5c,0x24, 0x24,           // 004ad803  |. 8d5c24 24      |lea ebx,dword ptr ss:[esp+0x24]
    0xe8 //740cf6ff                 // 004ad807  |. e8 740cf6ff    |call 英雼�戦.0040e480     ; jichi: hook here
  };
  enum { addr_offset = sizeof(bytes) - 1 };
  ULONG range = min(module_limit_ - module_base_, MAX_REL_ADDR);
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_base_ + range);
  //reladdr = 0x4ad807;
  if (!addr) {
    ConsoleOutput("vnreng:Tenco: pattern not found");
    return false;
  }

  HookParam hp = {};
  hp.address = addr + addr_offset;
  hp.length_offset = 1;
  hp.index = 4;
  hp.offset = -0xc;
  hp.type = NO_CONTEXT|DATA_INDIRECT;

  ConsoleOutput("vnreng: INSERT Tenco");
  NewHook(hp, "Tenco");
  return true;
}

/**
 *  jichi 4/1/2014: Insert AOS hook
 *  About 彩斤�: http://erogetrailers.com/brand/165
 *  About AOS: http://asmodean.reverse.net/pages/exaos.html
 *
 *  Sample games:
 *
 *  [140228] [Sugar Pot] 恋する少女と想�キセキ V1.00 H-CODE by �쿿
 *  - /HB8*0@3C2F0:恋する少女と想�キセキ.exe
 *  - /HBC*0@3C190:恋する少女と想�キセキ.exe
 *
 *  [120224] [Sugar Pot]  ヂ�モノツキ
 *
 *  LiLiM games
 *
 *  /HB8*0@3C2F0:恋する少女と想�キセ
 *  - addr: 246512 = 0x3c2f0
 *  - length_offset: 1
 *  - module: 1814017450
 *  - off: 8
 *  - type: 72 = 0x48
 *
 *  00e3c2ed     cc                         int3
 *  00e3c2ee     cc                         int3
 *  00e3c2ef     cc                         int3
 *  00e3c2f0  /$ 51                         push ecx    ; jichi: hook here, function starts
 *  00e3c2f1  |. a1 0c64eb00                mov eax,dword ptr ds:[0xeb640c]
 *  00e3c2f6  |. 8b0d 7846eb00              mov ecx,dword ptr ds:[0xeb4678]
 *  00e3c2fc  |. 53                         push ebx
 *  00e3c2fd  |. 55                         push ebp
 *  00e3c2fe  |. 8b6c24 10                  mov ebp,dword ptr ss:[esp+0x10]
 *  00e3c302  |. 56                         push esi
 *  00e3c303  |. 8b35 c446eb00              mov esi,dword ptr ds:[0xeb46c4]
 *  00e3c309  |. 57                         push edi
 *  00e3c30a  |. 0fb63d c746eb00            movzx edi,byte ptr ds:[0xeb46c7]
 *  00e3c311  |. 81e6 ffffff00              and esi,0xffffff
 *  00e3c317  |. 894424 18                  mov dword ptr ss:[esp+0x18],eax
 *  00e3c31b  |. 85ff                       test edi,edi
 *  00e3c31d  |. 74 6b                      je short 恋する�00e3c38a
 *  00e3c31f  |. 8bd9                       mov ebx,ecx
 *  00e3c321  |. 85db                       test ebx,ebx
 *  00e3c323  |. 74 17                      je short 恋する�00e3c33c
 *  00e3c325  |. 8b4b 28                    mov ecx,dword ptr ds:[ebx+0x28]
 *  00e3c328  |. 56                         push esi                                 ; /color
 *  00e3c329  |. 51                         push ecx                                 ; |hdc
 *  00e3c32a  |. ff15 3c40e800              call dword ptr ds:[<&gdi32.SetTextColor>>; \settextcolor
 *  00e3c330  |. 89b3 c8000000              mov dword ptr ds:[ebx+0xc8],esi
 *  00e3c336  |. 8b0d 7846eb00              mov ecx,dword ptr ds:[0xeb4678]
 *  00e3c33c  |> 0fbf55 1c                  movsx edx,word ptr ss:[ebp+0x1c]
 *  00e3c340  |. 0fbf45 0a                  movsx eax,word ptr ss:[ebp+0xa]
 *  00e3c344  |. 0fbf75 1a                  movsx esi,word ptr ss:[ebp+0x1a]
 *  00e3c348  |. 03d7                       add edx,edi
 *  00e3c34a  |. 03c2                       add eax,edx
 *  00e3c34c  |. 0fbf55 08                  movsx edx,word ptr ss:[ebp+0x8]
 *  00e3c350  |. 03f7                       add esi,edi
 *  00e3c352  |. 03d6                       add edx,esi
 *  00e3c354  |. 85c9                       test ecx,ecx
 *  00e3c356  |. 74 32                      je short 恋する�00e3c38a
 */

bool InsertAOS1Hook()
{
  // jichi 4/2/2014: The starting of this function is different from ヂ�モノツキ
  // So, use a pattern in the middle of the function instead.
  //
  //const BYTE bytes[] = {
  //  0x51,                                 // 00e3c2f0  /$ 51              push ecx    ; jichi: hook here, function begins
  //  0xa1, 0x0c,0x64,0xeb,0x00,            // 00e3c2f1  |. a1 0c64eb00     mov eax,dword ptr ds:[0xeb640c]
  //  0x8b,0x0d, 0x78,0x46,0xeb,0x00,       // 00e3c2f6  |. 8b0d 7846eb00   mov ecx,dword ptr ds:[0xeb4678]
  //  0x53,                                 // 00e3c2fc  |. 53              push ebx
  //  0x55,                                 // 00e3c2fd  |. 55              push ebp
  //  0x8b,0x6c,0x24, 0x10,                 // 00e3c2fe  |. 8b6c24 10       mov ebp,dword ptr ss:[esp+0x10]
  //  0x56,                                 // 00e3c302  |. 56              push esi
  //  0x8b,0x35, 0xc4,0x46,0xeb,0x00,       // 00e3c303  |. 8b35 c446eb00   mov esi,dword ptr ds:[0xeb46c4]
  //  0x57,                                 // 00e3c309  |. 57              push edi
  //  0x0f,0xb6,0x3d, 0xc7,0x46,0xeb,0x00,  // 00e3c30a  |. 0fb63d c746eb00 movzx edi,byte ptr ds:[0xeb46c7]
  //  0x81,0xe6, 0xff,0xff,0xff,0x00        // 00e3c311  |. 81e6 ffffff00   and esi,0xffffff
  //};
  //enum { addr_offset = 0 };

  const BYTE bytes[] = {
    0x0f,0xbf,0x55, 0x1c,   // 00e3c33c  |> 0fbf55 1c                  movsx edx,word ptr ss:[ebp+0x1c]
    0x0f,0xbf,0x45, 0x0a,   // 00e3c340  |. 0fbf45 0a                  movsx eax,word ptr ss:[ebp+0xa]
    0x0f,0xbf,0x75, 0x1a,   // 00e3c344  |. 0fbf75 1a                  movsx esi,word ptr ss:[ebp+0x1a]
    0x03,0xd7,              // 00e3c348  |. 03d7                       add edx,edi
    0x03,0xc2,              // 00e3c34a  |. 03c2                       add eax,edx
    0x0f,0xbf,0x55, 0x08,   // 00e3c34c  |. 0fbf55 08                  movsx edx,word ptr ss:[ebp+0x8]
    0x03,0xf7,              // 00e3c350  |. 03f7                       add esi,edi
    0x03,0xd6,              // 00e3c352  |. 03d6                       add edx,esi
    0x85,0xc9               // 00e3c354  |. 85c9                       test ecx,ecx
  };
  enum { addr_offset = 0x00e3c2f0 - 0x00e3c33c }; // distance to the beginning of the function, which is 0x51 (push ecx)
  ULONG range = min(module_limit_ - module_base_, MAX_REL_ADDR);
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_base_ + range);
  //GROWL(reladdr);
  if (!addr) {
    ConsoleOutput("vnreng:AOS1: pattern not found");
    return false;
  }
  addr += addr_offset;
  //GROWL(addr);
  enum { push_ecx = 0x51 }; // beginning of the function
  if (*(BYTE *)addr != push_ecx) {
    ConsoleOutput("vnreng:AOS1: beginning of the function not found");
    return false;
  }

  HookParam hp = {};
  hp.address = addr;
  hp.length_offset = 1;
  hp.offset = 8;
  hp.type = DATA_INDIRECT;

  ConsoleOutput("vnreng: INSERT AOS1");
  NewHook(hp, "AOS1");
  return true;
}

bool InsertAOS2Hook()
{
  const BYTE bytes[] = {
    0x51,                  // 00C4E7E0  /$  51            PUSH ECX ; mireado: hook here, function begins
    0x33,0xc0,             // 00C4E7E1  |.  33C0          XOR EAX,EAX
    0x53,                  // 00C4E7E3  |.  53            PUSH EBX
    0x55,                  // 00C4E7E4  |.  55            PUSH EBP
    0x8b,0x2d//, XX4,           // 00C4E7E5  |.  8B2D 40A3CF00 MOV EBP,DWORD PTR DS:[0CFA340] ; mireado: some time changing 40A3CF00 => 40A3C000
    //0x89,0x07,             // 00C4E7EB  |.  8907          MOV DWORD PTR DS:[EDI],EAX
    //0x89,0x47, 0x04       // 00C4E7ED  |.  8947 04       MOV DWORD PTR DS:[EDI+4],EAX
    //0x56,                  // 00C4E7F0  |.  56            PUSH ESI
    //0x8b,0x75, 0x44        // 00C4E7F1  |.  8B75 44       MOV ESI,DWORD PTR SS:[EBP+44]
  };

  enum { addr_offset = 0 }; // distance to the beginning of the function, which is 0x51 (push ecx)
  ULONG range = min(module_limit_ - module_base_, MAX_REL_ADDR);
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_base_ + range);
  //GROWL(reladdr);
  if (!addr) {
    ConsoleOutput("vnreng:AOS2: pattern not found");
    return false;
  }
  addr += addr_offset;
  //GROWL(addr);
  enum { push_ecx = 0x51 }; // beginning of the function
  if (*(BYTE *)addr != push_ecx) {
    ConsoleOutput("vnreng:AOS2: beginning of the function not found");
    return false;
  }

  HookParam hp = {};
  hp.address = addr;
  hp.length_offset = 1;
  hp.offset = 8;
  hp.type = DATA_INDIRECT;

  ConsoleOutput("vnreng: INSERT AOS2");
  NewHook(hp, "AOS2");
  return true;
}

bool InsertAOSHook()
{ return InsertAOS1Hook() ||  InsertAOS2Hook();}

  
/**
 *  jichi 1/10/2014: Rai7 puk
 *  See: http://www.hongfire.com/forum/showthread.php/421909-%E3%80%90Space-Warfare-Sim%E3%80%91Rai-7-PUK/page10
 *  See: www.hongfire.com/forum/showthread.php/421909-%E3%80%90Space-Warfare-Sim%E3%80%91Rai-7-PUK/page19
 *
 *  Version: R7P3-13v2(131220).rar, pass: sstm http://pan.baidu.com/share/home?uk=3727185265#category/type=0
 *  /HS0@409524
 */
//bool InsertRai7Hook()
//{
//}

/**
 *  jichi 10/1/2013: sol-fa-soft
 *  See (tryguy): http://www.hongfire.com/forum/printthread.php?t=36807&pp=10&page=639
 *
 *  @tryguy
 *  [sol-fa-soft]
 *  17 スク水不要� /HA4@4AD140
 *  18 ななちも�とぁ�しょ: /HA4@5104A0
 *  19 発惁�んこぁ�� /HA4@51D720
 *  20 わたし�たまごさ� /HA4@4968E0
 *  21 修学旡�夜更かし� /HA4@49DC00
 *  22 おぼえたてキヂ�: /HA4@49DDB0
 *  23 ちっさい巫女さんSOS: /HA4@4B4AA0
 *  24 はじめてのお�ろやさん: /HA4@4B5600
 *  25 はきわすれ愛好� /HA4@57E360
 *  26 朝っぱらから発惮�� /HA4@57E360
 *  27 となり�ヴァンパイア: /HA4@5593B0
 *  28 麦わら帽子と水辺の妖精: /HA4@5593B0
 *  29 海と温泉と夏休み: /HA4@6DE8E0
 *  30 駏�子屋さん繁盛� /HA4@6DEC90
 *  31 浴衣の下�… �神社で発見�ノ�パン少女 /HA4@6DEC90
 *  32 プ�ルのじか�スク水不要�: /HA4@62AE10
 *  33 妹のお泊まり� /HA4@6087A0
 *  34 薝�少女: /HA4@6087A0
 *  35 あや�Princess Intermezzo: /HA4@609BF0
 *
 *  SG01 男湯�: /HA4@6087A0
 *
 *  c71 真�の大晦日CD: /HA4@516b50
 *  c78 sol-fa-soft真夏�お気楽CD: /HA4@6DEC90
 *
 *  Example: 35 あや�Princess Intermezzo: /HA4@609BF0
 *  - addr: 6331376 = 0x609bf0
 *  - length_offset: 1
 *  - off: 4
 *  - type: 4
 *
 *  ASCII: あや� addr_offset = -50
 *  Function starts
 *  00609bef  /> cc             int3
 *  00609bf0  /> 55             push ebp
 *  00609bf1  |. 8bec           mov ebp,esp
 *  00609bf3  |. 64:a1 00000000 mov eax,dword ptr fs:[0]
 *  00609bf9  |. 6a ff          push -0x1
 *  00609bfb  |. 68 e1266300    push あや�006326e1
 *  00609c00  |. 50             push eax
 *  00609c01  |. 64:8925 000000>mov dword ptr fs:[0],esp
 *  00609c08  |. 81ec 80000000  sub esp,0x80
 *  00609c0e  |. 53             push ebx
 *  00609c0f  |. 8b5d 08        mov ebx,dword ptr ss:[ebp+0x8]
 *  00609c12  |. 57             push edi
 *  00609c13  |. 8bf9           mov edi,ecx
 *  00609c15  |. 8b07           mov eax,dword ptr ds:[edi]
 *  00609c17  |. 83f8 02        cmp eax,0x2
 *  00609c1a  |. 75 1f          jnz short あや�00609c3b
 *  00609c1c  |. 3b5f 40        cmp ebx,dword ptr ds:[edi+0x40]
 *  00609c1f  |. 75 1a          jnz short あや�00609c3b
 *  00609c21  |. 837f 44 00     cmp dword ptr ds:[edi+0x44],0x0
 *  00609c25  |. 74 14          je short あや�00609c3b
 *  00609c27  |. 5f             pop edi
 *  00609c28  |. b0 01          mov al,0x1
 *  00609c2a  |. 5b             pop ebx
 *  00609c2b  |. 8b4d f4        mov ecx,dword ptr ss:[ebp-0xc]
 *  00609c2e  |. 64:890d 000000>mov dword ptr fs:[0],ecx
 *  00609c35  |. 8be5           mov esp,ebp
 *  00609c37  |. 5d             pop ebp
 *  00609c38  |. c2 0400        retn 0x4
 *  Function stops
 *
 *  WideChar: こいな�小田舎で初恋x中出しセクシャルライ�, addr_offset = -53
 *  0040653a     cc             int3
 *  0040653b     cc             int3
 *  0040653c     cc             int3
 *  0040653d     cc             int3
 *  0040653e     cc             int3
 *  0040653f     cc             int3
 *  00406540   > 55             push ebp
 *  00406541   . 8bec           mov ebp,esp
 *  00406543   . 64:a1 00000000 mov eax,dword ptr fs:[0]
 *  00406549   . 6a ff          push -0x1
 *  0040654b   . 68 f1584300    push erondo01.004358f1
 *  00406550   . 50             push eax
 *  00406551   . 64:8925 000000>mov dword ptr fs:[0],esp
 *  00406558   . 83ec 6c        sub esp,0x6c
 *  0040655b   . 53             push ebx
 *  0040655c   . 8bd9           mov ebx,ecx
 *  0040655e   . 57             push edi
 *  0040655f   . 8b03           mov eax,dword ptr ds:[ebx]
 *  00406561   . 8b7d 08        mov edi,dword ptr ss:[ebp+0x8]
 *  00406564   . 83f8 02        cmp eax,0x2
 *  00406567   . 75 1f          jnz short erondo01.00406588
 *  00406569   . 3b7b 3c        cmp edi,dword ptr ds:[ebx+0x3c]
 *  0040656c   . 75 1a          jnz short erondo01.00406588
 *  0040656e   . 837b 40 00     cmp dword ptr ds:[ebx+0x40],0x0
 *  00406572   . 74 14          je short erondo01.00406588
 *  00406574   . 5f             pop edi
 *  00406575   . b0 01          mov al,0x1
 *  00406577   . 5b             pop ebx
 *  00406578   . 8b4d f4        mov ecx,dword ptr ss:[ebp-0xc]
 *  0040657b   . 64:890d 000000>mov dword ptr fs:[0],ecx
 *  00406582   . 8be5           mov esp,ebp
 *  00406584   . 5d             pop ebp
 *  00406585   . c2 0400        retn 0x4
 *
 *  WideChar: 祝福�鐘�音は、桜色の風と共に, addr_offset = -50,
 *  FIXME: how to know if it is UTF16? This game has /H code, though:
 *
 *      /HA-4@94D62:shukufuku_main.exe
 *
 *  011d619e   cc               int3
 *  011d619f   cc               int3
 *  011d61a0   55               push ebp
 *  011d61a1   8bec             mov ebp,esp
 *  011d61a3   64:a1 00000000   mov eax,dword ptr fs:[0]
 *  011d61a9   6a ff            push -0x1
 *  011d61ab   68 d1811f01      push .011f81d1
 *  011d61b0   50               push eax
 *  011d61b1   64:8925 00000000 mov dword ptr fs:[0],esp
 *  011d61b8   81ec 80000000    sub esp,0x80
 *  011d61be   53               push ebx
 *  011d61bf   8b5d 08          mov ebx,dword ptr ss:[ebp+0x8]
 *  011d61c2   57               push edi
 *  011d61c3   8bf9             mov edi,ecx
 *  011d61c5   8b07             mov eax,dword ptr ds:[edi]
 *  011d61c7   83f8 02          cmp eax,0x2
 *  011d61ca   75 1f            jnz short .011d61eb
 *  011d61cc   3b5f 40          cmp ebx,dword ptr ds:[edi+0x40]
 *  011d61cf   75 1a            jnz short .011d61eb
 *  011d61d1   837f 44 00       cmp dword ptr ds:[edi+0x44],0x0
 *  011d61d5   74 14            je short .011d61eb
 *  011d61d7   5f               pop edi
 *  011d61d8   b0 01            mov al,0x1
 *  011d61da   5b               pop ebx
 *  011d61db   8b4d f4          mov ecx,dword ptr ss:[ebp-0xc]
 *  011d61de   64:890d 00000000 mov dword ptr fs:[0],ecx
 *  011d61e5   8be5             mov esp,ebp
 *  011d61e7   5d               pop ebp
 *  011d61e8   c2 0400          retn 0x4
 */
bool InsertScenarioPlayerHook()
{
  //const BYTE bytes[] = {
  //  0x53,                    // 00609c0e  |. 53             push ebx
  //  0x8b,0x5d,0x08,          // 00609c0f  |. 8b5d 08        mov ebx,dword ptr ss:[ebp+0x8]
  //  0x57,                    // 00609c12  |. 57             push edi
  //  0x8b,0xf9,               // 00609c13  |. 8bf9           mov edi,ecx
  //  0x8b,0x07,               // 00609c15  |. 8b07           mov eax,dword ptr ds:[edi]
  //  0x83,0xf8, 0x02,         // 00609c17  |. 83f8 02        cmp eax,0x2
  //  0x75, 0x1f,              // 00609c1a  |. 75 1f          jnz short あや�00609c3b
  //  0x3b,0x5f, 0x40,         // 00609c1c  |. 3b5f 40        cmp ebx,dword ptr ds:[edi+0x40]
  //  0x75, 0x1a,              // 00609c1f  |. 75 1a          jnz short あや�00609c3b
  //  0x83,0x7f, 0x44, 0x00,   // 00609c21  |. 837f 44 00     cmp dword ptr ds:[edi+0x44],0x0
  //  0x74, 0x14,              // 00609c25  |. 74 14          je short あや�00609c3b
  //};
  //enum { addr_offset = 0x00609bf0 - 0x00609c0e }; // distance to the beginning of the function

  const BYTE bytes[] = {
    0x74, 0x14,     // 00609c25  |. 74 14          je short あや�00609c3b
    0x5f,           // 00609c27  |. 5f             pop edi
    0xb0, 0x01,     // 00609c28  |. b0 01          mov al,0x1
    0x5b,           // 00609c2a  |. 5b             pop ebx
    0x8b,0x4d, 0xf4 // 00609c2b  |. 8b4d f4        mov ecx,dword ptr ss:[ebp-0xc]
  };
  enum { // distance to the beginning of the function
    addr_offset_A = 0x00609bf0 - 0x00609c25   // -53
    , addr_offset_W = 0x00406540 - 0x00406572 // -50
  };
  ULONG range = min(module_limit_ - module_base_, MAX_REL_ADDR);
  ULONG start = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_base_ + range);
  if (!start) {
    ConsoleOutput("vnreng:ScenarioPlayer: pattern not found");
    return false;
  }

  DWORD addr = MemDbg::findEnclosingAlignedFunction(start, 80); // range is around 50, use 80

  enum : BYTE { push_ebp = 0x55 };  // 011d4c80  /$ 55             push ebp
  if (!addr || *(BYTE *)addr != push_ebp) {
    ConsoleOutput("vnreng:ScenarioPlayer: pattern found but the function offset is invalid");
    return false;
  }

  HookParam hp = {};
  hp.address = addr;
  hp.length_offset = 1;
  hp.offset = 4;
  if (addr - start == addr_offset_W) {
    hp.type = USING_UNICODE;
    ConsoleOutput("vnreng: INSERT ScenarioPlayerW");
    NewHook(hp, "ScenarioPlayerW");
  } else {
    hp.type = BIG_ENDIAN; // 4
    ConsoleOutput("vnreng: INSERT ScenarioPlayerA");
    NewHook(hp, "ScenarioPlayerA");
  }
  return true;
}

/**
 *  jichi 4/19/2014: Marine Heart
 *  See: http://blgames.proboards.com/post/1984
 *       http://www.yaoiotaku.com/forums/threads/11440-huge-bl-game-torrent
 *
 *  Issue: The extracted text someitems has limited repetition
 *  TODO: It might be better to use FindCallAndEntryAbs for gdi32.CreateFontA?
 *        See how FindCallAndEntryAbs is used in Majiro.
 *
 *  妖恋愛奭�神サマ�堕し方/HS4*0@40D160
 *  - addr: 4247904 = 0x40d160
 *  - off: 4
 *  - type: 9
 *
 *  Function starts
 *  0040d160  /$ 55                 push ebp    ; jichi: hook here
 *  0040d161  |. 8bec               mov ebp,esp
 *  0040d163  |. 83c4 90            add esp,-0x70
 *  0040d166  |. 33c0               xor eax,eax
 *  0040d168  |. 53                 push ebx
 *  0040d169  |. 56                 push esi
 *  0040d16a  |. 57                 push edi
 *  0040d16b  |. 8b75 08            mov esi,dword ptr ss:[ebp+0x8]
 *  0040d16e  |. c745 cc 281e4800   mov dword ptr ss:[ebp-0x34],saisys.00481>
 *  0040d175  |. 8965 d0            mov dword ptr ss:[ebp-0x30],esp
 *  0040d178  |. c745 c8 d0d14700   mov dword ptr ss:[ebp-0x38],<jmp.&cp3245>
 *  0040d17f  |. 66:c745 d4 0000    mov word ptr ss:[ebp-0x2c],0x0
 *  0040d185  |. 8945 e0            mov dword ptr ss:[ebp-0x20],eax
 *  0040d188  |. 64:8b15 00000000   mov edx,dword ptr fs:[0]
 *  0040d18f  |. 8955 c4            mov dword ptr ss:[ebp-0x3c],edx
 *  0040d192  |. 8d4d c4            lea ecx,dword ptr ss:[ebp-0x3c]
 *  0040d195  |. 64:890d 00000000   mov dword ptr fs:[0],ecx
 *  0040d19c  |. 8b05 741c4800      mov eax,dword ptr ds:[0x481c74]
 *  0040d1a2  |. 8945 bc            mov dword ptr ss:[ebp-0x44],eax
 *  0040d1a5  |. 8b05 781c4800      mov eax,dword ptr ds:[0x481c78]
 *  0040d1ab  |. 8945 c0            mov dword ptr ss:[ebp-0x40],eax
 *  0040d1ae  |. 8d46 24            lea eax,dword ptr ds:[esi+0x24]
 *  0040d1b1  |. 8b56 14            mov edx,dword ptr ds:[esi+0x14]
 *  0040d1b4  |. 8955 bc            mov dword ptr ss:[ebp-0x44],edx
 *  0040d1b7  |. 8b10               mov edx,dword ptr ds:[eax]
 *  0040d1b9  |. 85d2               test edx,edx
 *  0040d1bb  |. 74 04              je short saisys.0040d1c1
 *  0040d1bd  |. 8b08               mov ecx,dword ptr ds:[eax]
 *  0040d1bf  |. eb 05              jmp short saisys.0040d1c6
 *  0040d1c1  |> b9 9b1c4800        mov ecx,saisys.00481c9b
 *  0040d1c6  |> 51                 push ecx                                 ; /facename
 *  0040d1c7  |. 6a 01              push 0x1                                 ; |pitchandfamily = fixed_pitch|ff_dontcare
 *  0040d1c9  |. 6a 03              push 0x3                                 ; |quality = 3.
 *  0040d1cb  |. 6a 00              push 0x0                                 ; |clipprecision = clip_default_precis
 *  0040d1cd  |. 6a 00              push 0x0                                 ; |outputprecision = out_default_precis
 *  0040d1cf  |. 68 80000000        push 0x80                                ; |charset = 128.
 *  0040d1d4  |. 6a 00              push 0x0                                 ; |strikeout = false
 *  0040d1d6  |. 6a 00              push 0x0                                 ; |underline = false
 *  0040d1d8  |. 6a 00              push 0x0                                 ; |italic = false
 *  0040d1da  |. 68 90010000        push 0x190                               ; |weight = fw_normal
 *  0040d1df  |. 6a 00              push 0x0                                 ; |orientation = 0x0
 *  0040d1e1  |. 6a 00              push 0x0                                 ; |escapement = 0x0
 *  0040d1e3  |. 6a 00              push 0x0                                 ; |width = 0x0
 *  0040d1e5  |. 8b46 04            mov eax,dword ptr ds:[esi+0x4]           ; |
 *  0040d1e8  |. 50                 push eax                                 ; |height
 *  0040d1e9  |. e8 00fa0600        call <jmp.&gdi32.CreateFontA>            ; \createfonta
 *  0040d1ee  |. 8945 b8            mov dword ptr ss:[ebp-0x48],eax
 *  0040d1f1  |. 8b55 b8            mov edx,dword ptr ss:[ebp-0x48]
 *  0040d1f4  |. 85d2               test edx,edx
 *  0040d1f6  |. 75 14              jnz short saisys.0040d20c
 */
bool InsertMarineHeartHook()
{
  // FIXME: Why this does not work?!
  // jichi 6/3/2014: CreateFontA is only called once in this function
  //  0040d160  /$ 55                 push ebp    ; jichi: hook here
  //  0040d161  |. 8bec               mov ebp,esp
  //ULONG addr = Util::FindCallAndEntryAbs((DWORD)CreateFontA, module_limit_ - module_base_, module_base_, 0xec8b);

  const BYTE bytes[] = {
    0x51,                       // 0040d1c6  |> 51                 push ecx                        ; /facename
    0x6a, 0x01,                 // 0040d1c7  |. 6a 01              push 0x1                        ; |pitchandfamily = fixed_pitch|ff_dontcare
    0x6a, 0x03,                 // 0040d1c9  |. 6a 03              push 0x3                        ; |quality = 3.
    0x6a, 0x00,                 // 0040d1cb  |. 6a 00              push 0x0                        ; |clipprecision = clip_default_precis
    0x6a, 0x00,                 // 0040d1cd  |. 6a 00              push 0x0                        ; |outputprecision = out_default_precis
    0x68, 0x80,0x00,0x00,0x00,  // 0040d1cf  |. 68 80000000        push 0x80                       ; |charset = 128.
    0x6a, 0x00,                 // 0040d1d4  |. 6a 00              push 0x0                        ; |strikeout = false
    0x6a, 0x00,                 // 0040d1d6  |. 6a 00              push 0x0                        ; |underline = false
    0x6a, 0x00,                 // 0040d1d8  |. 6a 00              push 0x0                        ; |italic = false
    0x68, 0x90,0x01,0x00,0x00,  // 0040d1da  |. 68 90010000        push 0x190                      ; |weight = fw_normal
    0x6a, 0x00,                 // 0040d1df  |. 6a 00              push 0x0                        ; |orientation = 0x0
    0x6a, 0x00,                 // 0040d1e1  |. 6a 00              push 0x0                        ; |escapement = 0x0
    0x6a, 0x00,                 // 0040d1e3  |. 6a 00              push 0x0                        ; |width = 0x0 0x8b,0x46, 0x04,
    0x8b,0x46, 0x04,            // 0040d1e5  |. 8b46 04            mov eax,dword ptr ds:[esi+0x4]  ; |
    0x50,                       // 0040d1e8  |. 50                 push eax                        ; |height
    0xe8, 0x00,0xfa,0x06,0x00   // 0040d1e9  |. e8 00fa0600        call <jmp.&gdi32.CreateFontA>   ; \createfonta
  };
  enum { addr_offset = 0x0040d160 - 0x0040d1c6 }; // distance to the beginning of the function
  ULONG range = min(module_limit_ - module_base_, MAX_REL_ADDR);
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_base_ + range);
  //GROWL_DWORD(reladdr);
  if (!addr) {
    ConsoleOutput("vnreng:MarineHeart: pattern not found");
    return false;
  }

  addr += addr_offset;
  //addr = 0x40d160;
  //GROWL_DWORD(addr);
  enum : BYTE { push_ebp = 0x55 };  // 011d4c80  /$ 55             push ebp
  if (*(BYTE *)addr != push_ebp) {
    ConsoleOutput("vnreng:MarineHeart: pattern found but the function offset is invalid");
    return false;
  }

  HookParam hp = {};
  hp.address = addr;
  hp.offset = 4;
  hp.type = USING_STRING|DATA_INDIRECT; // = 9

  ConsoleOutput("vnreng: INSERT MarineHeart");
  NewHook(hp, "MarineHeart");
  return true;
}

/**
 *  jichi 6/1/2014:
 *  Observations from 愛姉妹4
 *  - Scenario: arg1 + 4*5 is 0, arg1+0xc is address of the text
 *  - Character: arg1 + 4*10 is 0, arg1+0xc is text
 */
static inline size_t _elf_strlen(LPCSTR p) // limit search address which might be bad
{
  //CC_ASSERT(p);
  for (size_t i = 0; i < VNR_TEXT_CAPACITY; i++)
    if (!*p++)
      return i;
  return 0; // when len >= VNR_TEXT_CAPACITY
}

static void SpecialHookElf(DWORD esp_base, HookParam *, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  //DWORD arg1 = *(DWORD *)(esp_base + 0x4);
  DWORD arg1 = argof(1, esp_base);
  DWORD arg2_scene = arg1 + 4*5,
        arg2_chara = arg1 + 4*10;
  DWORD text; //= 0; // This variable will be killed
  if (*(DWORD *)arg2_scene == 0) {
    text = *(DWORD *)(arg2_scene + 4*3);
    if (!text || ::IsBadReadPtr((LPCVOID)text, 1)) // Text from scenario could be bad when open backlog while the character is speaking
      return;
    *split = 1;
  } else if (*(DWORD *)arg2_chara == 0) {
    text = arg2_chara + 4*3;
    *split = 2;
  } else
    return;
  //if (text && text < MemDbg::UserMemoryStopAddress) {
  *len = _elf_strlen((LPCSTR)text); // in case the text is bad but still readable
  //*len = ::strlen((LPCSTR)text);
  *data = text;
}

/**
 *  jichi 5/31/2014: elf's
 *  Type1: SEXヂ�ーチャー剛史 trial, reladdr = 0x2f0f0, 2 parameters
 *  Type2: 愛姉妹4, reladdr = 0x2f9b0, 3 parameters
 *
 *  IDA: sub_42F9B0 proc near ; bp-based frame
 *    var_8 = dword ptr -8
 *    var_4 = byte ptr -4
 *    var_3 = word ptr -3
 *    arg_0 = dword ptr  8
 *    arg_4 = dword ptr  0Ch
 *    arg_8 = dword ptr  10h
 *
 *  Call graph (Type2):
 *  0x2f9b0 ;  hook here
 *  > 0x666a0 ; called multiple time
 *  > TextOutA ; there are two TextOutA, the second is the right one
 *
 *  Function starts (Type1), pattern offset: 0xc
 *  - 012ef0f0  /$ 55             push ebp ; jichi: hook
 *  - 012ef0f1  |. 8bec           mov ebp,esp
 *  - 012ef0f3  |. 83ec 10        sub esp,0x10
 *  - 012ef0f6  |. 837d 0c 00     cmp dword ptr ss:[ebp+0xc],0x0
 *  - 012ef0fa  |. 53             push ebx
 *  - 012ef0fb  |. 56             push esi
 *  - 012ef0fc  |. 75 0f          jnz short stt_tria.012ef10d ; jicchi: pattern starts
 *  - 012ef0fe  |. 8b45 08        mov eax,dword ptr ss:[ebp+0x8]
 *  - 012ef101  |. 8b48 04        mov ecx,dword ptr ds:[eax+0x4]
 *  - 012ef104  |. 8b91 90000000  mov edx,dword ptr ds:[ecx+0x90] ; jichi: pattern stops
 *  - 012ef10a  |. 8955 0c        mov dword ptr ss:[ebp+0xc],edx
 *  - 012ef10d  |> 8b4d 08        mov ecx,dword ptr ss:[ebp+0x8]
 *  - 012ef110  |. 8b51 04        mov edx,dword ptr ds:[ecx+0x4]
 *  - 012ef113  |. 33c0           xor eax,eax
 *  - 012ef115  |. c645 f8 00     mov byte ptr ss:[ebp-0x8],0x0
 *  - 012ef119  |. 66:8945 f9     mov word ptr ss:[ebp-0x7],ax
 *  - 012ef11d  |. 8b82 b0000000  mov eax,dword ptr ds:[edx+0xb0]
 *  - 012ef123  |. 8945 f4        mov dword ptr ss:[ebp-0xc],eax
 *  - 012ef126  |. 33db           xor ebx,ebx
 *  - 012ef128  |> 8b4f 20        /mov ecx,dword ptr ds:[edi+0x20]
 *  - 012ef12b  |. 83f9 10        |cmp ecx,0x10
 *
 *  Function starts (Type2), pattern offset: 0x10
 *  - 0093f9b0  /$ 55             push ebp  ; jichi: hook here
 *  - 0093f9b1  |. 8bec           mov ebp,esp
 *  - 0093f9b3  |. 83ec 08        sub esp,0x8
 *  - 0093f9b6  |. 837d 10 00     cmp dword ptr ss:[ebp+0x10],0x0
 *  - 0093f9ba  |. 53             push ebx
 *  - 0093f9bb  |. 8b5d 0c        mov ebx,dword ptr ss:[ebp+0xc]
 *  - 0093f9be  |. 56             push esi
 *  - 0093f9bf  |. 57             push edi
 *  - 0093f9c0  |. 75 0f          jnz short silkys.0093f9d1 ; jichi: pattern starts
 *  - 0093f9c2  |. 8b45 08        mov eax,dword ptr ss:[ebp+0x8]
 *  - 0093f9c5  |. 8b48 04        mov ecx,dword ptr ds:[eax+0x4]
 *  - 0093f9c8  |. 8b91 90000000  mov edx,dword ptr ds:[ecx+0x90] ; jichi: pattern stops
 *  - 0093f9ce  |. 8955 10        mov dword ptr ss:[ebp+0x10],edx
 *  - 0093f9d1  |> 33c0           xor eax,eax
 *  - 0093f9d3  |. c645 fc 00     mov byte ptr ss:[ebp-0x4],0x0
 *  - 0093f9d7  |. 66:8945 fd     mov word ptr ss:[ebp-0x3],ax
 *  - 0093f9db  |. 33ff           xor edi,edi
 *  - 0093f9dd  |> 8b53 20        /mov edx,dword ptr ds:[ebx+0x20]
 *  - 0093f9e0  |. 8d4b 0c        |lea ecx,dword ptr ds:[ebx+0xc]
 *  - 0093f9e3  |. 83fa 10        |cmp edx,0x10
 */
bool InsertElfHook()
{
  const BYTE bytes[] = {
      //0x55,                             // 0093f9b0  /$ 55             push ebp  ; jichi: hook here
      //0x8b,0xec,                        // 0093f9b1  |. 8bec           mov ebp,esp
      //0x83,0xec, 0x08,                  // 0093f9b3  |. 83ec 08        sub esp,0x8
      //0x83,0x7d, 0x10, 0x00,            // 0093f9b6  |. 837d 10 00     cmp dword ptr ss:[ebp+0x10],0x0
      //0x53,                             // 0093f9ba  |. 53             push ebx
      //0x8b,0x5d, 0x0c,                  // 0093f9bb  |. 8b5d 0c        mov ebx,dword ptr ss:[ebp+0xc]
      //0x56,                             // 0093f9be  |. 56             push esi
      //0x57,                             // 0093f9bf  |. 57             push edi
      0x75, 0x0f,                       // 0093f9c0  |. 75 0f          jnz short silkys.0093f9d1
      0x8b,0x45, 0x08,                  // 0093f9c2  |. 8b45 08        mov eax,dword ptr ss:[ebp+0x8]
      0x8b,0x48, 0x04,                  // 0093f9c5  |. 8b48 04        mov ecx,dword ptr ds:[eax+0x4]
      0x8b,0x91, 0x90,0x00,0x00,0x00    // 0093f9c8  |. 8b91 90000000  mov edx,dword ptr ds:[ecx+0x90]
  };
  //enum { addr_offset = 0xc };
  ULONG range = min(module_limit_ - module_base_, MAX_REL_ADDR);
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_base_ + range);
  //GROWL_DWORD(addr);
  //addr = 0x42f170; // 愛姉妹4 Trial
  //reladdr = 0x2f9b0; // 愛姉妹4
  //reladdr = 0x2f0f0; // SEXヂ�ーチャー剛史 trial
  if (!addr) {
    ConsoleOutput("vnreng:Elf: pattern not found");
    return false;
  }

  enum : BYTE { push_ebp = 0x55 };
  for (int i = 0; i < 0x20; i++, addr--) // value of i is supposed to be 0xc or 0x10
    if (*(BYTE *)addr == push_ebp) { // beginning of the function

      HookParam hp = {};
      hp.address = addr;
      hp.text_fun = SpecialHookElf;
      hp.type = USING_STRING|NO_CONTEXT; // = 9

      ConsoleOutput("vnreng: INSERT Elf");
      NewHook(hp, "Elf");
      return true;
    }
  ConsoleOutput("vnreng:Elf: function not found");
  return false;
}

/** jichi: 6/17/2015
 *  Sample games
 *  - 堕ちてぁ�新妻 trial
 *  - 根雪の幻影 trial
 *
 *  This function is found by backtracking GetGlyphOutlineA.
 *  There are two GetGlyphOutlineA, which are in the same function.
 *  That function are called by two other functions.
 *  The second function is hooked.
 *
 *  堕ちてぁ�新妻
 *  baseaddr = 08e0000
 *
 *  0096652E   CC               INT3
 *  0096652F   CC               INT3
 *  00966530   55               PUSH EBP
 *  00966531   8BEC             MOV EBP,ESP
 *  00966533   83EC 18          SUB ESP,0x18
 *  00966536   A1 00109F00      MOV EAX,DWORD PTR DS:[0x9F1000]
 *  0096653B   33C5             XOR EAX,EBP
 *  0096653D   8945 FC          MOV DWORD PTR SS:[EBP-0x4],EAX
 *  00966540   53               PUSH EBX
 *  00966541   8B5D 0C          MOV EBX,DWORD PTR SS:[EBP+0xC]
 *  00966544   56               PUSH ESI
 *  00966545   8B75 08          MOV ESI,DWORD PTR SS:[EBP+0x8]
 *  00966548   57               PUSH EDI
 *  00966549   6A 00            PUSH 0x0
 *  0096654B   894D EC          MOV DWORD PTR SS:[EBP-0x14],ECX
 *  0096654E   8B0D FCB7A200    MOV ECX,DWORD PTR DS:[0xA2B7FC]
 *  00966554   68 90D29D00      PUSH .009DD290                           ; ASCII "/Config/SceneSkip"
 *  00966559   895D F0          MOV DWORD PTR SS:[EBP-0x10],EBX
 *  0096655C   E8 2F4A0100      CALL .0097AF90
 *  00966561   83F8 01          CMP EAX,0x1
 *  00966564   0F84 E0010000    JE .0096674A
 *  0096656A   8B55 EC          MOV EDX,DWORD PTR SS:[EBP-0x14]
 *  0096656D   85DB             TEST EBX,EBX
 *  0096656F   75 09            JNZ SHORT .0096657A
 *  00966571   8B42 04          MOV EAX,DWORD PTR DS:[EDX+0x4]
 *  00966574   8B40 38          MOV EAX,DWORD PTR DS:[EAX+0x38]
 *  00966577   8945 F0          MOV DWORD PTR SS:[EBP-0x10],EAX
 *  0096657A   33C0             XOR EAX,EAX
 *  0096657C   C645 F8 00       MOV BYTE PTR SS:[EBP-0x8],0x0
 *  00966580   33C9             XOR ECX,ECX
 *  00966582   66:8945 F9       MOV WORD PTR SS:[EBP-0x7],AX
 *  00966586   3946 14          CMP DWORD PTR DS:[ESI+0x14],EAX
 *  00966589   0F86 BB010000    JBE .0096674A
 *
 *  Scenario stack:
 *
 *  002FF9DC   00955659  RETURN to .00955659 from .00966530
 *  002FF9E0   002FFA10  ; jichi: text in [arg1+4]
 *  002FF9E4   00000000  ; arg2 is zero
 *  002FF9E8   00000001
 *  002FF9EC   784B8FC7
 *
 *  Name stack:
 *
 *  002FF59C   00930A76  RETURN to .00930A76 from .00966530
 *  002FF5A0   002FF5D0 ; jichi: text in [arg1+4]
 *  002FF5A4   004DDEC0 ; arg2 is a pointer
 *  002FF5A8   00000001
 *  002FF5AC   784B8387
 *  002FF5B0   00000182
 *  002FF5B4   00000000
 *
 *  Scenario and Name are called by different callers.
 *
 *  根雪の幻影
 *
 *  00A1A00E   CC               INT3
 *  00A1A00F   CC               INT3
 *  00A1A010   55               PUSH EBP
 *  00A1A011   8BEC             MOV EBP,ESP
 *  00A1A013   83EC 18          SUB ESP,0x18
 *  00A1A016   A1 0050AA00      MOV EAX,DWORD PTR DS:[0xAA5000]
 *  00A1A01B   33C5             XOR EAX,EBP
 *  00A1A01D   8945 FC          MOV DWORD PTR SS:[EBP-0x4],EAX
 *  00A1A020   53               PUSH EBX
 *  00A1A021   56               PUSH ESI
 *  00A1A022   8B75 0C          MOV ESI,DWORD PTR SS:[EBP+0xC]
 *  00A1A025   57               PUSH EDI
 *  00A1A026   8B7D 08          MOV EDI,DWORD PTR SS:[EBP+0x8]
 *  00A1A029   6A 00            PUSH 0x0
 *  00A1A02B   894D F0          MOV DWORD PTR SS:[EBP-0x10],ECX
 *  00A1A02E   8B0D C434AE00    MOV ECX,DWORD PTR DS:[0xAE34C4]
 *  00A1A034   68 F816A900      PUSH .00A916F8                           ; ASCII "/Config/SceneSkip"
 *  00A1A039   8975 EC          MOV DWORD PTR SS:[EBP-0x14],ESI
 *  00A1A03C   E8 7F510100      CALL .00A2F1C0
 *  00A1A041   83F8 01          CMP EAX,0x1
 *  00A1A044   0F84 3A010000    JE .00A1A184
 *  00A1A04A   8B4D F0          MOV ECX,DWORD PTR SS:[EBP-0x10]
 *  00A1A04D   85F6             TEST ESI,ESI
 *  00A1A04F   75 09            JNZ SHORT .00A1A05A
 *  00A1A051   8B41 04          MOV EAX,DWORD PTR DS:[ECX+0x4]
 *  00A1A054   8B40 38          MOV EAX,DWORD PTR DS:[EAX+0x38]
 *  00A1A057   8945 EC          MOV DWORD PTR SS:[EBP-0x14],EAX
 *  00A1A05A   33C0             XOR EAX,EAX
 *  00A1A05C   C645 F8 00       MOV BYTE PTR SS:[EBP-0x8],0x0
 *  00A1A060   33DB             XOR EBX,EBX
 *  00A1A062   66:8945 F9       MOV WORD PTR SS:[EBP-0x7],AX
 *  00A1A066   3947 14          CMP DWORD PTR DS:[EDI+0x14],EAX
 *  00A1A069   0F86 15010000    JBE .00A1A184
 *  00A1A06F   90               NOP
 *  00A1A070   837F 18 10       CMP DWORD PTR DS:[EDI+0x18],0x10
 *  00A1A074   72 05            JB SHORT .00A1A07B
 *  00A1A076   8B47 04          MOV EAX,DWORD PTR DS:[EDI+0x4]
 *  00A1A079   EB 03            JMP SHORT .00A1A07E
 *  00A1A07B   8D47 04          LEA EAX,DWORD PTR DS:[EDI+0x4]
 *  00A1A07E   803C18 00        CMP BYTE PTR DS:[EAX+EBX],0x0
 *  00A1A082   0F84 FC000000    JE .00A1A184
 *  00A1A088   837F 18 10       CMP DWORD PTR DS:[EDI+0x18],0x10
 *  00A1A08C   72 05            JB SHORT .00A1A093
 *  00A1A08E   8B47 04          MOV EAX,DWORD PTR DS:[EDI+0x4]
 *  00A1A091   EB 03            JMP SHORT .00A1A096
 *  00A1A093   8D47 04          LEA EAX,DWORD PTR DS:[EDI+0x4]
 *  00A1A096   8A0418           MOV AL,BYTE PTR DS:[EAX+EBX]
 *  00A1A099   3C 81            CMP AL,0x81
 *  00A1A09B   72 04            JB SHORT .00A1A0A1
 *  00A1A09D   3C 9F            CMP AL,0x9F
 *  00A1A09F   76 06            JBE SHORT .00A1A0A7
 *  00A1A0A1   04 20            ADD AL,0x20
 *  00A1A0A3   3C 0F            CMP AL,0xF
 *  00A1A0A5   77 40            JA SHORT .00A1A0E7
 *  00A1A0A7   837F 18 10       CMP DWORD PTR DS:[EDI+0x18],0x10
 *  00A1A0AB   72 05            JB SHORT .00A1A0B2
 *  00A1A0AD   8B47 04          MOV EAX,DWORD PTR DS:[EDI+0x4]
 *  00A1A0B0   EB 03            JMP SHORT .00A1A0B5
 *  00A1A0B2   8D47 04          LEA EAX,DWORD PTR DS:[EDI+0x4]
 *  00A1A0B5   837F 18 10       CMP DWORD PTR DS:[EDI+0x18],0x10
 *  00A1A0B9   8A0418           MOV AL,BYTE PTR DS:[EAX+EBX]
 *  00A1A0BC   8845 F8          MOV BYTE PTR SS:[EBP-0x8],AL
 *  00A1A0BF   72 13            JB SHORT .00A1A0D4
 *  00A1A0C1   8B47 04          MOV EAX,DWORD PTR DS:[EDI+0x4]
 *  00A1A0C4   C645 F7 02       MOV BYTE PTR SS:[EBP-0x9],0x2
 *  00A1A0C8   8A4418 01        MOV AL,BYTE PTR DS:[EAX+EBX+0x1]
 *  00A1A0CC   83C3 02          ADD EBX,0x2
 *  00A1A0CF   8845 F9          MOV BYTE PTR SS:[EBP-0x7],AL
 *  00A1A0D2   EB 30            JMP SHORT .00A1A104
 *  00A1A0D4   8D47 04          LEA EAX,DWORD PTR DS:[EDI+0x4]
 *  00A1A0D7   C645 F7 02       MOV BYTE PTR SS:[EBP-0x9],0x2
 *  00A1A0DB   8A4418 01        MOV AL,BYTE PTR DS:[EAX+EBX+0x1]
 *  00A1A0DF   83C3 02          ADD EBX,0x2
 *  00A1A0E2   8845 F9          MOV BYTE PTR SS:[EBP-0x7],AL
 *  00A1A0E5   EB 1D            JMP SHORT .00A1A104
 *  00A1A0E7   837F 18 10       CMP DWORD PTR DS:[EDI+0x18],0x10
 *  00A1A0EB   72 05            JB SHORT .00A1A0F2
 *  00A1A0ED   8B47 04          MOV EAX,DWORD PTR DS:[EDI+0x4]
 *  00A1A0F0   EB 03            JMP SHORT .00A1A0F5
 *  00A1A0F2   8D47 04          LEA EAX,DWORD PTR DS:[EDI+0x4]
 *  00A1A0F5   8A0418           MOV AL,BYTE PTR DS:[EAX+EBX]
 *  00A1A0F8   43               INC EBX
 *  00A1A0F9   8845 F8          MOV BYTE PTR SS:[EBP-0x8],AL
 *  00A1A0FC   C645 F9 00       MOV BYTE PTR SS:[EBP-0x7],0x0
 *  00A1A100   C645 F7 01       MOV BYTE PTR SS:[EBP-0x9],0x1
 *  00A1A104   807F 48 01       CMP BYTE PTR DS:[EDI+0x48],0x1
 *  00A1A108   75 21            JNZ SHORT .00A1A12B
 *  00A1A10A   8B49 08          MOV ECX,DWORD PTR DS:[ECX+0x8]
 *  00A1A10D   8D47 38          LEA EAX,DWORD PTR DS:[EDI+0x38]
 *  00A1A110   50               PUSH EAX
 *  00A1A111   FF77 28          PUSH DWORD PTR DS:[EDI+0x28]
 *  00A1A114   8B47 24          MOV EAX,DWORD PTR DS:[EDI+0x24]
 *  00A1A117   03C0             ADD EAX,EAX
 *  00A1A119   50               PUSH EAX
 *  00A1A11A   8D47 20          LEA EAX,DWORD PTR DS:[EDI+0x20]
 *  00A1A11D   50               PUSH EAX
 *  00A1A11E   8D47 1C          LEA EAX,DWORD PTR DS:[EDI+0x1C]
 *  00A1A121   50               PUSH EAX
 *  00A1A122   8D45 F8          LEA EAX,DWORD PTR SS:[EBP-0x8]
 *  00A1A125   50               PUSH EAX
 *  00A1A126   E8 85220000      CALL .00A1C3B0
 *  00A1A12B   FF77 34          PUSH DWORD PTR DS:[EDI+0x34]
 *  00A1A12E   8B4D EC          MOV ECX,DWORD PTR SS:[EBP-0x14]
 *  00A1A131   8D45 F8          LEA EAX,DWORD PTR SS:[EBP-0x8]
 *  00A1A134   FF77 4C          PUSH DWORD PTR DS:[EDI+0x4C]
 *  00A1A137   FF77 30          PUSH DWORD PTR DS:[EDI+0x30]
 *  00A1A13A   FF77 2C          PUSH DWORD PTR DS:[EDI+0x2C]
 *  00A1A13D   FF77 20          PUSH DWORD PTR DS:[EDI+0x20]
 *  00A1A140   FF77 1C          PUSH DWORD PTR DS:[EDI+0x1C]
 *  00A1A143   50               PUSH EAX
 *  00A1A144   E8 1733FFFF      CALL .00A0D460
 *  00A1A149   0FBE45 F7        MOVSX EAX,BYTE PTR SS:[EBP-0x9]
 *  00A1A14D   0FAF47 24        IMUL EAX,DWORD PTR DS:[EDI+0x24]
 *  00A1A151   0147 1C          ADD DWORD PTR DS:[EDI+0x1C],EAX
 *  00A1A154   807F 48 00       CMP BYTE PTR DS:[EDI+0x48],0x0
 *  00A1A158   8B47 1C          MOV EAX,DWORD PTR DS:[EDI+0x1C]
 *  00A1A15B   75 1B            JNZ SHORT .00A1A178
 *  00A1A15D   3947 40          CMP DWORD PTR DS:[EDI+0x40],EAX
 *  00A1A160   7F 16            JG SHORT .00A1A178
 *  00A1A162   8B47 38          MOV EAX,DWORD PTR DS:[EDI+0x38]
 *  00A1A165   8B4F 28          MOV ECX,DWORD PTR DS:[EDI+0x28]
 *  00A1A168   014F 20          ADD DWORD PTR DS:[EDI+0x20],ECX
 *  00A1A16B   8947 1C          MOV DWORD PTR DS:[EDI+0x1C],EAX
 *  00A1A16E   8B47 20          MOV EAX,DWORD PTR DS:[EDI+0x20]
 *  00A1A171   03C1             ADD EAX,ECX
 *  00A1A173   3B47 44          CMP EAX,DWORD PTR DS:[EDI+0x44]
 *  00A1A176   7D 0C            JGE SHORT .00A1A184
 *  00A1A178   8B4D F0          MOV ECX,DWORD PTR SS:[EBP-0x10]
 *  00A1A17B   3B5F 14          CMP EBX,DWORD PTR DS:[EDI+0x14]
 *  00A1A17E  ^0F82 ECFEFFFF    JB .00A1A070
 *  00A1A184   8B4D FC          MOV ECX,DWORD PTR SS:[EBP-0x4]
 *  00A1A187   5F               POP EDI
 *  00A1A188   5E               POP ESI
 *  00A1A189   33CD             XOR ECX,EBP
 *  00A1A18B   5B               POP EBX
 *  00A1A18C   E8 87600200      CALL .00A40218
 *  00A1A191   8BE5             MOV ESP,EBP
 *  00A1A193   5D               POP EBP
 *  00A1A194   C2 0C00          RETN 0xC
 *  00A1A197   CC               INT3
 *  00A1A198   CC               INT3
 */
static void SpecialHookSilkys(DWORD esp_base, HookParam *, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  //DWORD arg1 = *(DWORD *)(esp_base + 0x4);
  DWORD arg1 = argof(1, esp_base),
        arg2 = argof(2, esp_base);

  int size = *(DWORD *)(arg1 + 0x14);
  if (size <= 0)
    return;

  enum { ShortTextCapacity = 0x10 };

  DWORD text = 0;
  //if (arg2 == 0) {
  if (size >= ShortTextCapacity) {
    text = *(DWORD *)(arg1 + 4);
    if (text && ::IsBadReadPtr((LPCVOID)text, size)) // this might not be needed though
      text = 0;
  }
  if (!text) { // short text
    text = arg1 + 4;
    size = min(size, ShortTextCapacity);
  }
  *len = size;
  *data = text;

  *split = arg2 == 0 ? 1 : 2; // arg2 == 0 ? scenario : name
}
bool InsertSilkysHook()
{
  ULONG startAddress, stopAddress;
  if (!NtInspect::getProcessMemoryRange(&startAddress, &stopAddress)) { // need accurate stopAddress
    ConsoleOutput("vnreng:Silkys: failed to get memory range");
    return false;
  }

  const BYTE bytes[] = {
    0x66,0x89,0x45, 0xf9,   // 00a1a062   66:8945 f9       mov word ptr ss:[ebp-0x7],ax
    0x39,0x47, 0x14         // 00a1a066   3947 14          cmp dword ptr ds:[edi+0x14],eax
  };
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress);
  if (!addr) {
    ConsoleOutput("vnreng:Silkys: pattern not found");
    return false;
  }

  addr = MemDbg::findEnclosingAlignedFunction(addr);
  if (!addr) {
    ConsoleOutput("vnreng:Silkys: function not found");
    return false;
  }

  HookParam hp = {};
  hp.address = addr;
  hp.text_fun = SpecialHookSilkys;
  hp.type = USING_STRING|NO_CONTEXT; // = 9

  ConsoleOutput("vnreng: INSERT Silkys");
  NewHook(hp, "SilkysPlus");
  return true;
}

/** jichi 6/1/2014 Eushully
 *  Insert to the last GetTextExtentPoint32A
 *
 *  ATCode:
 *  http://capita.tistory.com/m/post/255
 *
 *  Binary:
 *  {AGE.EXE!0x000113C3(89 C2 C1 E2 04 29 C2 E8 BD 25 20 00 52 89 D1 59), AGE.EXE!0x00012A47(E8 40 0F 20 00 90 90 90 90), AGE.EXE!0x0001DF07(55 8B EC 83 EC 08 56 EB 07 E8 32 5A 1F 00 EB F0), AGE.EXE!0x002137CE(90 90 90 90 90 C2 04 00 53 8B 1A 83 FB 6E 74 14 81 FB 96 01 00 00 74 1B 83 FB 6F 74 25 83 FB 72 74 27 EB 2C 8B 5A 10 89 1F 83 C7 04 B8 05 00 00 00 EB 1F 8B 5A 10 89 1F 83 C7 04 B8 07 00 00 00 EB 10 B8 03 00 00 00 EB 09 B8 01 00 00 00 EB 02 31 C0 5B C3 60 89 E5 83 EC 18 E8 7E 01 00 00 8B 55 F8 83 3A 00 75 31 8B 45 FC 8B 4C 30 E8 89 CA C1 E2 04 29 CA 8D 0C D6 8B 1C 08 51 8B 4C 08 FC 8B 7D F4 89 DA E8 7E FF FF FF 85 C0 74 0A 83 F8 01 74 09 8D 14 82 EB ED 89 EC 61 C3 C7 07 00 00 00 00 8B 75 F4 8B 7D F0 52 8B 06 85 C0 74 17 8D 04 81 8A 10 80 FA FF 74 08 F6 D2 88 17 40 47 EB F1 83 C6 04 EB E3 8B 55 F0 52 8B 02 E8 2F FF FF FF 8B 12 39 D0 74 C1 8B 55 F8 C7 02 01 00 00 00 8B 4D E4 8B 45 FC 8D 04 08 8B 55 F8 89 42 04 58 89 42 08 89 5A 0C 8B 45 FC 8B 4C 08 FC 8B 45 F4 8B 00 89 42 10 8D 04 81 89 42 14 8B 72 0C 8B 7D EC B9 08 00 00 00 F3 A5 8B 5D E8 8B 7A 14 8B 75 F0 31 C9 52 8A 06 84 C0 74 0F F6 D0 8A 14 39 88 14 19 88 04 39 41 46 EB EB 5A 8B 04 39 89 04 19 31 C0 F7 D0 89 04 39 83 C1 04 89 4A 18 8B 7A 0C 8B 42 10 31 C9 BB 6E 00 00 00 89 1F 89 4F 04 89 4F 08 C7 47 0C 02 00 00 00 83 C3 04 89 5F 14 89 4F 18 89 4F 1C 89 EC 61 C3 60 89 E5 83 EC 18 E8 59 00 00 00 8B 5D F8 83 3B 01 75 2E 31 C9 89 0B 8B 7B 0C 8B 75 EC 8D 49 08 F3 A5 8B 7B 14 8B 75 E8 8B 4B 18 F3 A4 8B 43 04 8B 53 08 89 10 8D 7B 04 31 C0 B9 40 01 00 00 F3 AB 89 EC 61 C3 8B 8C D6 A8 D7 05 00 8B 01 3D 96 01 00 00 74 07 83 F8 6E 74 02 EB 07 E8 7A FE FF FF 8B 01 C3 60 C7 45 FC A8 D7 05 00 EB 03 58 EB 05 E8 F8 FF FF FF 2D BD 39 21 00 03 80 D4 02 00 00 B9 00 01 00 00 8D 80 00 40 01 00 89 45 F8 8D 04 01 89 45 F4 8D 04 01 89 45 F0 8D 04 01 89 45 EC 8D 04 01 89 45 E8 61 C3)}
 *
 *  #1 other text AGE.EXE!0x000113C3(89 C2 C1 E2 04 29 C2 E8 BD 25 20 00 52 89 D1 59)
 *  #2 scenario AGE.EXE!0x00012A47(E8 40 0F 20 00 90 90 90 90)
 *
 *  0041130B   8B96 9CA30A00    MOV EDX,DWORD PTR DS:[ESI+0xAA39C]
 *  00411311   81A6 CCA90A00 FF>AND DWORD PTR DS:[ESI+0xAA9CC],0xF7FFFFF>
 *  0041131B   33C0             XOR EAX,EAX
 *  0041131D   50               PUSH EAX
 *  0041131E   8986 1C160000    MOV DWORD PTR DS:[ESI+0x161C],EAX
 *  00411324   8986 78EB0500    MOV DWORD PTR DS:[ESI+0x5EB78],EAX
 *  0041132A   8B42 0C          MOV EAX,DWORD PTR DS:[EDX+0xC]
 *  0041132D   68 F4536100      PUSH .006153F4                           ; ASCII "message:ReadTextSkip"
 *  00411332   8D8E 9CA30A00    LEA ECX,DWORD PTR DS:[ESI+0xAA39C]
 *  00411338   FFD0             CALL EAX
 *  0041133A   8B96 9CA30A00    MOV EDX,DWORD PTR DS:[ESI+0xAA39C]
 *  00411340   8B42 04          MOV EAX,DWORD PTR DS:[EDX+0x4]
 *  00411343   68 4C606100      PUSH .0061604C                           ; ASCII "set:CancelMesSkipOnClick"
 *  00411348   8D8E 9CA30A00    LEA ECX,DWORD PTR DS:[ESI+0xAA39C]
 *  0041134E   FFD0             CALL EAX
 *  00411350   83F8 02          CMP EAX,0x2
 *  00411353   75 1A            JNZ SHORT .0041136F
 *  00411355   68 34606100      PUSH .00616034                           ; ASCII "CALLBACK_SETTING.BIN"
 *  0041135A   8BCE             MOV ECX,ESI
 *  0041135C   E8 7FFBFFFF      CALL .00410EE0
 *  00411361   5F               POP EDI
 *  00411362   5E               POP ESI
 *  00411363   5B               POP EBX
 *  00411364   C3               RETN
 *  00411365   C786 18770700 01>MOV DWORD PTR DS:[ESI+0x77718],0x1
 *  0041136F   83BE 6C780700 00 CMP DWORD PTR DS:[ESI+0x7786C],0x0
 *  00411376   75 45            JNZ SHORT .004113BD
 *  00411378   F603 40          TEST BYTE PTR DS:[EBX],0x40
 *  0041137B   75 40            JNZ SHORT .004113BD
 *  0041137D   81A6 CCA90A00 FF>AND DWORD PTR DS:[ESI+0xAA9CC],0xF7FFFFF>
 *  00411387   33DB             XOR EBX,EBX
 *  00411389   8DBE B0780700    LEA EDI,DWORD PTR DS:[ESI+0x778B0]
 *  0041138F   90               NOP
 *  00411390   8B07             MOV EAX,DWORD PTR DS:[EDI]
 *  00411392   85C0             TEST EAX,EAX
 *  00411394   74 1E            JE SHORT .004113B4
 *  00411396   8B8F E4D5F8FF    MOV ECX,DWORD PTR DS:[EDI+0xFFF8D5E4]
 *  0041139C   8B57 0C          MOV EDX,DWORD PTR DS:[EDI+0xC]
 *  0041139F   51               PUSH ECX
 *  004113A0   52               PUSH EDX
 *  004113A1   50               PUSH EAX
 *  004113A2   53               PUSH EBX
 *  004113A3   8D8E 04480100    LEA ECX,DWORD PTR DS:[ESI+0x14804]
 *  004113A9   E8 42840900      CALL .004A97F0
 *  004113AE   C707 00000000    MOV DWORD PTR DS:[EDI],0x0
 *  004113B4   43               INC EBX
 *  004113B5   83C7 04          ADD EDI,0x4
 *  004113B8   83FB 03          CMP EBX,0x3
 *  004113BB  ^7C D3            JL SHORT .00411390
 *  004113BD   8B86 90D70500    MOV EAX,DWORD PTR DS:[ESI+0x5D790]
 *  004113C3   8BC8             MOV ECX,EAX         ; jichi: #1 hook here
 *  004113C5   C1E1 04          SHL ECX,0x4
 *  004113C8   2BC8             SUB ECX,EAX
 *  004113CA   8B94CE A8D70500  MOV EDX,DWORD PTR DS:[ESI+ECX*8+0x5D7A8]
 *  004113D1   8B02             MOV EAX,DWORD PTR DS:[EDX]
 *  004113D3   85C0             TEST EAX,EAX
 *  //004113C3   89C2             MOV EDX,EAX
 *  //004113C5   C1E2 04          SHL EDX,0x4
 *  //004113C8   29C2             SUB EDX,EAX
 *  //004113CA   E8 BD252000      CALL .0061398C
 *  //004113CF   52               PUSH EDX
 *  //004113D0   89D1             MOV ECX,EDX
 *  //004113D2   59               POP ECX
 *  004113D5   78 35            JS SHORT .0041140C
 *  004113D7   3D 00040000      CMP EAX,0x400
 *  004113DC   7D 2E            JGE SHORT .0041140C
 *  004113DE   8B8486 244F0A00  MOV EAX,DWORD PTR DS:[ESI+EAX*4+0xA4F24]
 *  004113E5   8BCE             MOV ECX,ESI
 *  004113E7   FFD0             CALL EAX
 *  004113E9   8B86 90D70500    MOV EAX,DWORD PTR DS:[ESI+0x5D790]
 *  004113EF   8BC8             MOV ECX,EAX
 *  004113F1   C1E1 04          SHL ECX,0x4
 *  004113F4   2BC8             SUB ECX,EAX
 *  004113F6   8B94CE 04D80500  MOV EDX,DWORD PTR DS:[ESI+ECX*8+0x5D804]
 *  004113FD   8D04CE           LEA EAX,DWORD PTR DS:[ESI+ECX*8]
 *  00411400   03D2             ADD EDX,EDX
 *  00411402   03D2             ADD EDX,EDX
 *  00411404   0190 A8D70500    ADD DWORD PTR DS:[EAX+0x5D7A8],EDX
 *  0041140A   EB 07            JMP SHORT .00411413
 *  0041140C   8BCE             MOV ECX,ESI
 *  0041140E   E8 7D6C0000      CALL .00418090
 *  00411413   8B86 9CA30A00    MOV EAX,DWORD PTR DS:[ESI+0xAA39C]
 *  00411419   8B50 04          MOV EDX,DWORD PTR DS:[EAX+0x4]
 *  0041141C   8D8E 9CA30A00    LEA ECX,DWORD PTR DS:[ESI+0xAA39C]
 *  00411422   68 4C606100      PUSH .0061604C                           ; ASCII "set:CancelMesSkipOnClick"
 *  00411427   FFD2             CALL EDX
 *  00411429   85C0             TEST EAX,EAX
 *  0041142B  ^0F85 30FFFFFF    JNZ .00411361
 *  00411431   3986 D8C90000    CMP DWORD PTR DS:[ESI+0xC9D8],EAX
 *  00411437  ^0F84 24FFFFFF    JE .00411361
 *  0041143D   8B86 D0A90A00    MOV EAX,DWORD PTR DS:[ESI+0xAA9D0]
 *  00411443   A8 10            TEST AL,0x10
 *  00411445   0F84 84000000    JE .004114CF
 *  0041144B   83E0 EF          AND EAX,0xFFFFFFEF
 *  0041144E   83BE 10770700 00 CMP DWORD PTR DS:[ESI+0x77710],0x0
 *  00411455   8986 D0A90A00    MOV DWORD PTR DS:[ESI+0xAA9D0],EAX
 *  0041145B  ^0F85 00FFFFFF    JNZ .00411361
 *  00411461   8B86 ECC90000    MOV EAX,DWORD PTR DS:[ESI+0xC9EC]
 *  00411467   8DBE 3C550000    LEA EDI,DWORD PTR DS:[ESI+0x553C]
 *  0041146D   85C0             TEST EAX,EAX
 *  0041146F  ^0F88 ECFEFFFF    JS .00411361
 *  00411475   3987 08040000    CMP DWORD PTR DS:[EDI+0x408],EAX
 *  0041147B  ^0F8E E0FEFFFF    JLE .00411361
 *  00411481   8BCE             MOV ECX,ESI
 *  00411483   E8 A86AFFFF      CALL .00407F30
 *  00411488   6A 00            PUSH 0x0
 *  0041148A   8BCE             MOV ECX,ESI
 *  0041148C   E8 EF3CFFFF      CALL .00405180
 *  00411491   8B86 90D70500    MOV EAX,DWORD PTR DS:[ESI+0x5D790]
 *  00411497   8BC8             MOV ECX,EAX
 *  00411499   C1E1 04          SHL ECX,0x4
 *  0041149C   2BC8             SUB ECX,EAX
 *  0041149E   8D34CE           LEA ESI,DWORD PTR DS:[ESI+ECX*8]
 *  004114A1   8BCF             MOV ECX,EDI
 *  004114A3   E8 0839FFFF      CALL .00404DB0
 *  004114A8   8B96 A4D70500    MOV EDX,DWORD PTR DS:[ESI+0x5D7A4]
 *  004114AE   8D0482           LEA EAX,DWORD PTR DS:[EDX+EAX*4]
 *  004114B1   8986 A8D70500    MOV DWORD PTR DS:[ESI+0x5D7A8],EAX
 *  004114B7   C787 B0740000 FF>MOV DWORD PTR DS:[EDI+0x74B0],-0x1
 *
 *  00412953   53               PUSH EBX
 *  00412954   FF15 B8406100    CALL DWORD PTR DS:[0x6140B8]             ; kernel32.Sleep
 *  0041295A   53               PUSH EBX
 *  0041295B   53               PUSH EBX
 *  0041295C   53               PUSH EBX
 *  0041295D   53               PUSH EBX
 *  0041295E   8D8D 34F8FFFF    LEA ECX,DWORD PTR SS:[EBP-0x7CC]
 *  00412964   51               PUSH ECX
 *  00412965   FF15 AC436100    CALL DWORD PTR DS:[0x6143AC]             ; user32.PeekMessageA
 *  0041296B   85C0             TEST EAX,EAX
 *  0041296D  ^0F85 5DF3FFFF    JNZ .00411CD0
 *  00412973  ^E9 D8F3FFFF      JMP .00411D50
 *  00412978   A9 00000020      TEST EAX,0x20000000
 *  0041297D   74 0C            JE SHORT .0041298B
 *  0041297F   8BCE             MOV ECX,ESI
 *  00412981   E8 3A63FFFF      CALL .00408CC0
 *  00412986  ^E9 C5F3FFFF      JMP .00411D50
 *  0041298B   85C0             TEST EAX,EAX
 *  0041298D   79 14            JNS SHORT .004129A3
 *  0041298F   8BCE             MOV ECX,ESI
 *  00412991   E8 AAEBFFFF      CALL .00411540
 *  00412996   6A 02            PUSH 0x2
 *  00412998   FF15 B8406100    CALL DWORD PTR DS:[0x6140B8]             ; kernel32.Sleep
 *  0041299E  ^E9 ADF3FFFF      JMP .00411D50
 *  004129A3   A8 01            TEST AL,0x1
 *  004129A5   74 25            JE SHORT .004129CC
 *  004129A7   8D8E D08D0600    LEA ECX,DWORD PTR DS:[ESI+0x68DD0]
 *  004129AD   E8 CEF30300      CALL .00451D80
 *  004129B2   8985 ACF8FFFF    MOV DWORD PTR SS:[EBP-0x754],EAX
 *  004129B8   3BC3             CMP EAX,EBX
 *  004129BA  ^0F8C 90F3FFFF    JL .00411D50
 *  004129C0   83A6 CCA90A00 FE AND DWORD PTR DS:[ESI+0xAA9CC],0xFFFFFFF>
 *  004129C7  ^E9 84F3FFFF      JMP .00411D50
 *  004129CC   A8 20            TEST AL,0x20
 *  004129CE   74 3C            JE SHORT .00412A0C
 *  004129D0   8D8E 5C8E0600    LEA ECX,DWORD PTR DS:[ESI+0x68E5C]
 *  004129D6   E8 A5F30300      CALL .00451D80
 *  004129DB   8985 ACF8FFFF    MOV DWORD PTR SS:[EBP-0x754],EAX
 *  004129E1   3BC3             CMP EAX,EBX
 *  004129E3  ^0F8C 67F3FFFF    JL .00411D50
 *  004129E9   83A6 CCA90A00 DF AND DWORD PTR DS:[ESI+0xAA9CC],0xFFFFFFD>
 *  004129F0   8D8E 5C8E0600    LEA ECX,DWORD PTR DS:[ESI+0x68E5C]
 *  004129F6   E8 45EE0300      CALL .00451840
 *  004129FB   50               PUSH EAX
 *  004129FC   8D8E 5C8E0600    LEA ECX,DWORD PTR DS:[ESI+0x68E5C]
 *  00412A02   E8 39F30300      CALL .00451D40
 *  00412A07  ^E9 44F3FFFF      JMP .00411D50
 *  00412A0C   A9 00000010      TEST EAX,0x10000000
 *  00412A11   74 14            JE SHORT .00412A27
 *  00412A13   8BCE             MOV ECX,ESI
 *  00412A15   E8 A664FFFF      CALL .00408EC0
 *  00412A1A   6A 02            PUSH 0x2
 *  00412A1C   FF15 B8406100    CALL DWORD PTR DS:[0x6140B8]             ; kernel32.Sleep
 *  00412A22  ^E9 29F3FFFF      JMP .00411D50
 *  00412A27   A9 00008000      TEST EAX,0x800000
 *  00412A2C   74 0C            JE SHORT .00412A3A
 *  00412A2E   8BCE             MOV ECX,ESI
 *  00412A30   E8 6B66FFFF      CALL .004090A0
 *  00412A35  ^E9 16F3FFFF      JMP .00411D50
 *  00412A3A   8B86 90D70500    MOV EAX,DWORD PTR DS:[ESI+0x5D790]
 *  00412A40   8BD0             MOV EDX,EAX
 *  00412A42   C1E2 04          SHL EDX,0x4
 *  00412A45   2BD0             SUB EDX,EAX
 *  00412A47   8B84D6 A8D70500  MOV EAX,DWORD PTR DS:[ESI+EDX*8+0x5D7A8]    ; jichi: #2 hook here
 *  //00412A47   E8 400F2000      CALL .0061398C
 *  00412A4E   8B00             MOV EAX,DWORD PTR DS:[EAX]
 *  00412A50   3BC3             CMP EAX,EBX
 *  00412A52   7C 37            JL SHORT .00412A8B
 *  00412A54   3D 00040000      CMP EAX,0x400
 *  00412A59   7D 30            JGE SHORT .00412A8B
 *  00412A5B   8BCE             MOV ECX,ESI
 *  00412A5D   8B9486 244F0A00  MOV EDX,DWORD PTR DS:[ESI+EAX*4+0xA4F24]
 *  00412A64   FFD2             CALL EDX
 *  00412A66   8B86 90D70500    MOV EAX,DWORD PTR DS:[ESI+0x5D790]
 *  00412A6C   8BC8             MOV ECX,EAX
 *  00412A6E   C1E1 04          SHL ECX,0x4
 *  00412A71   2BC8             SUB ECX,EAX
 *  00412A73   8D04CE           LEA EAX,DWORD PTR DS:[ESI+ECX*8]
 *  00412A76   8B90 04D80500    MOV EDX,DWORD PTR DS:[EAX+0x5D804]
 *  00412A7C   03D2             ADD EDX,EDX
 *  00412A7E   03D2             ADD EDX,EDX
 *  00412A80   0190 A8D70500    ADD DWORD PTR DS:[EAX+0x5D7A8],EDX
 *  00412A86  ^E9 C5F2FFFF      JMP .00411D50
 *  00412A8B   8BCE             MOV ECX,ESI
 *  00412A8D   E8 FE550000      CALL .00418090
 *  00412A92  ^E9 B9F2FFFF      JMP .00411D50
 *  00412A97   C785 A4F8FFFF 01>MOV DWORD PTR SS:[EBP-0x75C],0x1
 *  00412AA1   C745 FC FFFFFFFF MOV DWORD PTR SS:[EBP-0x4],-0x1
 *  00412AA8   B8 E02D4100      MOV EAX,.00412DE0
 *  00412AAD   C3               RETN
 *  00412AAE   8B85 14F8FFFF    MOV EAX,DWORD PTR SS:[EBP-0x7EC]
 *  00412AB4   50               PUSH EAX
 *  00412AB5   8B8D 10F8FFFF    MOV ECX,DWORD PTR SS:[EBP-0x7F0]
 *
 *  Patched code:
 *
 *  0041DF07   55               PUSH EBP
 *  0041DF08   8BEC             MOV EBP,ESP
 *  0041DF0A   83EC 08          SUB ESP,0x8
 *  0041DF0D   56               PUSH ESI
 *  0041DF0E   EB 07            JMP SHORT .0041DF17
 *  0041DF10   E8 325A1F00      CALL .00613947
 *  0041DF15  ^EB F0            JMP SHORT .0041DF07
 *
 *  006137CE   90               NOP
 *  006137CF   90               NOP
 *  006137D0   90               NOP
 *  006137D1   90               NOP
 *  006137D2   90               NOP
 *  006137D3   C2 0400          RETN 0x4
 *  006137D6   53               PUSH EBX
 *  006137D7   8B1A             MOV EBX,DWORD PTR DS:[EDX]
 *  006137D9   83FB 6E          CMP EBX,0x6E
 *  006137DC   74 14            JE SHORT .006137F2
 *  006137DE   81FB 96010000    CMP EBX,0x196
 *  006137E4   74 1B            JE SHORT .00613801
 *  006137E6   83FB 6F          CMP EBX,0x6F
 *  006137E9   74 25            JE SHORT .00613810
 *  006137EB   83FB 72          CMP EBX,0x72
 *  006137EE   74 27            JE SHORT .00613817
 *  006137F0   EB 2C            JMP SHORT .0061381E
 *  006137F2   8B5A 10          MOV EBX,DWORD PTR DS:[EDX+0x10]
 *  006137F5   891F             MOV DWORD PTR DS:[EDI],EBX
 *  006137F7   83C7 04          ADD EDI,0x4
 *  006137FA   B8 05000000      MOV EAX,0x5
 *  006137FF   EB 1F            JMP SHORT .00613820
 *  00613801   8B5A 10          MOV EBX,DWORD PTR DS:[EDX+0x10]
 *  00613804   891F             MOV DWORD PTR DS:[EDI],EBX
 *  00613806   83C7 04          ADD EDI,0x4
 *  00613809   B8 07000000      MOV EAX,0x7
 *  0061380E   EB 10            JMP SHORT .00613820
 *  00613810   B8 03000000      MOV EAX,0x3
 *  00613815   EB 09            JMP SHORT .00613820
 *  00613817   B8 01000000      MOV EAX,0x1
 *  0061381C   EB 02            JMP SHORT .00613820
 *  0061381E   31C0             XOR EAX,EAX
 *  00613820   5B               POP EBX
 *  00613821   C3               RETN
 *  00613822   60               PUSHAD      ; jichi: the translate function for hookpoint #2
 *  00613823   89E5             MOV EBP,ESP
 *  00613825   83EC 18          SUB ESP,0x18    ; reserve 18 local variables
 *  00613828   E8 7E010000      CALL .006139AB
 *  0061382D   8B55 F8          MOV EDX,DWORD PTR SS:[EBP-0x8]
 *  00613830   833A 00          CMP DWORD PTR DS:[EDX],0x0
 *  00613833   75 31            JNZ SHORT .00613866
 *  00613835   8B45 FC          MOV EAX,DWORD PTR SS:[EBP-0x4]
 *  00613838   8B4C30 E8        MOV ECX,DWORD PTR DS:[EAX+ESI-0x18]
 *  0061383C   89CA             MOV EDX,ECX
 *  0061383E   C1E2 04          SHL EDX,0x4
 *  00613841   29CA             SUB EDX,ECX
 *  00613843   8D0CD6           LEA ECX,DWORD PTR DS:[ESI+EDX*8]
 *  00613846   8B1C08           MOV EBX,DWORD PTR DS:[EAX+ECX]
 *  00613849   51               PUSH ECX
 *  0061384A   8B4C08 FC        MOV ECX,DWORD PTR DS:[EAX+ECX-0x4]
 *  0061384E   8B7D F4          MOV EDI,DWORD PTR SS:[EBP-0xC]
 *  00613851   89DA             MOV EDX,EBX
 *  00613853   E8 7EFFFFFF      CALL .006137D6
 *  00613858   85C0             TEST EAX,EAX
 *  0061385A   74 0A            JE SHORT .00613866
 *  0061385C   83F8 01          CMP EAX,0x1
 *  0061385F   74 09            JE SHORT .0061386A
 *  00613861   8D1482           LEA EDX,DWORD PTR DS:[EDX+EAX*4]
 *  00613864  ^EB ED            JMP SHORT .00613853
 *  00613866   89EC             MOV ESP,EBP
 *  00613868   61               POPAD
 *  00613869   C3               RETN
 *  0061386A   C707 00000000    MOV DWORD PTR DS:[EDI],0x0
 *  00613870   8B75 F4          MOV ESI,DWORD PTR SS:[EBP-0xC]
 *  00613873   8B7D F0          MOV EDI,DWORD PTR SS:[EBP-0x10]
 *  00613876   52               PUSH EDX
 *  00613877   8B06             MOV EAX,DWORD PTR DS:[ESI]
 *  00613879   85C0             TEST EAX,EAX
 *  0061387B   74 17            JE SHORT .00613894
 *  0061387D   8D0481           LEA EAX,DWORD PTR DS:[ECX+EAX*4]
 *  00613880   8A10             MOV DL,BYTE PTR DS:[EAX]
 *  00613882   80FA FF          CMP DL,0xFF
 *  00613885   74 08            JE SHORT .0061388F
 *  00613887   F6D2             NOT DL
 *  00613889   8817             MOV BYTE PTR DS:[EDI],DL
 *  0061388B   40               INC EAX
 *  0061388C   47               INC EDI
 *  0061388D  ^EB F1            JMP SHORT .00613880
 *  0061388F   83C6 04          ADD ESI,0x4
 *  00613892  ^EB E3            JMP SHORT .00613877
 *  00613894   8B55 F0          MOV EDX,DWORD PTR SS:[EBP-0x10]
 *  00613897   52               PUSH EDX
 *  00613898   8B02             MOV EAX,DWORD PTR DS:[EDX]
 *  0061389A   E8 2FFFFFFF      CALL .006137CE
 *  0061389F   8B12             MOV EDX,DWORD PTR DS:[EDX]
 *  006138A1   39D0             CMP EAX,EDX
 *  006138A3  ^74 C1            JE SHORT .00613866
 *  006138A5   8B55 F8          MOV EDX,DWORD PTR SS:[EBP-0x8]
 *  006138A8   C702 01000000    MOV DWORD PTR DS:[EDX],0x1
 *  006138AE   8B4D E4          MOV ECX,DWORD PTR SS:[EBP-0x1C]
 *  006138B1   8B45 FC          MOV EAX,DWORD PTR SS:[EBP-0x4]
 *  006138B4   8D0408           LEA EAX,DWORD PTR DS:[EAX+ECX]
 *  006138B7   8B55 F8          MOV EDX,DWORD PTR SS:[EBP-0x8]
 *  006138BA   8942 04          MOV DWORD PTR DS:[EDX+0x4],EAX
 *  006138BD   58               POP EAX
 *  006138BE   8942 08          MOV DWORD PTR DS:[EDX+0x8],EAX
 *  006138C1   895A 0C          MOV DWORD PTR DS:[EDX+0xC],EBX
 *  006138C4   8B45 FC          MOV EAX,DWORD PTR SS:[EBP-0x4]
 *  006138C7   8B4C08 FC        MOV ECX,DWORD PTR DS:[EAX+ECX-0x4]
 *  006138CB   8B45 F4          MOV EAX,DWORD PTR SS:[EBP-0xC]
 *  006138CE   8B00             MOV EAX,DWORD PTR DS:[EAX]
 *  006138D0   8942 10          MOV DWORD PTR DS:[EDX+0x10],EAX
 *  006138D3   8D0481           LEA EAX,DWORD PTR DS:[ECX+EAX*4]
 *  006138D6   8942 14          MOV DWORD PTR DS:[EDX+0x14],EAX
 *  006138D9   8B72 0C          MOV ESI,DWORD PTR DS:[EDX+0xC]
 *  006138DC   8B7D EC          MOV EDI,DWORD PTR SS:[EBP-0x14]
 *  006138DF   B9 08000000      MOV ECX,0x8
 *  006138E4   F3:A5            REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS>
 *  006138E6   8B5D E8          MOV EBX,DWORD PTR SS:[EBP-0x18]
 *  006138E9   8B7A 14          MOV EDI,DWORD PTR DS:[EDX+0x14]
 *  006138EC   8B75 F0          MOV ESI,DWORD PTR SS:[EBP-0x10]
 *  006138EF   31C9             XOR ECX,ECX
 *  006138F1   52               PUSH EDX
 *  006138F2   8A06             MOV AL,BYTE PTR DS:[ESI]
 *  006138F4   84C0             TEST AL,AL
 *  006138F6   74 0F            JE SHORT .00613907
 *  006138F8   F6D0             NOT AL
 *  006138FA   8A1439           MOV DL,BYTE PTR DS:[ECX+EDI]
 *  006138FD   881419           MOV BYTE PTR DS:[ECX+EBX],DL
 *  00613900   880439           MOV BYTE PTR DS:[ECX+EDI],AL
 *  00613903   41               INC ECX
 *  00613904   46               INC ESI
 *  00613905  ^EB EB            JMP SHORT .006138F2
 *  00613907   5A               POP EDX
 *  00613908   8B0439           MOV EAX,DWORD PTR DS:[ECX+EDI]
 *  0061390B   890419           MOV DWORD PTR DS:[ECX+EBX],EAX
 *  0061390E   31C0             XOR EAX,EAX
 *  00613910   F7D0             NOT EAX
 *  00613912   890439           MOV DWORD PTR DS:[ECX+EDI],EAX
 *  00613915   83C1 04          ADD ECX,0x4
 *  00613918   894A 18          MOV DWORD PTR DS:[EDX+0x18],ECX
 *  0061391B   8B7A 0C          MOV EDI,DWORD PTR DS:[EDX+0xC]
 *  0061391E   8B42 10          MOV EAX,DWORD PTR DS:[EDX+0x10]
 *  00613921   31C9             XOR ECX,ECX
 *  00613923   BB 6E000000      MOV EBX,0x6E
 *  00613928   891F             MOV DWORD PTR DS:[EDI],EBX
 *  0061392A   894F 04          MOV DWORD PTR DS:[EDI+0x4],ECX
 *  0061392D   894F 08          MOV DWORD PTR DS:[EDI+0x8],ECX
 *  00613930   C747 0C 02000000 MOV DWORD PTR DS:[EDI+0xC],0x2
 *  00613937   83C3 04          ADD EBX,0x4
 *  0061393A   895F 14          MOV DWORD PTR DS:[EDI+0x14],EBX
 *  0061393D   894F 18          MOV DWORD PTR DS:[EDI+0x18],ECX
 *  00613940   894F 1C          MOV DWORD PTR DS:[EDI+0x1C],ECX
 *  00613943   89EC             MOV ESP,EBP
 *  00613945   61               POPAD
 *  00613946   C3               RETN
 *  00613947   60               PUSHAD
 *  00613948   89E5             MOV EBP,ESP
 *  0061394A   83EC 18          SUB ESP,0x18
 *  0061394D   E8 59000000      CALL .006139AB
 *  00613952   8B5D F8          MOV EBX,DWORD PTR SS:[EBP-0x8]
 *  00613955   833B 01          CMP DWORD PTR DS:[EBX],0x1
 *  00613958   75 2E            JNZ SHORT .00613988
 *  0061395A   31C9             XOR ECX,ECX
 *  0061395C   890B             MOV DWORD PTR DS:[EBX],ECX
 *  0061395E   8B7B 0C          MOV EDI,DWORD PTR DS:[EBX+0xC]
 *  00613961   8B75 EC          MOV ESI,DWORD PTR SS:[EBP-0x14]
 *  00613964   8D49 08          LEA ECX,DWORD PTR DS:[ECX+0x8]
 *  00613967   F3:A5            REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS>
 *  00613969   8B7B 14          MOV EDI,DWORD PTR DS:[EBX+0x14]
 *  0061396C   8B75 E8          MOV ESI,DWORD PTR SS:[EBP-0x18]
 *  0061396F   8B4B 18          MOV ECX,DWORD PTR DS:[EBX+0x18]
 *  00613972   F3:A4            REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[>
 *  00613974   8B43 04          MOV EAX,DWORD PTR DS:[EBX+0x4]
 *  00613977   8B53 08          MOV EDX,DWORD PTR DS:[EBX+0x8]
 *  0061397A   8910             MOV DWORD PTR DS:[EAX],EDX
 *  0061397C   8D7B 04          LEA EDI,DWORD PTR DS:[EBX+0x4]
 *  0061397F   31C0             XOR EAX,EAX
 *  00613981   B9 40010000      MOV ECX,0x140
 *  00613986   F3:AB            REP STOS DWORD PTR ES:[EDI]
 *  00613988   89EC             MOV ESP,EBP
 *  0061398A   61               POPAD
 *  0061398B   C3               RETN
 *  0061398C   8B8CD6 A8D70500  MOV ECX,DWORD PTR DS:[ESI+EDX*8+0x5D7A8]    ; jichi: #2 hook jumped here, execute the original instruction first
 *  00613993   8B01             MOV EAX,DWORD PTR DS:[ECX]                  ; get dword split in ecx
 *  00613995   3D 96010000      CMP EAX,0x196
 *  0061399A   74 07            JE SHORT .006139A3                          ; translate if split is 0x196 or 0x6e
 *  0061399C   83F8 6E          CMP EAX,0x6E
 *  0061399F   74 02            JE SHORT .006139A3
 *  006139A1   EB 07            JMP SHORT .006139AA
 *  006139A3   E8 7AFEFFFF      CALL .00613822
 *  006139A8   8B01             MOV EAX,DWORD PTR DS:[ECX]
 *  006139AA   C3               RETN
 *  006139AB   60               PUSHAD
 *  006139AC   C745 FC A8D70500 MOV DWORD PTR SS:[EBP-0x4],0x5D7A8
 *  006139B3   EB 03            JMP SHORT .006139B8
 *  006139B5   58               POP EAX
 *  006139B6   EB 05            JMP SHORT .006139BD
 *  006139B8   E8 F8FFFFFF      CALL .006139B5
 *  006139BD   2D BD392100      SUB EAX,0x2139BD
 *  006139C2   0380 D4020000    ADD EAX,DWORD PTR DS:[EAX+0x2D4]
 *  006139C8   B9 00010000      MOV ECX,0x100
 *  006139CD   8D80 00400100    LEA EAX,DWORD PTR DS:[EAX+0x14000]
 *  006139D3   8945 F8          MOV DWORD PTR SS:[EBP-0x8],EAX
 *  006139D6   8D0401           LEA EAX,DWORD PTR DS:[ECX+EAX]
 *  006139D9   8945 F4          MOV DWORD PTR SS:[EBP-0xC],EAX
 *  006139DC   8D0401           LEA EAX,DWORD PTR DS:[ECX+EAX]
 *  006139DF   8945 F0          MOV DWORD PTR SS:[EBP-0x10],EAX
 *  006139E2   8D0401           LEA EAX,DWORD PTR DS:[ECX+EAX]
 *  006139E5   8945 EC          MOV DWORD PTR SS:[EBP-0x14],EAX
 *  006139E8   8D0401           LEA EAX,DWORD PTR DS:[ECX+EAX]
 *  006139EB   8945 E8          MOV DWORD PTR SS:[EBP-0x18],EAX
 *  006139EE   61               POPAD
 *  006139EF   C3               RETN
 *  006139F0   0000             ADD BYTE PTR DS:[EAX],AL
 *  006139F2   0000             ADD BYTE PTR DS:[EAX],AL
 *  006139F4   0000             ADD BYTE PTR DS:[EAX],AL
 */
bool InsertEushullyHook()
{
  ULONG startAddress, stopAddress;
  if (!NtInspect::getProcessMemoryRange(&startAddress, &stopAddress)) { // need accurate stopAddress
    ConsoleOutput("vnreng:Eushully: failed to get memory range");
    return false;
  }
  ULONG addr = MemDbg::findLastCallerAddressAfterInt3((DWORD)::GetTextExtentPoint32A, startAddress, stopAddress);
  //GROWL_DWORD(addr);
  if (!addr) {
    ConsoleOutput("vnreng:Eushully: failed");
    return false;
  }

  // BOOL GetTextExtentPoint32(
  //   _In_   HDC hdc,
  //   _In_   LPCTSTR lpString,
  //   _In_   int c,
  //   _Out_  LPSIZE lpSize
  // );
  enum stack { // current stack
    //retaddr = 0 // esp[0] is the return address since this is the beginning of the function
    arg1_hdc = 4 * 1 // 0x4
    , arg2_lpString = 4 * 2 // 0x8
    , arg3_lc = 4 * 3 // 0xc
    , arg4_lpSize = 4 * 4 // 0x10
  };

  HookParam hp = {};
  hp.address = addr;
  hp.type = USING_STRING|FIXING_SPLIT; // merging all threads
  hp.offset = arg2_lpString; // arg2 = 0x4 * 2
  ConsoleOutput("vnreng: INSERT Eushully");
  NewHook(hp, "ARCGameEngine");
  return true;
}

/** jichi 6/1/2014 AMUSE CRAFT
 *  Related brands: http://erogetrailers.com/brand/2047
 *  Sample game: 魔女こいにっ� *  See:  http://sakuradite.com/topic/223
 *  Sample H-code: /HBN-4*0:18@26159:MAJOKOI_try.exe (need remove context, though)
 *
 *  Sample games:
 *  - 時計仕掛け�レイライン
 *  - きみと僕との騎士の日� *
 *  /HBN-4*0:18@26159:MAJOKOI_TRY.EXE
 *  - addr: 155993
 *  - length_offset: 1
 *  - module: 104464j455
 *  - off: 4294967288 = 0xfffffff8
 *  - split: 24 = 0x18
 *  - type: 1112 = 0x458
 *
 *  Call graph:
 *  - hook reladdr:  0x26159, fun reladdr: 26150
 *  - scene fun reladdr: 0x26fd0
 *    - arg1 and arg3 are pointers
 *    - arg2 is the text
 *  - scenairo only reladdr: 0x26670
 *  Issue for implementing embeded engine: two functions are needed to be hijacked
 *
 *  013c614e     cc             int3
 *  013c614f     cc             int3
 *  013c6150  /$ 55             push ebp ; jichi: function starts, this function seems to process text encoding
 *  013c6151  |. 8bec           mov ebp,esp
 *  013c6153  |. 8b45 08        mov eax,dword ptr ss:[ebp+0x8]
 *  013c6156  |. 0fb608         movzx ecx,byte ptr ds:[eax]
 *  013c6159  |. 81f9 81000000  cmp ecx,0x81 ; jichi: hook here
 *  013c615f  |. 7c 0d          jl short majokoi_.013c616e
 *  013c6161  |. 8b55 08        mov edx,dword ptr ss:[ebp+0x8]
 *  013c6164  |. 0fb602         movzx eax,byte ptr ds:[edx]
 *  013c6167  |. 3d 9f000000    cmp eax,0x9f
 *  013c616c  |. 7e 1c          jle short majokoi_.013c618a
 *  013c616e  |> 8b4d 08        mov ecx,dword ptr ss:[ebp+0x8]
 *  013c6171  |. 0fb611         movzx edx,byte ptr ds:[ecx]
 *  013c6174  |. 81fa e0000000  cmp edx,0xe0
 *  013c617a  |. 7c 30          jl short majokoi_.013c61ac
 *  013c617c  |. 8b45 08        mov eax,dword ptr ss:[ebp+0x8]
 *  013c617f  |. 0fb608         movzx ecx,byte ptr ds:[eax]
 *  013c6182  |. 81f9 fc000000  cmp ecx,0xfc
 *  013c6188  |. 7f 22          jg short majokoi_.013c61ac
 *  013c618a  |> 8b55 08        mov edx,dword ptr ss:[ebp+0x8]
 *  013c618d  |. 0fb642 01      movzx eax,byte ptr ds:[edx+0x1]
 *  013c6191  |. 83f8 40        cmp eax,0x40
 *  013c6194  |. 7c 16          jl short majokoi_.013c61ac
 *  013c6196  |. 8b4d 08        mov ecx,dword ptr ss:[ebp+0x8]
 *  013c6199  |. 0fb651 01      movzx edx,byte ptr ds:[ecx+0x1]
 *  013c619d  |. 81fa fc000000  cmp edx,0xfc
 *  013c61a3  |. 7f 07          jg short majokoi_.013c61ac
 *  013c61a5  |. b8 01000000    mov eax,0x1
 *  013c61aa  |. eb 02          jmp short majokoi_.013c61ae
 *  013c61ac  |> 33c0           xor eax,eax
 *  013c61ae  |> 5d             pop ebp
 *  013c61af  \. c3             retn
 */
static bool InsertOldPalHook() // this is used in case the new pattern does not work
{
  const BYTE bytes[] = {
    0x55,                 // 013c6150  /$ 55             push ebp ; jichi: function starts
    0x8b,0xec,            // 013c6151  |. 8bec           mov ebp,esp
    0x8b,0x45, 0x08,      // 013c6153  |. 8b45 08        mov eax,dword ptr ss:[ebp+0x8]
    0x0f,0xb6,0x08,       // 013c6156  |. 0fb608         movzx ecx,byte ptr ds:[eax]
    0x81,0xf9 //81000000  // 013c6159  |. 81f9 81000000  cmp ecx,0x81 ; jichi: hook here
  };
  enum { addr_offset = sizeof(bytes) - 2 };
  ULONG range = min(module_limit_ - module_base_, MAX_REL_ADDR);
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_base_ + range);
  //GROWL_DWORD(reladdr); // supposed to be 0x21650
  //GROWL_DWORD(reladdr  + addr_offset);
  //reladdr = 0x26159; // 魔女こいにっ�trial
  if (!addr) {
    ConsoleOutput("vnreng:AMUSE CRAFT: pattern not found");
    return false;
  }

  HookParam hp = {};
  hp.address = addr + addr_offset;
  //hp.type = NO_CONTEXT|USING_SPLIT|DATA_INDIRECT; // 0x418
  //hp.type = NO_CONTEXT|USING_SPLIT|DATA_INDIRECT|RELATIVE_SPLIT;  // Use relative address to prevent floating issue
  hp.type = NO_CONTEXT|USING_SPLIT|DATA_INDIRECT;
  hp.offset = -0x8; // eax
  //hp.split = 0x18;
  //hp.split = 0x4; // This is supposed to be the return address
  hp.split = 0x20; // arg6
  hp.length_offset = 1;
  ConsoleOutput("vnreng: INSERT AMUSE CRAFT");
  NewHook(hp, "Pal");
  return true;
}
static bool InsertNewPal1Hook()
{
  const BYTE bytes[] = {
    0x55,               // 002c6ab0   55               push ebp
    0x8b,0xec,          // 002c6ab1   8bec             mov ebp,esp
    0x83,0xec, 0x78,    // 002c6ab3   83ec 78          sub esp,0x78
    0xa1, XX4,          // 002c6ab6   a1 8c002f00      mov eax,dword ptr ds:[0x2f008c]
    0x33,0xc5,          // 002c6abb   33c5             xor eax,ebp
    0x89,0x45, 0xf8     // 002c6abd   8945 f8          mov dword ptr ss:[ebp-0x8],eax ; mireado : small update
  };
  ULONG range = min(module_limit_ - module_base_, MAX_REL_ADDR);
  ULONG addr = MemDbg::matchBytes(bytes, sizeof(bytes), module_base_, module_base_ + range);
  if (!addr) {
    ConsoleOutput("vnreng:Pal1: pattern not found");
    return false;
  }

  HookParam hp = {};
  hp.address = addr;
  //hp.type = NO_CONTEXT|USING_SPLIT|DATA_INDIRECT; // 0x418
  hp.type = RELATIVE_SPLIT;  // Use relative address to prevent floating issue
  hp.offset = 4 * 2; // arg2
  ConsoleOutput("vnreng: INSERT Pal1");
  NewHook(hp, "Pal");
  return true;
}
// Eguni 2016/11/06
// Supporting new Pal engine, tested with 恋×シンアイ彼女
static bool InsertNewPal2Hook()
{
  const BYTE bytes[] = {
    0x55,               // 0124E220   55               push ebp; doesn't works... why?
    0x8b,0xec,          // 0124E221   8bec             mov ebp,esp
    0x83,0xec, 0x7c,    // 0124E223   83ec 7c          sub esp,0x7C
    0xa1, XX4,          // 0124E226   a1 788D2901      mov eax,dword ptr ds:[0x2f008c]
    0x33,0xc5,          // 0124E22B   33c5             xor eax,ebp
    0x89,0x45, 0xfc,     // 0124E22D   8945 FC          mov dword ptr ss:[ebp-0x8],eax ; mireado : small update
	0xe8				// 0136e230   e8			   call 01377800
  };
  ULONG range = min(module_limit_ - module_base_, MAX_REL_ADDR);
  ULONG addr = MemDbg::matchBytes(bytes, sizeof(bytes), module_base_, module_base_ + range);
  if (!addr) {
    ConsoleOutput("vnreng:Pal2: pattern not found");
    return false;
  }

  HookParam hp = {};
  hp.address = addr;
  //hp.type = NO_CONTEXT|USING_SPLIT|DATA_INDIRECT; // 0x418
  hp.type = RELATIVE_SPLIT;  // Use relative address to prevent floating issue
  hp.offset = 4 * 2; // arg2
  ConsoleOutput("vnreng: INSERT Pal2");
  NewHook(hp, "Pal");
  return true;
}
bool InsertPalHook() // use Old Pal first, which does not have ruby
{ return InsertOldPalHook() || InsertNewPal1Hook() || InsertNewPal2Hook(); }

/** jichi 7/6/2014 NeXAS
 *  Sample game: BALDRSKYZERO EXTREME
 *
 *  Call graph:
 *  - GetGlyphOutlineA x 2 functions
 *  - Caller 503620: char = [arg1 + 0x1a8]
 *  - Caller: 500039, 4ffff0
 *    edi = [esi+0x1a0] # stack size 4x3
 *    arg1 = eax = [edi]
 *
 *  0050361f     cc             int3
 *  00503620  /$ 55             push ebp
 *  00503621  |. 8bec           mov ebp,esp
 *  00503623  |. 83e4 f8        and esp,0xfffffff8
 *  00503626  |. 64:a1 00000000 mov eax,dword ptr fs:[0]
 *  0050362c  |. 6a ff          push -0x1
 *  0050362e  |. 68 15815900    push bszex.00598115
 *  00503633  |. 50             push eax
 *  00503634  |. 64:8925 000000>mov dword ptr fs:[0],esp
 *  0050363b  |. 81ec 78010000  sub esp,0x178
 *  00503641  |. 53             push ebx
 *  00503642  |. 8b5d 08        mov ebx,dword ptr ss:[ebp+0x8]
 *  00503645  |. 80bb ed010000 >cmp byte ptr ds:[ebx+0x1ed],0x0
 *  0050364c  |. 56             push esi
 *  0050364d  |. 57             push edi
 *  0050364e  |. 0f85 6e0b0000  jnz bszex.005041c2
 *  00503654  |. 8db3 a8010000  lea esi,dword ptr ds:[ebx+0x1a8]
 *  0050365a  |. c683 ed010000 >mov byte ptr ds:[ebx+0x1ed],0x1
 *  00503661  |. 837e 14 10     cmp dword ptr ds:[esi+0x14],0x10
 *  00503665  |. 72 04          jb short bszex.0050366b
 *  00503667  |. 8b06           mov eax,dword ptr ds:[esi]
 *  00503669  |. eb 02          jmp short bszex.0050366d
 *  0050366b  |> 8bc6           mov eax,esi
 *  0050366d  |> 8038 20        cmp byte ptr ds:[eax],0x20
 *  00503670  |. 0f84 ef0a0000  je bszex.00504165
 *  00503676  |. b9 fcc97400    mov ecx,bszex.0074c9fc
 *  0050367b  |. 8bfe           mov edi,esi
 *  0050367d  |. e8 2e20f1ff    call bszex.004156b0
 *  00503682  |. 84c0           test al,al
 *  00503684  |. 0f85 db0a0000  jnz bszex.00504165
 *  0050368a  |. 8b93 38010000  mov edx,dword ptr ds:[ebx+0x138]
 *  00503690  |. 33c0           xor eax,eax
 *  00503692  |. 3bd0           cmp edx,eax
 *  00503694  |. 0f84 8d0a0000  je bszex.00504127
 *  0050369a  |. 8b8b 3c010000  mov ecx,dword ptr ds:[ebx+0x13c]
 *  005036a0  |. 3bc8           cmp ecx,eax
 *  005036a2  |. 0f84 7f0a0000  je bszex.00504127
 *  005036a8  |. 894424 40      mov dword ptr ss:[esp+0x40],eax
 *  005036ac  |. 894424 44      mov dword ptr ss:[esp+0x44],eax
 *  005036b0  |. 894424 48      mov dword ptr ss:[esp+0x48],eax
 *  005036b4  |. 898424 8c01000>mov dword ptr ss:[esp+0x18c],eax
 *  005036bb  |. 33ff           xor edi,edi
 *  005036bd  |. 66:897c24 60   mov word ptr ss:[esp+0x60],di
 *  005036c2  |. bf 01000000    mov edi,0x1
 *  005036c7  |. 66:897c24 62   mov word ptr ss:[esp+0x62],di
 *  005036cc  |. 33ff           xor edi,edi
 *  005036ce  |. 66:897c24 64   mov word ptr ss:[esp+0x64],di
 *  005036d3  |. 66:897c24 66   mov word ptr ss:[esp+0x66],di
 *  005036d8  |. 66:897c24 68   mov word ptr ss:[esp+0x68],di
 *  005036dd  |. 66:897c24 6a   mov word ptr ss:[esp+0x6a],di
 *  005036e2  |. 66:897c24 6c   mov word ptr ss:[esp+0x6c],di
 *  005036e7  |. bf 01000000    mov edi,0x1
 *  005036ec  |. 66:897c24 6e   mov word ptr ss:[esp+0x6e],di
 *  005036f1  |. 894424 0c      mov dword ptr ss:[esp+0xc],eax
 *  005036f5  |. 894424 10      mov dword ptr ss:[esp+0x10],eax
 *  005036f9  |. 3883 ec010000  cmp byte ptr ds:[ebx+0x1ec],al
 *  005036ff  |. 0f84 39010000  je bszex.0050383e
 *  00503705  |. c78424 f000000>mov dword ptr ss:[esp+0xf0],bszex.00780e>
 *  00503710  |. 898424 3001000>mov dword ptr ss:[esp+0x130],eax
 *  00503717  |. 898424 1001000>mov dword ptr ss:[esp+0x110],eax
 *  0050371e  |. 898424 1401000>mov dword ptr ss:[esp+0x114],eax
 *  00503725  |. c68424 8c01000>mov byte ptr ss:[esp+0x18c],0x1
 *  0050372d  |. 837e 14 10     cmp dword ptr ds:[esi+0x14],0x10
 *  00503731  |. 72 02          jb short bszex.00503735
 *  00503733  |. 8b36           mov esi,dword ptr ds:[esi]
 *  00503735  |> 51             push ecx
 *  00503736  |. 52             push edx
 *  00503737  |. 56             push esi
 *  00503738  |. 8d8424 ec00000>lea eax,dword ptr ss:[esp+0xec]
 *  0050373f  |. 68 00ca7400    push bszex.0074ca00                      ;  ascii "gaiji%s%02d%02d.fil"
 *  00503744  |. 50             push eax
 *  00503745  |. e8 cec6f7ff    call bszex.0047fe18
 *  0050374a  |. 83c4 14        add esp,0x14
 *  0050374d  |. 8d8c24 e000000>lea ecx,dword ptr ss:[esp+0xe0]
 *  00503754  |. 51             push ecx                                 ; /arg1
 *  00503755  |. 8d8c24 9400000>lea ecx,dword ptr ss:[esp+0x94]          ; |
 *  0050375c  |. e8 dfeaefff    call bszex.00402240                      ; \bszex.00402240
 *  00503761  |. 6a 00          push 0x0                                 ; /arg4 = 00000000
 *  00503763  |. 8d9424 9400000>lea edx,dword ptr ss:[esp+0x94]          ; |
 *  0050376a  |. c68424 9001000>mov byte ptr ss:[esp+0x190],0x2          ; |
 *  00503772  |. a1 a8a78200    mov eax,dword ptr ds:[0x82a7a8]          ; |
 *  00503777  |. 52             push edx                                 ; |arg3
 *  00503778  |. 50             push eax                                 ; |arg2 => 00000000
 *  00503779  |. 8d8c24 fc00000>lea ecx,dword ptr ss:[esp+0xfc]          ; |
 *  00503780  |. 51             push ecx                                 ; |arg1
 *  00503781  |. e8 2a0dfeff    call bszex.004e44b0                      ; \bszex.004e44b0
 *  00503786  |. 84c0           test al,al
 *  00503788  |. 8d8c24 9000000>lea ecx,dword ptr ss:[esp+0x90]
 *  0050378f  |. 0f95c3         setne bl
 *  00503792  |. c68424 8c01000>mov byte ptr ss:[esp+0x18c],0x1
 *  0050379a  |. e8 a1baf1ff    call bszex.0041f240
 *  0050379f  |. 84db           test bl,bl
 *  005037a1  |. 74 40          je short bszex.005037e3
 *  005037a3  |. 8db424 f000000>lea esi,dword ptr ss:[esp+0xf0]
 *  005037aa  |. e8 6106feff    call bszex.004e3e10
 *  005037af  |. 8bd8           mov ebx,eax
 *  005037b1  |. 895c24 0c      mov dword ptr ss:[esp+0xc],ebx
 *  005037b5  |. e8 5606feff    call bszex.004e3e10
 *  005037ba  |. 8bf8           mov edi,eax
 *  005037bc  |. 0faffb         imul edi,ebx
 *  005037bf  |. 894424 10      mov dword ptr ss:[esp+0x10],eax
 *  005037c3  |. 8bc7           mov eax,edi
 *  005037c5  |. 8d7424 40      lea esi,dword ptr ss:[esp+0x40]
 *  005037c9  |. e8 e219f1ff    call bszex.004151b0
 *  005037ce  |. 8b5424 40      mov edx,dword ptr ss:[esp+0x40]
 *  005037d2  |. 52             push edx                                 ; /arg1
 *  005037d3  |. 8bc7           mov eax,edi                              ; |
 *  005037d5  |. 8db424 f400000>lea esi,dword ptr ss:[esp+0xf4]          ; |
 *  005037dc  |. e8 8f03feff    call bszex.004e3b70                      ; \bszex.004e3b70
 *  005037e1  |. eb 10          jmp short bszex.005037f3
 *  005037e3  |> 8d8424 e000000>lea eax,dword ptr ss:[esp+0xe0]
 *  005037ea  |. 50             push eax
 *  005037eb  |. e8 60c5f2ff    call bszex.0042fd50
 *  005037f0  |. 83c4 04        add esp,0x4
 *  005037f3  |> 8b5c24 10      mov ebx,dword ptr ss:[esp+0x10]
 *  005037f7  |. 8b7c24 40      mov edi,dword ptr ss:[esp+0x40]
 *  005037fb  |. 8bcb           mov ecx,ebx
 *  005037fd  |. 0faf4c24 0c    imul ecx,dword ptr ss:[esp+0xc]
 *  00503802  |. 33c0           xor eax,eax
 *  00503804  |. 85c9           test ecx,ecx
 *  00503806  |. 7e 09          jle short bszex.00503811
 *  00503808  |> c02c07 02      /shr byte ptr ds:[edi+eax],0x2
 *  0050380c  |. 40             |inc eax
 *  0050380d  |. 3bc1           |cmp eax,ecx
 *  0050380f  |.^7c f7          \jl short bszex.00503808
 *  00503811  |> 8b4d 08        mov ecx,dword ptr ss:[ebp+0x8]
 *  00503814  |. 33c0           xor eax,eax
 *  00503816  |. 8db424 f000000>lea esi,dword ptr ss:[esp+0xf0]
 *  0050381d  |. 8981 dc010000  mov dword ptr ds:[ecx+0x1dc],eax
 *  00503823  |. 8981 e0010000  mov dword ptr ds:[ecx+0x1e0],eax
 *  00503829  |. c78424 f000000>mov dword ptr ss:[esp+0xf0],bszex.00780e>
 *  00503834  |. e8 4702feff    call bszex.004e3a80
 *  00503839  |. e9 68010000    jmp bszex.005039a6
 *  0050383e  |> 8b0d 08a58200  mov ecx,dword ptr ds:[0x82a508]
 *  00503844  |. 51             push ecx                                 ; /hwnd => null
 *  00503845  |. ff15 d4e26f00  call dword ptr ds:[<&user32.getdc>]      ; \getdc
 *  0050384b  |. 68 50b08200    push bszex.0082b050                      ; /facename = ""
 *  00503850  |. 6a 00          push 0x0                                 ; |pitchandfamily = default_pitch|ff_dontcare
 *  00503852  |. 6a 02          push 0x2                                 ; |quality = proof_quality
 *  00503854  |. 6a 00          push 0x0                                 ; |clipprecision = clip_default_precis
 *  00503856  |. 6a 07          push 0x7                                 ; |outputprecision = out_tt_only_precis
 *  00503858  |. 68 80000000    push 0x80                                ; |charset = 128.
 *  0050385d  |. 6a 00          push 0x0                                 ; |strikeout = false
 *  0050385f  |. 6a 00          push 0x0                                 ; |underline = false
 *  00503861  |. 8bf8           mov edi,eax                              ; |
 *  00503863  |. 8b83 38010000  mov eax,dword ptr ds:[ebx+0x138]         ; |
 *  00503869  |. 6a 00          push 0x0                                 ; |italic = false
 *  0050386b  |. 68 84030000    push 0x384                               ; |weight = fw_heavy
 *  00503870  |. 99             cdq                                      ; |
 *  00503871  |. 6a 00          push 0x0                                 ; |orientation = 0x0
 *  00503873  |. 2bc2           sub eax,edx                              ; |
 *  00503875  |. 8b93 3c010000  mov edx,dword ptr ds:[ebx+0x13c]         ; |
 *  0050387b  |. 6a 00          push 0x0                                 ; |escapement = 0x0
 *  0050387d  |. d1f8           sar eax,1                                ; |
 *  0050387f  |. 50             push eax                                 ; |width
 *  00503880  |. 52             push edx                                 ; |height
 *  00503881  |. ff15 48e06f00  call dword ptr ds:[<&gdi32.createfonta>] ; \createfonta
 *  00503887  |. 50             push eax                                 ; /hobject
 *  00503888  |. 57             push edi                                 ; |hdc
 *  00503889  |. 894424 30      mov dword ptr ss:[esp+0x30],eax          ; |
 *  0050388d  |. ff15 4ce06f00  call dword ptr ds:[<&gdi32.selectobject>>; \selectobject
 *  00503893  |. 894424 1c      mov dword ptr ss:[esp+0x1c],eax
 *  00503897  |. 8d8424 4801000>lea eax,dword ptr ss:[esp+0x148]
 *  0050389e  |. 50             push eax                                 ; /ptextmetric
 *  0050389f  |. 57             push edi                                 ; |hdc
 *  005038a0  |. ff15 50e06f00  call dword ptr ds:[<&gdi32.gettextmetric>; \gettextmetricsa
 *  005038a6  |. 837e 14 10     cmp dword ptr ds:[esi+0x14],0x10
 *  005038aa  |. 72 02          jb short bszex.005038ae
 *  005038ac  |. 8b36           mov esi,dword ptr ds:[esi]
 *  005038ae  |> 56             push esi                                 ; /arg1
 *  005038af  |. e8 deccf7ff    call bszex.00480592                      ; \bszex.00480592
 *  005038b4  |. 83c4 04        add esp,0x4
 *  005038b7  |. 8d4c24 60      lea ecx,dword ptr ss:[esp+0x60]
 *  005038bb  |. 51             push ecx                                 ; /pmat2
 *  005038bc  |. 6a 00          push 0x0                                 ; |buffer = null
 *  005038be  |. 6a 00          push 0x0                                 ; |bufsize = 0x0
 *  005038c0  |. 8d9424 d800000>lea edx,dword ptr ss:[esp+0xd8]          ; |
 *  005038c7  |. 52             push edx                                 ; |pmetrics
 *  005038c8  |. 6a 06          push 0x6                                 ; |format = ggo_gray8_bitmap
 *  005038ca  |. 50             push eax                                 ; |char
 *  005038cb  |. 57             push edi                                 ; |hdc
 *  005038cc  |. 894424 30      mov dword ptr ss:[esp+0x30],eax          ; |
 *  005038d0  |. ff15 54e06f00  call dword ptr ds:[<&gdi32.getglyphoutli>; \getglyphoutlinea
 *  005038d6  |. 8bd8           mov ebx,eax
 *  005038d8  |. 85db           test ebx,ebx
 *  005038da  |. 0f84 d5070000  je bszex.005040b5
 *  005038e0  |. 83fb ff        cmp ebx,-0x1
 *  005038e3  |. 0f84 cc070000  je bszex.005040b5
 *  005038e9  |. 8d7424 40      lea esi,dword ptr ss:[esp+0x40]
 *  005038ed  |. e8 be18f1ff    call bszex.004151b0
 *  005038f2  |. 8b4c24 40      mov ecx,dword ptr ss:[esp+0x40]
 *  005038f6  |. 8d4424 60      lea eax,dword ptr ss:[esp+0x60]
 *  005038fa  |. 50             push eax                                 ; /pmat2
 *  005038fb  |. 8b4424 18      mov eax,dword ptr ss:[esp+0x18]          ; |
 *  005038ff  |. 51             push ecx                                 ; |buffer
 *  00503900  |. 53             push ebx                                 ; |bufsize
 *  00503901  |. 8d9424 d800000>lea edx,dword ptr ss:[esp+0xd8]          ; |
 *  00503908  |. 52             push edx                                 ; |pmetrics
 *  00503909  |. 6a 06          push 0x6                                 ; |format = ggo_gray8_bitmap
 *  0050390b  |. 50             push eax                                 ; |char
 *  0050390c  |. 57             push edi                                 ; |hdc
 *  0050390d  |. ff15 54e06f00  call dword ptr ds:[<&gdi32.getglyphoutli>; \getglyphoutlinea
 *  00503913  |. 8b4c24 1c      mov ecx,dword ptr ss:[esp+0x1c]
 *  00503917  |. 51             push ecx                                 ; /hobject
 *  00503918  |. 57             push edi                                 ; |hdc
 *  00503919  |. ff15 4ce06f00  call dword ptr ds:[<&gdi32.selectobject>>; \selectobject
 *  0050391f  |. 8b15 08a58200  mov edx,dword ptr ds:[0x82a508]
 *  00503925  |. 57             push edi                                 ; /hdc
 *  00503926  |. 52             push edx                                 ; |hwnd => null
 *  00503927  |. ff15 a4e26f00  call dword ptr ds:[<&user32.releasedc>]  ; \releasedc
 *  0050392d  |. 8b4424 28      mov eax,dword ptr ss:[esp+0x28]
 *  00503931  |. 50             push eax                                 ; /hobject
 *  00503932  |. ff15 58e06f00  call dword ptr ds:[<&gdi32.deleteobject>>; \deleteobject
 *  00503938  |. 8bb424 cc00000>mov esi,dword ptr ss:[esp+0xcc]
 *  0050393f  |. 8b8c24 d000000>mov ecx,dword ptr ss:[esp+0xd0]
 *  00503946  |. 83c6 03        add esi,0x3
 *  00503949  |. 81e6 fcff0000  and esi,0xfffc
 *  0050394f  |. 8bd1           mov edx,ecx
 *  00503951  |. 0fafd6         imul edx,esi
 *  00503954  |. 897424 0c      mov dword ptr ss:[esp+0xc],esi
 *  00503958  |. 894c24 10      mov dword ptr ss:[esp+0x10],ecx
 *  0050395c  |. 3bda           cmp ebx,edx
 *  0050395e  |. 74 1a          je short bszex.0050397a
 */
bool InsertNeXASHook()
{
  // There are two GetGlyphOutlineA, both of which seem to have the same texts
  ULONG addr = MemDbg::findCallAddress((ULONG)::GetGlyphOutlineA, module_base_, module_limit_);
  if (!addr) {
    ConsoleOutput("vnreng:NexAS: failed");
    return false;
  }

  // DWORD GetGlyphOutline(
  //   _In_   HDC hdc,
  //   _In_   UINT uChar,
  //   _In_   UINT uFormat,
  //   _Out_  LPGLYPHMETRICS lpgm,
  //   _In_   DWORD cbBuffer,
  //   _Out_  LPVOID lpvBuffer,
  //   _In_   const MAT2 *lpmat2
  // );
  enum stack { // current stack
    arg1_hdc = 4 * 0 // starting from 0 before function call
    , arg2_uChar = 4 * 1
    , arg3_uFormat = 4 * 2
    , arg4_lpgm = 4 * 3
    , arg5_cbBuffer = 4 * 4
    , arg6_lpvBuffer = 4 * 5
    , arg7_lpmat2 = 4 * 6
  };

  HookParam hp = {};
  //hp.address = (DWORD)::GetGlyphOutlineA;
  hp.address = addr;
  //hp.type = USING_STRING|USING_SPLIT;
  hp.type = BIG_ENDIAN|NO_CONTEXT|USING_SPLIT;
  hp.length_offset = 1; // determine string length at runtime
  hp.offset = arg2_uChar; // = 0x4, arg2 before the function call, so it is: 0x4 * (2-1) = 4

  // Either lpgm or lpmat2 are good choices
  hp.split = arg4_lpgm; // = 0xc, arg4
  //hp.split = arg7_lpmat2; // = 0x18, arg7

  ConsoleOutput("vnreng: INSERT NeXAS");
  NewHook(hp, "NeXAS");
  return true;
}

/** jichi 7/6/2014 YukaSystem2
 *  Sample game: セミラミスの天秤
 *
 *  Observations from Debug:
 *  - Ollydbg got UTF8 text memory address
 *  - Hardware break points have loops on 0x4010ED
 *  - The hooked function seems to take 3 parameters, and arg3 is the right text
 *  - The text appears character by character
 *
 *  Runtime stack:
 *  - return address
 *  - arg1 pointer's pointer
 *  - arg2 text
 *  - arg3 pointer's pointer
 *  - code address or -1, maybe a handle
 *  - unknown pointer
 *  - return address
 *  - usually zero
 *
 *  0040109d     cc             int3
 *  0040109e     cc             int3
 *  0040109f     cc             int3
 *  004010a0  /$ 55             push ebp
 *  004010a1  |. 8bec           mov ebp,esp
 *  004010a3  |. 8b45 14        mov eax,dword ptr ss:[ebp+0x14]
 *  004010a6  |. 50             push eax                                 ; /arg4
 *  004010a7  |. 8b4d 10        mov ecx,dword ptr ss:[ebp+0x10]          ; |
 *  004010aa  |. 51             push ecx                                 ; |arg3
 *  004010ab  |. 8b55 0c        mov edx,dword ptr ss:[ebp+0xc]           ; |
 *  004010ae  |. 52             push edx                                 ; |arg2
 *  004010af  |. 8b45 08        mov eax,dword ptr ss:[ebp+0x8]           ; |
 *  004010b2  |. 50             push eax                                 ; |arg1
 *  004010b3  |. e8 48ffffff    call semirami.00401000                   ; \semirami.00401000
 *  004010b8  |. 83c4 10        add esp,0x10
 *  004010bb  |. 8b45 08        mov eax,dword ptr ss:[ebp+0x8]
 *  004010be  |. 5d             pop ebp
 *  004010bf  \. c3             retn
 *  004010c0  /$ 55             push ebp
 *  004010c1  |. 8bec           mov ebp,esp
 *  004010c3  |. 8b45 14        mov eax,dword ptr ss:[ebp+0x14]
 *  004010c6  |. 50             push eax                                 ; /arg4
 *  004010c7  |. 8b4d 10        mov ecx,dword ptr ss:[ebp+0x10]          ; |
 *  004010ca  |. 51             push ecx                                 ; |arg3
 *  004010cb  |. 8b55 0c        mov edx,dword ptr ss:[ebp+0xc]           ; |
 *  004010ce  |. 52             push edx                                 ; |arg2
 *  004010cf  |. 8b45 08        mov eax,dword ptr ss:[ebp+0x8]           ; |
 *  004010d2  |. 50             push eax                                 ; |arg1
 *  004010d3  |. e8 58ffffff    call semirami.00401030                   ; \semirami.00401030
 *  004010d8  |. 83c4 10        add esp,0x10
 *  004010db  |. 8b45 08        mov eax,dword ptr ss:[ebp+0x8]
 *  004010de  |. 5d             pop ebp
 *  004010df  \. c3             retn
 *  004010e0  /$ 55             push ebp ; jichi: function begin, hook here, bp-based frame, arg2 is the text
 *  004010e1  |. 8bec           mov ebp,esp
 *  004010e3  |. 8b45 08        mov eax,dword ptr ss:[ebp+0x8] ; jichi: ebp+0x8 = arg2
 *  004010e6  |. 8b4d 0c        mov ecx,dword ptr ss:[ebp+0xc] ; jichi: arg3 is also a pointer of pointer
 *  004010e9  |. 8a11           mov dl,byte ptr ds:[ecx]
 *  004010eb  |. 8810           mov byte ptr ds:[eax],dl    ; jichi: eax is the data
 *  004010ed  |. 5d             pop ebp
 *  004010ee  \. c3             retn
 *  004010ef     cc             int3
 */

// Ignore image and music file names
// Sample text: "Voice\tou00012.ogg""運命論って云うのかなあ……神さまを信じてる人が多かったからだろうね、何があっても、それ�神さまが�刁�ちに与えられた試練なんだって、そ぀�ってたみたい。勿論、今でもそ぀��てあ�人はぁ�ぱぁ�るん�けど�
// Though the input string is UTF-8, it should be ASCII compatible.
static bool _yk2garbage(const char *p)
{
  //Q_ASSERT(p);
  while (char ch = *p++) {
    if (!(
        ch >= '0' && ch <= '9' ||
        ch >= 'A' && ch <= 'z' || // also ignore ASCII 91-96: [ \ ] ^ _ `
        ch == '"' || ch == '.' || ch == '-' || ch == '#'
      ))
    return false;
  }
  return true;
}

// Get text from arg2
static void SpecialHookYukaSystem2(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  DWORD arg2 = argof(2, esp_base), // [esp+0x8]
        arg3 = argof(3, esp_base); // [esp+0xc]
        //arg4 = argof(4, esp_base); // there is no arg4. arg4 is properlly a function pointer
  LPCSTR text = (LPCSTR)arg2;
  if (*text && !_yk2garbage(text)) { // I am sure this could be null
    *data = (DWORD)text;
    *len = ::strlen(text); // UTF-8 is null-terminated
    if (arg3)
      *split = *(DWORD *)arg3;
  }
}

bool InsertYukaSystem2Hook()
{
  const BYTE bytes[] = {
    0x55,            // 004010e0  /$ 55             push ebp ; jichi; hook here
    0x8b,0xec,       // 004010e1  |. 8bec           mov ebp,esp
    0x8b,0x45, 0x08, // 004010e3  |. 8b45 08        mov eax,dword ptr ss:[ebp+0x8] ; jichi: ebp+0x8 = arg2
    0x8b,0x4d, 0x0c, // 004010e6  |. 8b4d 0c        mov ecx,dword ptr ss:[ebp+0xc]
    0x8a,0x11,       // 004010e9  |. 8a11           mov dl,byte ptr ds:[ecx]
    0x88,0x10,       // 004010eb  |. 8810           mov byte ptr ds:[eax],dl    ; jichi: eax is the address to text
    0x5d,            // 004010ed  |. 5d             pop ebp
    0xc3             // 004010ee  \. c3             retn
  };
  //enum { addr_offset = 0 };
  ULONG range = min(module_limit_ - module_base_, MAX_REL_ADDR);
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_base_ + range);
  //GROWL_DWORD(addr); // supposed to be 0x4010e0
  if (!addr) {
    ConsoleOutput("vnreng:YukaSystem2: pattern not found");
    return false;
  }

  HookParam hp = {};
  hp.address = addr;
  hp.type = NO_CONTEXT|USING_STRING|USING_UTF8; // UTF-8, though
  hp.text_fun = SpecialHookYukaSystem2;
  ConsoleOutput("vnreng: INSERT YukaSystem2");
  NewHook(hp, "YukaSystem2");
  return true;
}

/** jichi 8/2/2014 2RM
 *  Sample games:
 *  - [エロイッ�] 父娘� �いけなね�作り2- /HBN-20*0@54925:oyakoai.exe
 *  - [エロイッ�] ぁ�なね�作り �親友�お母さんに種付けしまくる1週間�-- /HS-1C@46FC9D (not used)
 *
 *  Observations from Debug of 父娘�:
 *  - The executable shows product name as 2RM - Adventure Engine
 *  - 2 calls to GetGlyphOutlineA with incompleted game
 *  - Memory location of the text is fixed
 *  - The LAST place accessing the text is hooked
 *  - The actual text has pattern like this {surface,ruby} and hence not hooked
 *
 *  /HBN-20*0@54925:oyakoai.exe
 *  - addr: 346405 = 0x54925
 *  - length_offset: 1
 *  - module: 3918223605
 *  - off: 4294967260 = 0xffffffdc = -0x24 -- 0x24 comes from  mov ebp,dword ptr ss:[esp+0x24]
 *  - type: 1096 = 0x448
 *
 *  This is a very long function
 *  父娘�:
 *  - 004548e1  |. 84db           test bl,bl
 *  - 004548e3  |. 8b7424 20      mov esi,dword ptr ss:[esp+0x20]
 *  - 004548e7  |. 74 08          je short oyakoai.004548f1
 *  - 004548e9  |. c74424 24 0000>mov dword ptr ss:[esp+0x24],0x0
 *  - 004548f1  |> 8b6c24 3c      mov ebp,dword ptr ss:[esp+0x3c]
 *  - 004548f5  |. 837d 5c 00     cmp dword ptr ss:[ebp+0x5c],0x0
 *  - 004548f9  |. c74424 18 0000>mov dword ptr ss:[esp+0x18],0x0
 *  - 00454901  |. 0f8e da000000  jle oyakoai.004549e1
 *  - 00454907  |. 8b6c24 24      mov ebp,dword ptr ss:[esp+0x24]
 *  - 0045490b  |. eb 0f          jmp short oyakoai.0045491c
 *  - 0045490d  |  8d49 00        lea ecx,dword ptr ds:[ecx]
 *  - 00454910  |> 8b15 50bd6c00  mov edx,dword ptr ds:[0x6cbd50]
 *  - 00454916  |. 8b0d 94bd6c00  mov ecx,dword ptr ds:[0x6cbd94]
 *  - 0045491c  |> 803f 00        cmp byte ptr ds:[edi],0x0
 *  - 0045491f  |. 0f84 db000000  je oyakoai.00454a00
 *  - 00454925  |. 0fb717         movzx edx,word ptr ds:[edi]   ; jichi: hook here
 *  - 00454928  |. 8b4c24 10      mov ecx,dword ptr ss:[esp+0x10]
 *  - 0045492c  |. 52             push edx
 *  - 0045492d  |. 894c24 2c      mov dword ptr ss:[esp+0x2c],ecx
 *  - 00454931  |. e8 9a980100    call oyakoai.0046e1d0
 *  - 00454936  |. 83c4 04        add esp,0x4
 *  - 00454939  |. 85c0           test eax,eax
 *  - 0045493b  |. 74 50          je short oyakoai.0045498d
 *  - 0045493d  |. 0335 50bd6c00  add esi,dword ptr ds:[0x6cbd50]
 *  - 00454943  |. 84db           test bl,bl
 *  - 00454945  |. 74 03          je short oyakoai.0045494a
 *  - 00454947  |. 83c5 02        add ebp,0x2
 *  - 0045494a  |> 3b7424 1c      cmp esi,dword ptr ss:[esp+0x1c]
 *  - 0045494e  |. a1 54bd6c00    mov eax,dword ptr ds:[0x6cbd54]
 *  - 00454953  |. 7f 12          jg short oyakoai.00454967
 *  - 00454955  |. 84db           test bl,bl
 *  - 00454957  |. 0f84 ea000000  je oyakoai.00454a47
 *  - 0045495d  |. 3b6c24 40      cmp ebp,dword ptr ss:[esp+0x40]
 *  - 00454961  |. 0f85 e0000000  jnz oyakoai.00454a47
 *  - 00454967  |> 014424 10      add dword ptr ss:[esp+0x10],eax
 *  - 0045496b  |. 84db           test bl,bl
 *  - 0045496d  |. 8b7424 20      mov esi,dword ptr ss:[esp+0x20]
 *  - 00454971  |. 0f84 d0000000  je oyakoai.00454a47
 *  - 00454977  |. 3b6c24 40      cmp ebp,dword ptr ss:[esp+0x40]
 *  - 0045497b  |. 0f85 c6000000  jnz oyakoai.00454a47
 *  - 00454981  |. 33ed           xor ebp,ebp
 *  - 00454983  |. 83c7 02        add edi,0x2
 *  - 00454986  |. 834424 18 01   add dword ptr ss:[esp+0x18],0x1
 *  - 0045498b  |. eb 3c          jmp short oyakoai.004549c9
 *  - 0045498d  |> a1 50bd6c00    mov eax,dword ptr ds:[0x6cbd50]
 *  - 00454992  |. d1e8           shr eax,1
 *  - 00454994  |. 03f0           add esi,eax
 *  - 00454996  |. 84db           test bl,bl
 *  - 00454998  |. 74 03          je short oyakoai.0045499d
 *  - 0045499a  |. 83c5 01        add ebp,0x1
 *  - 0045499d  |> 3b7424 1c      cmp esi,dword ptr ss:[esp+0x1c]
 *  - 004549a1  |. a1 54bd6c00    mov eax,dword ptr ds:[0x6cbd54]
 *  - 004549a6  |. 7f 0a          jg short oyakoai.004549b2
 *  - 004549a8  |. 84db           test bl,bl
 *
 *  ぁ�なね�作り:
 *  00454237   c74424 24 020000>mov dword ptr ss:[esp+0x24],0x2
 *  0045423f   3bf5             cmp esi,ebp
 *  00454241   7f 0e            jg short .00454251
 *  00454243   84db             test bl,bl
 *  00454245   74 1e            je short .00454265
 *  00454247   8b6c24 24        mov ebp,dword ptr ss:[esp+0x24]
 *  0045424b   3b6c24 40        cmp ebp,dword ptr ss:[esp+0x40]
 *  0045424f   75 14            jnz short .00454265
 *  00454251   014424 10        add dword ptr ss:[esp+0x10],eax
 *  00454255   84db             test bl,bl
 *  00454257   8b7424 20        mov esi,dword ptr ss:[esp+0x20]
 *  0045425b   74 08            je short .00454265
 *  0045425d   c74424 24 000000>mov dword ptr ss:[esp+0x24],0x0
 *  00454265   8b6c24 3c        mov ebp,dword ptr ss:[esp+0x3c]
 *  00454269   837d 5c 00       cmp dword ptr ss:[ebp+0x5c],0x0
 *  0045426d   c74424 18 000000>mov dword ptr ss:[esp+0x18],0x0
 *  00454275   0f8e d7000000    jle .00454352
 *  0045427b   8b6c24 24        mov ebp,dword ptr ss:[esp+0x24]
 *  0045427f   eb 0c            jmp short .0045428d
 *  00454281   8b15 18ad6c00    mov edx,dword ptr ds:[0x6cad18]
 *  00454287   8b0d 5cad6c00    mov ecx,dword ptr ds:[0x6cad5c]
 *  0045428d   803f 00          cmp byte ptr ds:[edi],0x0
 *  00454290   0f84 db000000    je .00454371
 *  00454296   0fb717           movzx edx,word ptr ds:[edi] ; jichi: hook here
 *  00454299   8b4c24 10        mov ecx,dword ptr ss:[esp+0x10]
 *  0045429d   52               push edx
 *  0045429e   894c24 2c        mov dword ptr ss:[esp+0x2c],ecx
 *  004542a2   e8 498a0100      call .0046ccf0
 *  004542a7   83c4 04          add esp,0x4
 *  004542aa   85c0             test eax,eax
 *  004542ac   74 50            je short .004542fe
 *  004542ae   0335 18ad6c00    add esi,dword ptr ds:[0x6cad18]
 *  004542b4   84db             test bl,bl
 *  004542b6   74 03            je short .004542bb
 *  004542b8   83c5 02          add ebp,0x2
 *  004542bb   3b7424 1c        cmp esi,dword ptr ss:[esp+0x1c]
 *  004542bf   a1 1cad6c00      mov eax,dword ptr ds:[0x6cad1c]
 *  004542c4   7f 12            jg short .004542d8
 *  004542c6   84db             test bl,bl
 *  004542c8   0f84 ea000000    je .004543b8
 *  004542ce   3b6c24 40        cmp ebp,dword ptr ss:[esp+0x40]
 *  004542d2   0f85 e0000000    jnz .004543b8
 *  004542d8   014424 10        add dword ptr ss:[esp+0x10],eax
 *  004542dc   84db             test bl,bl
 *  004542de   8b7424 20        mov esi,dword ptr ss:[esp+0x20]
 *  004542e2   0f84 d0000000    je .004543b8
 *  004542e8   3b6c24 40        cmp ebp,dword ptr ss:[esp+0x40]
 *  004542ec   0f85 c6000000    jnz .004543b8
 *  004542f2   33ed             xor ebp,ebp
 *  004542f4   83c7 02          add edi,0x2
 *  004542f7   834424 18 01     add dword ptr ss:[esp+0x18],0x1
 *  004542fc   eb 3c            jmp short .0045433a
 *  004542fe   a1 18ad6c00      mov eax,dword ptr ds:[0x6cad18]
 *  00454303   d1e8             shr eax,1
 *  00454305   03f0             add esi,eax
 *  00454307   84db             test bl,bl
 *  00454309   74 03            je short .0045430e
 *  0045430b   83c5 01          add ebp,0x1
 */
bool Insert2RMHook()
{
  const BYTE bytes[] = {
    0x80,0x3f, 0x00,                // 0045428d   803f 00          cmp byte ptr ds:[edi],0x0
    0x0f,0x84, 0xdb,0x00,0x00,0x00, // 00454290   0f84 db000000    je .00454371
    0x0f,0xb7,0x17,                 // 00454296   0fb717           movzx edx,word ptr ds:[edi] ; jichi: hook here
    0x8b,0x4c,0x24, 0x10,           // 00454299   8b4c24 10        mov ecx,dword ptr ss:[esp+0x10]
    0x52,                           // 0045429d   52               push edx
    0x89,0x4c,0x24, 0x2c,           // 0045429e   894c24 2c        mov dword ptr ss:[esp+0x2c],ecx
    0xe8 //, 498a0100               // 004542a2   e8 498a0100      call .0046ccf0
  };
  enum { addr_offset = 0x00454296 - 0x0045428d };
  ULONG range = min(module_limit_ - module_base_, MAX_REL_ADDR);
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_base_ + range);
  //GROWL_DWORD(addr); // supposed to be 0x4010e0
  if (!addr) {
    ConsoleOutput("vnreng:2RM: pattern not found");
    return false;
  }

  HookParam hp = {};
  hp.address = addr + addr_offset;
  hp.length_offset = 1;
  hp.offset = -0x24;
  hp.type = NO_CONTEXT|DATA_INDIRECT;
  ConsoleOutput("vnreng: INSERT 2RM");
  NewHook(hp, "2RM");
  return true;
}

/** jichi 8/2/2014 side-B
 *  Sample games:
 *  - [side-B] メルトピア -- /HS-4@B4452:Martopia.exe
 *
 *  Observations:
 *
 *  /HS-4@B4452:Martopia.exe
 *  - addr: 738386 = 0xb4452
 *  - module: 3040177000
 *  - off: 4294967288 = 0xfffffff8 = -0x8
 *  - type: 65 = 0x41
 *
 *  Sample stack structure:
 *  - 0016F558   00EB74E9  RETURN to Martopia.00EB74E9
 *  - 0016F55C   0060EE30 ; jichi: this is the text
 *  - 0016F560   0016F5C8
 *  - 0016F564   082CAA98
 *  - 0016F568   00EBE735  RETURN to Martopia.00EBE735 from Martopia.00EB74C0
 *
 *  00f6440e   cc               int3
 *  00f6440f   cc               int3
 *  00f64410   55               push ebp    ; jichi: hook here, text in arg1 ([EncodeSystemPointer(+4])
 *  00f64411   8bec             mov ebp,esp
 *  00f64413   6a ff            push -0x1
 *  00f64415   68 c025fb00      push martopia.00fb25c0
 *  00f6441a   64:a1 00000000   mov eax,dword ptr fs:[0]
 *  00f64420   50               push eax
 *  00f64421   83ec 3c          sub esp,0x3c
 *  00f64424   a1 c8620101      mov eax,dword ptr ds:[0x10162c8]
 *  00f64429   33c5             xor eax,ebp
 *  00f6442b   8945 f0          mov dword ptr ss:[ebp-0x10],eax
 *  00f6442e   53               push ebx
 *  00f6442f   56               push esi
 *  00f64430   57               push edi
 *  00f64431   50               push eax
 *  00f64432   8d45 f4          lea eax,dword ptr ss:[ebp-0xc]
 *  00f64435   64:a3 00000000   mov dword ptr fs:[0],eax
 *  00f6443b   8bf9             mov edi,ecx
 *  00f6443d   8b4d 08          mov ecx,dword ptr ss:[ebp+0x8]
 *  00f64440   33db             xor ebx,ebx
 *  00f64442   3bcb             cmp ecx,ebx
 *  00f64444   74 40            je short martopia.00f64486
 *  00f64446   8bc1             mov eax,ecx
 *  00f64448   c745 e8 0f000000 mov dword ptr ss:[ebp-0x18],0xf
 *  00f6444f   895d e4          mov dword ptr ss:[ebp-0x1c],ebx
 *  00f64452   885d d4          mov byte ptr ss:[ebp-0x2c],bl   ; jichi: or hook here, get text in eax
 *  00f64455   8d70 01          lea esi,dword ptr ds:[eax+0x1]
 *  00f64458   8a10             mov dl,byte ptr ds:[eax]
 *  00f6445a   40               inc eax
 *  00f6445b   3ad3             cmp dl,bl
 *  00f6445d  ^75 f9            jnz short martopia.00f64458
 *  00f6445f   2bc6             sub eax,esi
 *  00f64461   50               push eax
 *  00f64462   51               push ecx
 *  00f64463   8d4d d4          lea ecx,dword ptr ss:[ebp-0x2c]
 *  00f64466   e8 f543f5ff      call martopia.00eb8860
 *  00f6446b   8d45 d4          lea eax,dword ptr ss:[ebp-0x2c]
 *  00f6446e   50               push eax
 *  00f6446f   8d4f 3c          lea ecx,dword ptr ds:[edi+0x3c]
 *  00f64472   895d fc          mov dword ptr ss:[ebp-0x4],ebx
 *  00f64475   e8 16d7f8ff      call martopia.00ef1b90
 *  00f6447a   837d e8 10       cmp dword ptr ss:[ebp-0x18],0x10
 *  00f6447e   72 47            jb short martopia.00f644c7
 *  00f64480   8b4d d4          mov ecx,dword ptr ss:[ebp-0x2c]
 *  00f64483   51               push ecx
 *  00f64484   eb 38            jmp short martopia.00f644be
 *  00f64486   53               push ebx
 *  00f64487   68 a11efd00      push martopia.00fd1ea1
 *  00f6448c   8d4d b8          lea ecx,dword ptr ss:[ebp-0x48]
 *  00f6448f   c745 cc 0f000000 mov dword ptr ss:[ebp-0x34],0xf
 *  00f64496   895d c8          mov dword ptr ss:[ebp-0x38],ebx
 *  00f64499   885d b8          mov byte ptr ss:[ebp-0x48],bl
 *  00f6449c   e8 bf43f5ff      call martopia.00eb8860
 *  00f644a1   8d55 b8          lea edx,dword ptr ss:[ebp-0x48]
 *  00f644a4   52               push edx
 *  00f644a5   8d4f 3c          lea ecx,dword ptr ds:[edi+0x3c]
 *  00f644a8   c745 fc 01000000 mov dword ptr ss:[ebp-0x4],0x1
 *  00f644af   e8 dcd6f8ff      call martopia.00ef1b90
 *  00f644b4   837d cc 10       cmp dword ptr ss:[ebp-0x34],0x10
 *  00f644b8   72 0d            jb short martopia.00f644c7
 *  00f644ba   8b45 b8          mov eax,dword ptr ss:[ebp-0x48]
 *  00f644bd   50               push eax
 *  00f644be   ff15 f891fc00    call dword ptr ds:[<&msvcr100.??3@yaxpax>; msvcr100.??3@yaxpax@z
 *  00f644c4   83c4 04          add esp,0x4
 *  00f644c7   8b4d f4          mov ecx,dword ptr ss:[ebp-0xc]
 *  00f644ca   64:890d 00000000 mov dword ptr fs:[0],ecx
 *  00f644d1   59               pop ecx
 *  00f644d2   5f               pop edi
 *  00f644d3   5e               pop esi
 *  00f644d4   5b               pop ebx
 *  00f644d5   8b4d f0          mov ecx,dword ptr ss:[ebp-0x10]
 *  00f644d8   33cd             xor ecx,ebp
 *  00f644da   e8 77510400      call martopia.00fa9656
 *  00f644df   8be5             mov esp,ebp
 *  00f644e1   5d               pop ebp
 *  00f644e2   c2 0400          retn 0x4
 *  00f644e5   cc               int3
 *  00f644e6   cc               int3
 */
bool InsertSideBHook()
{
  const BYTE bytes[] = {
    0x64,0xa3, 0x00,0x00,0x00,0x00,       // 00f64435   64:a3 00000000   mov dword ptr fs:[0],eax
    0x8b,0xf9,                            // 00f6443b   8bf9             mov edi,ecx
    0x8b,0x4d, 0x08,                      // 00f6443d   8b4d 08          mov ecx,dword ptr ss:[ebp+0x8]
    0x33,0xdb,                            // 00f64440   33db             xor ebx,ebx
    0x3b,0xcb,                            // 00f64442   3bcb             cmp ecx,ebx
    0x74, 0x40,                           // 00f64444   74 40            je short martopia.00f64486
    0x8b,0xc1,                            // 00f64446   8bc1             mov eax,ecx
    0xc7,0x45, 0xe8, 0x0f,0x00,0x00,0x00, // 00f64448   c745 e8 0f000000 mov dword ptr ss:[ebp-0x18],0xf
    0x89,0x5d, 0xe4,                      // 00f6444f   895d e4          mov dword ptr ss:[ebp-0x1c],ebx
    0x88,0x5d, 0xd4                       // 00f64452   885d d4          mov byte ptr ss:[ebp-0x2c],bl
  };
  enum { addr_offset = 0x00f64410 - 0x00f64435 }; // distance to the beginning of the function
  ULONG range = min(module_limit_ - module_base_, MAX_REL_ADDR);
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_base_ + range);
  //GROWL_DWORD(addr); // supposed to be 0x4010e0
  if (!addr) {
    ConsoleOutput("vnreng:SideB: pattern not found");
    return false;
  }
  addr += addr_offset;
  enum : BYTE { push_ebp = 0x55 };  // 011d4c80  /$ 55             push ebp
  if (*(BYTE *)addr != push_ebp) {
    ConsoleOutput("vnreng:SideB: pattern found but the function offset is invalid");
    return false;
  }
  //GROWL_DWORD(addr);

  HookParam hp = {};
  hp.address = addr;
  //hp.length_offset = 1;
  hp.offset = 4; // [esp+4] == arg1
  hp.type = USING_STRING|NO_CONTEXT|USING_SPLIT|RELATIVE_SPLIT; // NO_CONTEXT && RELATIVE_SPLIT to get rid of floating return address
  //hp.split = 0; // use retaddr as split
  ConsoleOutput("vnreng: INSERT SideB");
  NewHook(hp, "SideB");
  return true;
}

/** jichi 9/8/2014 EXP, http://www.exp-inc.jp
 *  Maker: EXP, 5pb
 *  Sample game: 剣の街�異邦人
 *
 *  There are three matched memory addresses with SHIFT-JIS.
 *  The middle one is used as it is aligned with zeros.
 *  The memory address is fixed.
 *
 *  There are three functions found using hardware breakpoints.
 *  The last one is used as the first two are looped.
 *
 *  reladdr = 0x138020
 *
 *  baseaddr = 0x00120000
 *
 *  0025801d   cc               int3
 *  0025801e   cc               int3
 *  0025801f   cc               int3
 *  00258020   55               push ebp  ; jichi: hook here
 *  00258021   8bec             mov ebp,esp
 *  00258023   8b45 08          mov eax,dword ptr ss:[ebp+0x8]
 *  00258026   83ec 08          sub esp,0x8
 *  00258029   85c0             test eax,eax
 *  0025802b   0f84 d8000000    je .00258109
 *  00258031   837d 10 00       cmp dword ptr ss:[ebp+0x10],0x0
 *  00258035   0f84 ce000000    je .00258109
 *  0025803b   8b10             mov edx,dword ptr ds:[eax]      ; jichi: edx is the text
 *  0025803d   8b45 0c          mov eax,dword ptr ss:[ebp+0xc]
 *  00258040   53               push ebx
 *  00258041   56               push esi
 *  00258042   c745 f8 00000000 mov dword ptr ss:[ebp-0x8],0x0
 *  00258049   8945 fc          mov dword ptr ss:[ebp-0x4],eax
 *  0025804c   57               push edi
 *  0025804d   8d49 00          lea ecx,dword ptr ds:[ecx]
 *  00258050   8a0a             mov cl,byte ptr ds:[edx]    jichi: text in accessed in edx
 *  00258052   8a45 14          mov al,byte ptr ss:[ebp+0x14]
 *  00258055   3ac1             cmp al,cl
 *  00258057   74 7a            je short .002580d3
 *  00258059   8b7d 10          mov edi,dword ptr ss:[ebp+0x10]
 *  0025805c   8b5d fc          mov ebx,dword ptr ss:[ebp-0x4]
 *  0025805f   33f6             xor esi,esi
 *  00258061   8bc2             mov eax,edx
 *  00258063   80f9 81          cmp cl,0x81
 *  00258066   72 05            jb short .0025806d
 *  00258068   80f9 9f          cmp cl,0x9f
 *  0025806b   76 0a            jbe short .00258077
 *  0025806d   80f9 e0          cmp cl,0xe0
 *  00258070   72 1d            jb short .0025808f
 *  00258072   80f9 fc          cmp cl,0xfc
 *  00258075   77 18            ja short .0025808f
 *  00258077   8b45 fc          mov eax,dword ptr ss:[ebp-0x4]
 *  0025807a   85c0             test eax,eax
 *  0025807c   74 05            je short .00258083
 *  0025807e   8808             mov byte ptr ds:[eax],cl
 *  00258080   8d58 01          lea ebx,dword ptr ds:[eax+0x1]
 *  00258083   8b7d 10          mov edi,dword ptr ss:[ebp+0x10]
 *  00258086   8d42 01          lea eax,dword ptr ds:[edx+0x1]
 *  00258089   be 01000000      mov esi,0x1
 *  0025808e   4f               dec edi
 *  0025808f   85ff             test edi,edi
 *  00258091   74 36            je short .002580c9
 *  00258093   85db             test ebx,ebx
 *  00258095   74 04            je short .0025809b
 *  00258097   8a08             mov cl,byte ptr ds:[eax]
 *  00258099   880b             mov byte ptr ds:[ebx],cl
 *  0025809b   46               inc esi
 *  0025809c   33c0             xor eax,eax
 *  0025809e   66:3bc6          cmp ax,si
 *  002580a1   7f 47            jg short .002580ea
 *  002580a3   0fbfce           movsx ecx,si
 *  002580a6   03d1             add edx,ecx
 *  002580a8   3945 fc          cmp dword ptr ss:[ebp-0x4],eax
 *  002580ab   74 03            je short .002580b0
 *  002580ad   014d fc          add dword ptr ss:[ebp-0x4],ecx
 *  002580b0   294d 10          sub dword ptr ss:[ebp+0x10],ecx
 *  002580b3   014d f8          add dword ptr ss:[ebp-0x8],ecx
 *  002580b6   8a0a             mov cl,byte ptr ds:[edx]
 *  002580b8   80f9 0a          cmp cl,0xa
 *  002580bb   74 20            je short .002580dd
 *  002580bd   80f9 0d          cmp cl,0xd
 *  002580c0   74 1b            je short .002580dd
 *  002580c2   3945 10          cmp dword ptr ss:[ebp+0x10],eax
 *  002580c5  ^75 89            jnz short .00258050
 *  002580c7   eb 21            jmp short .002580ea
 *  002580c9   85db             test ebx,ebx
 *  002580cb   74 1d            je short .002580ea
 *  002580cd   c643 ff 00       mov byte ptr ds:[ebx-0x1],0x0
 *  002580d1   eb 17            jmp short .002580ea
 *  002580d3   84c0             test al,al
 *  002580d5   74 13            je short .002580ea
 *  002580d7   42               inc edx
 *  002580d8   ff45 f8          inc dword ptr ss:[ebp-0x8]
 *  002580db   eb 0d            jmp short .002580ea
 *  002580dd   8a42 01          mov al,byte ptr ds:[edx+0x1]
 *  002580e0   42               inc edx
 *  002580e1   3c 0a            cmp al,0xa
 *  002580e3   74 04            je short .002580e9
 *  002580e5   3c 0d            cmp al,0xd
 *  002580e7   75 01            jnz short .002580ea
 *  002580e9   42               inc edx
 *  002580ea   8b45 fc          mov eax,dword ptr ss:[ebp-0x4]
 *  002580ed   5f               pop edi
 *  002580ee   5e               pop esi
 *  002580ef   5b               pop ebx
 *  002580f0   85c0             test eax,eax
 *  002580f2   74 09            je short .002580fd
 *  002580f4   837d 10 00       cmp dword ptr ss:[ebp+0x10],0x0
 *  002580f8   74 03            je short .002580fd
 *  002580fa   c600 00          mov byte ptr ds:[eax],0x0
 *  002580fd   8b4d 08          mov ecx,dword ptr ss:[ebp+0x8]
 *  00258100   8b45 f8          mov eax,dword ptr ss:[ebp-0x8]
 *  00258103   8911             mov dword ptr ds:[ecx],edx
 *  00258105   8be5             mov esp,ebp
 *  00258107   5d               pop ebp
 *  00258108   c3               retn
 *  00258109   33c0             xor eax,eax
 *  0025810b   8be5             mov esp,ebp
 *  0025810d   5d               pop ebp
 *  0025810e   c3               retn
 *  0025810f   cc               int3
 *
 *  Stack:
 *  0f14f87c   00279177  return to .00279177 from .00258020
 *  0f14f880   0f14f8b0 ; arg1  address of the text's pointer
 *  0f14f884   0f14f8c0 ; arg2  pointed to zero, maybe a buffer
 *  0f14f888   00000047 ; arg3  it is zero if no text, this value might be text size + 1
 *  0f14f88c   ffffff80 ; constant, used as split
 *  0f14f890   005768c8  .005768c8
 *  0f14f894   02924340 ; text is at 02924350
 *  0f14f898   00000001 ; this might also be a good split
 *  0f14f89c   1b520020
 *  0f14f8a0   00000000
 *  0f14f8a4   00000000
 *  0f14f8a8   029245fc
 *  0f14f8ac   0004bfd3
 *  0f14f8b0   0f14fae0
 *  0f14f8b4   00000000
 *  0f14f8b8   00000000
 *  0f14f8bc   02924340
 *  0f14f8c0   00000000
 *
 *  Registers:
 *  eax 0f14f8c0 ; floating at runtime
 *  ecx 0f14f8b0; floating at runtime
 *  edx 00000000
 *  ebx 0f14fae0; floating at runtime
 *  esp 0f14f87c; floating at runtime
 *  ebp 0f14facc; floating at runtime
 *  esi 00000047
 *  edi 02924340 ; text is in 02924350
 *  eip 00258020 .00258020
 *
 *  Memory access pattern:
 *  For long sentences, it first render the first line, then the second line, and so on.
 *  So, the second line is a subtext of the entire dialog.
 */
static void SpecialHookExp(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  static DWORD lasttext;
  // 00258020   55               push ebp  ; jichi: hook here
  // 00258021   8bec             mov ebp,esp
  // 00258023   8b45 08          mov eax,dword ptr ss:[ebp+0x8] ; jichi: move arg1 to eax
  // 00258029   85c0             test eax,eax   ; check if text is null
  // 0025802b   0f84 d8000000    je .00258109
  // 00258031   837d 10 00       cmp dword ptr ss:[ebp+0x10],0x0 ; jichi: compare 0 with arg3, which is size+1
  // 00258035   0f84 ce000000    je .00258109
  // 0025803b   8b10             mov edx,dword ptr ds:[eax] ; move text address to edx
  DWORD arg1 = argof(1, esp_base), // mov eax,dword ptr ss:[ebp+0x8]
        arg3 = argof(3, esp_base); // size - 1
  if (arg1 && arg3)
    if (DWORD text = *(DWORD *)arg1)
      if (!(text > lasttext && text < lasttext + VNR_TEXT_CAPACITY)) { // text is not a subtext of lastText
        *data = lasttext = text; // mov edx,dword ptr ds:[eax]
        //*len = arg3 - 1; // the last char is the '\0', so -1, but this value is not reliable
        *len = ::strlen((LPCSTR)text);
        // Registers are not used as split as all of them are floating at runtime
        //*split = argof(4, esp_base); // arg4, always -8, this will merge all threads and result in repetition
        *split = argof(7, esp_base); // reduce repetition, but still have sub-text repeat
      }
}
bool InsertExpHook()
{
  const BYTE bytes[] = {
    0x55,                   // 00258020   55               push ebp  ; jichi: hook here, function starts, text in [arg1], size+1 in arg3
    0x8b,0xec,              // 00258021   8bec             mov ebp,esp
    0x8b,0x45, 0x08,        // 00258023   8b45 08          mov eax,dword ptr ss:[ebp+0x8]
    0x83,0xec, 0x08,        // 00258026   83ec 08          sub esp,0x8
    0x85,0xc0,              // 00258029   85c0             test eax,eax
    0x0f,0x84, XX4,         // 0025802b   0f84 d8000000    je .00258109
    0x83,0x7d, 0x10, 0x00,  // 00258031   837d 10 00       cmp dword ptr ss:[ebp+0x10],0x0
    0x0f,0x84, XX4,         // 00258035   0f84 ce000000    je .00258109
    0x8b,0x10,              // 0025803b   8b10             mov edx,dword ptr ds:[eax] ; jichi: edx is the text
    0x8b,0x45, 0x0c,        // 0025803d   8b45 0c          mov eax,dword ptr ss:[ebp+0xc]
    0x53,                   // 00258040   53               push ebx
    0x56,                   // 00258041   56               push esi
    0xc7,0x45, 0xf8, 0x00,0x00,0x00,0x00,   // 00258042   c745 f8 00000000 mov dword ptr ss:[ebp-0x8],0x0
    0x89,0x45, 0xfc,        // 00258049   8945 fc          mov dword ptr ss:[ebp-0x4],eax
    0x57,                   // 0025804c   57               push edi
    0x8d,0x49, 0x00,        // 0025804d   8d49 00          lea ecx,dword ptr ds:[ecx]
    0x8a,0x0a               // 00258050   8a0a             mov cl,byte ptr ds:[edx]  ; jichi: text accessed in edx
  };
  enum { addr_offset = 0 };
  ULONG range = min(module_limit_ - module_base_, MAX_REL_ADDR);
  ULONG addr = MemDbg::matchBytes(bytes, sizeof(bytes), module_base_, module_base_ + range);
  //GROWL_DWORD(addr);
  if (!addr) {
    ConsoleOutput("vnreng:EXP: pattern not found");
    return false;
  }

  HookParam hp = {};
  hp.address = addr + addr_offset;
  hp.type = NO_CONTEXT|USING_STRING; // NO_CONTEXT to get rid of floating address
  hp.text_fun = SpecialHookExp;
  ConsoleOutput("vnreng: INSERT EXP");
  NewHook(hp, "EXP"); // FIXME: text displayed line by line

  ConsoleOutput("vnreng:EXP: disable GDI hooks"); // There are no GDI functions hooked though
  DisableGDIHooks();
  return true;
}

/** 10/20/2014 jichi: HorkEye, http://horkeye.com
 *  Sample game: [150226] 結城友奈�勀��ある 体験版
 *
 *  No GDI functions are used by this game.
 *
 *  Debug method:
 *  There are two matched texts.
 *  The one having fixed address is used to insert hw breakpoints.
 *
 *  I found are two functions addressing the address, both of which seems to be good.
 *  The first one is used:
 *
 *  013cda60   8d4c24 1c        lea ecx,dword ptr ss:[esp+0x1c]
 *  013cda64   51               push ecx
 *  013cda65   68 48a8c201      push .01c2a848                                     ; ascii "if"
 *  013cda6a   e8 d1291600      call .01530440
 *  013cda6f   83c4 0c          add esp,0xc
 *  013cda72   6a 01            push 0x1
 *  013cda74   83ec 1c          sub esp,0x1c
 *  013cda77   8bcc             mov ecx,esp
 *  013cda79   896424 30        mov dword ptr ss:[esp+0x30],esp
 *  013cda7d   6a 10            push 0x10
 *  013cda7f   c741 14 0f000000 mov dword ptr ds:[ecx+0x14],0xf
 *  013cda86   c741 10 00000000 mov dword ptr ds:[ecx+0x10],0x0
 *  013cda8d   68 80125601      push .01561280
 *  013cda92   c601 00          mov byte ptr ds:[ecx],0x0
 *  013cda95   e8 5681ffff      call .013c5bf0
 *  013cda9a   e8 717a0900      call .01465510
 *  013cda9f   83c4 20          add esp,0x20
 *  013cdaa2   b8 01000000      mov eax,0x1
 *  013cdaa7   8b8c24 b8000000  mov ecx,dword ptr ss:[esp+0xb8]
 *  013cdaae   5f               pop edi
 *  013cdaaf   5e               pop esi
 *  013cdab0   5d               pop ebp
 *  013cdab1   5b               pop ebx
 *  013cdab2   33cc             xor ecx,esp
 *  013cdab4   e8 c7361600      call .01531180
 *  013cdab9   81c4 ac000000    add esp,0xac
 *  013cdabf   c3               retn
 *  013cdac0   83ec 40          sub esp,0x40
 *  013cdac3   a1 24805d01      mov eax,dword ptr ds:[0x15d8024]
 *  013cdac8   8b15 c4709901    mov edx,dword ptr ds:[0x19970c4]
 *  013cdace   8d0c00           lea ecx,dword ptr ds:[eax+eax]
 *  013cdad1   a1 9c506b01      mov eax,dword ptr ds:[0x16b509c]
 *  013cdad6   0305 18805d01    add eax,dword ptr ds:[0x15d8018]
 *  013cdadc   53               push ebx
 *  013cdadd   8b5c24 48        mov ebx,dword ptr ss:[esp+0x48]
 *  013cdae1   55               push ebp
 *  013cdae2   8b6c24 50        mov ebp,dword ptr ss:[esp+0x50]
 *  013cdae6   894c24 34        mov dword ptr ss:[esp+0x34],ecx
 *  013cdaea   8b0d 20805d01    mov ecx,dword ptr ds:[0x15d8020]
 *  013cdaf0   894424 18        mov dword ptr ss:[esp+0x18],eax
 *  013cdaf4   a1 1c805d01      mov eax,dword ptr ds:[0x15d801c]
 *  013cdaf9   03c8             add ecx,eax
 *  013cdafb   56               push esi
 *  013cdafc   33f6             xor esi,esi
 *  013cdafe   d1f8             sar eax,1
 *  013cdb00   45               inc ebp
 *  013cdb01   896c24 24        mov dword ptr ss:[esp+0x24],ebp
 *  013cdb05   897424 0c        mov dword ptr ss:[esp+0xc],esi
 *  013cdb09   894c24 18        mov dword ptr ss:[esp+0x18],ecx
 *  013cdb0d   8a0c1a           mov cl,byte ptr ds:[edx+ebx]        jichi: here
 *  013cdb10   894424 30        mov dword ptr ss:[esp+0x30],eax
 *  013cdb14   8a441a 01        mov al,byte ptr ds:[edx+ebx+0x1]
 *  013cdb18   57               push edi
 *  013cdb19   897424 14        mov dword ptr ss:[esp+0x14],esi
 *  013cdb1d   3935 c8709901    cmp dword ptr ds:[0x19970c8],esi
 *
 *  The hooked place is only accessed once.
 *  013cdb0d   8a0c1a           mov cl,byte ptr ds:[edx+ebx]        jichi: here
 *  ebx is the text to be base address.
 *  edx is the offset to skip character name.
 *
 *  023B66A0  81 79 89 C4 EA A3 2C 53 30 30 35 5F 42 5F 30 30  【夏偾,S005_B_00
 *  023B66B0  30 32 81 7A 81 75 83 6F 81 5B 83 65 83 62 83 4E  02】「バーッ�ク
 *  023B66C0  83 58 82 CD 82 B1 82 C1 82 BF 82 CC 93 73 8D 87  スはこっちの都� *  023B66D0  82 C8 82 C7 82 A8 8D 5C 82 A2 82 C8 82 B5 81 63  などお構いなし…
 *
 *  There are garbage in character name.
 *
 *  1/15/2015
 *  Alternative hook that might not need a text filter:
 *  http://www.hongfire.com/forum/showthread.php/36807-AGTH-text-extraction-tool-for-games-translation/page753
 *  /HA-4@552B5:姉小路直子と銀色の死�exe
 *  If this hook no longer works, try that one instead.
 */
// Skip text between "," and "�, and remove [n]
// ex:【夏偾,S005_B_0002】「バーッ�ク
static bool HorkEyeFilter(LPVOID data, DWORD *size, HookParam *, BYTE)
{
  size_t len = *size;
  char *str = reinterpret_cast<char *>(data),
       *start,
       *stop;

  // Remove text between , and ]
  // FIXME: This does not work well because of the ascii encoding
  if ((start = (char *)::memchr(str, ',', len)) &&
      (stop = cpp_strnstr(start, "\x81\x7a", len - (start - str))) &&
      (len -= stop - start)) // = u'�.encode('sjis')
    ::memmove(start, stop, len - (start - str));

  // Remove [n]
  enum { skip_len = 3 }; // = length of "[n]"
  while (len >= skip_len &&
         (start = cpp_strnstr(str, "[n]", len)) &&
         (len -= skip_len))
    ::memmove(start, start + skip_len, len - (start - str));

  *size = len;
  return true;
}
bool InsertHorkEyeHook()
{
  const BYTE bytes[] = {
    0x89,0x6c,0x24, 0x24,   // 013cdb01   896c24 24        mov dword ptr ss:[esp+0x24],ebp
    0x89,0x74,0x24, 0x0c,   // 013cdb05   897424 0c        mov dword ptr ss:[esp+0xc],esi
    0x89,0x4c,0x24, 0x18,   // 013cdb09   894c24 18        mov dword ptr ss:[esp+0x18],ecx
    0x8a,0x0c,0x1a          // 013cdb0d   8a0c1a           mov cl,byte ptr ds:[edx+ebx]        jichi: here
  };
  enum { addr_offset = sizeof(bytes) - 3 }; // 8a0c1a
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_limit_);
  if (!addr) {
    ConsoleOutput("vnreng:HorkEye: pattern not found");
    return false;
  }

  HookParam hp = {};
  hp.address = addr + addr_offset;
  hp.offset = pusha_ebx_off -4;
  hp.type = USING_STRING|NO_CONTEXT|FIXING_SPLIT; // floating address
  hp.filter_fun = HorkEyeFilter;
  ConsoleOutput("vnreng: INSERT HorkEye");
  NewHook(hp, "HorkEye");
  return true;
}

/** jichi 12/2/2014 5pb
 *
 *  Sample game: [140924] CROSS�CHANNEL 〜FINAL COMPLETE� *  See: http://sakuradite.com/topic/528
 *
 *  Debugging method: insert breakpoint.
 *  The first matched function cannot extract prelude text.
 *  The second matched function can extract anything but contains garbage.
 *
 *  Function for scenario:
 *  0016d90e   cc               int3
 *  0016d90f   cc               int3
 *  0016d910   8b15 782b6e06    mov edx,dword ptr ds:[0x66e2b78]         ; .00b43bfe
 *  0016d916   8a0a             mov cl,byte ptr ds:[edx]	; jichi: hook here
 *  0016d918   33c0             xor eax,eax
 *  0016d91a   84c9             test cl,cl
 *  0016d91c   74 41            je short .0016d95f
 *  0016d91e   8bff             mov edi,edi
 *  0016d920   80f9 25          cmp cl,0x25
 *  0016d923   75 11            jnz short .0016d936
 *  0016d925   8a4a 01          mov cl,byte ptr ds:[edx+0x1]
 *  0016d928   42               inc edx
 *  0016d929   80f9 4e          cmp cl,0x4e
 *  0016d92c   74 05            je short .0016d933
 *  0016d92e   80f9 6e          cmp cl,0x6e
 *  0016d931   75 26            jnz short .0016d959
 *  0016d933   42               inc edx
 *  0016d934   eb 23            jmp short .0016d959
 *  0016d936   80f9 81          cmp cl,0x81
 *  0016d939   72 05            jb short .0016d940
 *  0016d93b   80f9 9f          cmp cl,0x9f
 *  0016d93e   76 0a            jbe short .0016d94a
 *  0016d940   80f9 e0          cmp cl,0xe0
 *  0016d943   72 0c            jb short .0016d951
 *  0016d945   80f9 fc          cmp cl,0xfc
 *  0016d948   77 07            ja short .0016d951
 *  0016d94a   b9 02000000      mov ecx,0x2
 *  0016d94f   eb 05            jmp short .0016d956
 *  0016d951   b9 01000000      mov ecx,0x1
 *  0016d956   40               inc eax
 *  0016d957   03d1             add edx,ecx
 *  0016d959   8a0a             mov cl,byte ptr ds:[edx]
 *  0016d95b   84c9             test cl,cl
 *  0016d95d  ^75 c1            jnz short .0016d920
 *  0016d95f   c3               retn
 *
 *  Function for everything:
 *  001e9a76   8bff             mov edi,edi
 *  001e9a78   55               push ebp
 *  001e9a79   8bec             mov ebp,esp
 *  001e9a7b   51               push ecx
 *  001e9a7c   8365 fc 00       and dword ptr ss:[ebp-0x4],0x0
 *  001e9a80   53               push ebx
 *  001e9a81   8b5d 10          mov ebx,dword ptr ss:[ebp+0x10]
 *  001e9a84   85db             test ebx,ebx
 *  001e9a86   75 07            jnz short .001e9a8f
 *  001e9a88   33c0             xor eax,eax
 *  001e9a8a   e9 9a000000      jmp .001e9b29
 *  001e9a8f   56               push esi
 *  001e9a90   83fb 04          cmp ebx,0x4
 *  001e9a93   72 75            jb short .001e9b0a
 *  001e9a95   8d73 fc          lea esi,dword ptr ds:[ebx-0x4]
 *  001e9a98   85f6             test esi,esi
 *  001e9a9a   74 6e            je short .001e9b0a
 *  001e9a9c   8b4d 0c          mov ecx,dword ptr ss:[ebp+0xc]
 *  001e9a9f   8b45 08          mov eax,dword ptr ss:[ebp+0x8]
 *  001e9aa2   8a10             mov dl,byte ptr ds:[eax]
 *  001e9aa4   83c0 04          add eax,0x4
 *  001e9aa7   83c1 04          add ecx,0x4
 *  001e9aaa   84d2             test dl,dl
 *  001e9aac   74 52            je short .001e9b00
 *  001e9aae   3a51 fc          cmp dl,byte ptr ds:[ecx-0x4]
 *  001e9ab1   75 4d            jnz short .001e9b00
 *  001e9ab3   8a50 fd          mov dl,byte ptr ds:[eax-0x3]
 *  001e9ab6   84d2             test dl,dl
 *  001e9ab8   74 3c            je short .001e9af6
 *  001e9aba   3a51 fd          cmp dl,byte ptr ds:[ecx-0x3]
 *  001e9abd   75 37            jnz short .001e9af6
 *  001e9abf   8a50 fe          mov dl,byte ptr ds:[eax-0x2]
 *  001e9ac2   84d2             test dl,dl
 *  001e9ac4   74 26            je short .001e9aec
 *  001e9ac6   3a51 fe          cmp dl,byte ptr ds:[ecx-0x2]
 *  001e9ac9   75 21            jnz short .001e9aec
 *  001e9acb   8a50 ff          mov dl,byte ptr ds:[eax-0x1]
 *  001e9ace   84d2             test dl,dl
 *  001e9ad0   74 10            je short .001e9ae2
 *  001e9ad2   3a51 ff          cmp dl,byte ptr ds:[ecx-0x1]
 *  001e9ad5   75 0b            jnz short .001e9ae2
 *  001e9ad7   8345 fc 04       add dword ptr ss:[ebp-0x4],0x4
 *  001e9adb   3975 fc          cmp dword ptr ss:[ebp-0x4],esi
 *  001e9ade  ^72 c2            jb short .001e9aa2
 *  001e9ae0   eb 2e            jmp short .001e9b10
 *  001e9ae2   0fb640 ff        movzx eax,byte ptr ds:[eax-0x1]
 *  001e9ae6   0fb649 ff        movzx ecx,byte ptr ds:[ecx-0x1]
 *  001e9aea   eb 46            jmp short .001e9b32
 *  001e9aec   0fb640 fe        movzx eax,byte ptr ds:[eax-0x2]
 *  001e9af0   0fb649 fe        movzx ecx,byte ptr ds:[ecx-0x2]
 *  001e9af4   eb 3c            jmp short .001e9b32
 *  001e9af6   0fb640 fd        movzx eax,byte ptr ds:[eax-0x3]
 *  001e9afa   0fb649 fd        movzx ecx,byte ptr ds:[ecx-0x3]
 *  001e9afe   eb 32            jmp short .001e9b32
 *  001e9b00   0fb640 fc        movzx eax,byte ptr ds:[eax-0x4]
 *  001e9b04   0fb649 fc        movzx ecx,byte ptr ds:[ecx-0x4]
 *  001e9b08   eb 28            jmp short .001e9b32
 *  001e9b0a   8b4d 0c          mov ecx,dword ptr ss:[ebp+0xc]
 *  001e9b0d   8b45 08          mov eax,dword ptr ss:[ebp+0x8]
 *  001e9b10   8b75 fc          mov esi,dword ptr ss:[ebp-0x4]
 *  001e9b13   eb 0d            jmp short .001e9b22
 *  001e9b15   8a10             mov dl,byte ptr ds:[eax]    ; jichi: here, word by word
 *  001e9b17   84d2             test dl,dl
 *  001e9b19   74 11            je short .001e9b2c
 *  001e9b1b   3a11             cmp dl,byte ptr ds:[ecx]
 *  001e9b1d   75 0d            jnz short .001e9b2c
 *  001e9b1f   40               inc eax
 *  001e9b20   46               inc esi
 *  001e9b21   41               inc ecx
 *  001e9b22   3bf3             cmp esi,ebx
 *  001e9b24  ^72 ef            jb short .001e9b15
 *  001e9b26   33c0             xor eax,eax
 *  001e9b28   5e               pop esi
 *  001e9b29   5b               pop ebx
 *  001e9b2a   c9               leave
 *  001e9b2b   c3               retn
 */
namespace { // unnamed

// Characters to ignore: [%0-9A-Z]
bool Insert5pbHook1()
{
  const BYTE bytes[] = {
    0xcc,           // 0016d90e   cc               int3
    0xcc,           // 0016d90f   cc               int3
    0x8b,0x15, XX4, // 0016d910   8b15 782b6e06    mov edx,dword ptr ds:[0x66e2b78]         ; .00b43bfe
    0x8a,0x0a,      // 0016d916   8a0a             mov cl,byte ptr ds:[edx]	; jichi: hook here
    0x33,0xc0,      // 0016d918   33c0             xor eax,eax
    0x84,0xc9       // 0016d91a   84c9             test cl,cl
  };
  enum { addr_offset = 0x0016d916 - 0x0016d90e };

  ULONG addr = MemDbg::matchBytes(bytes, sizeof(bytes), module_base_, module_limit_);
  //GROWL_DWORD3(addr+addr_offset, module_base_,module_limit_);
  if (!addr) {
    ConsoleOutput("vnreng:5pb1: pattern not found");
    return false;
  }

  HookParam hp = {};
  hp.address = addr + addr_offset;
  hp.offset = pusha_edx_off - 4;
  hp.type = USING_STRING;
  ConsoleOutput("vnreng: INSERT 5pb1");
  NewHook(hp, "5pb1");

  // GDI functions are not used by 5pb games anyway.
  //ConsoleOutput("vnreng:5pb: disable GDI hooks");
  //DisableGDIHooks();
  return true;
}

// Characters to ignore: [%@A-z]
inline bool _5pb2garbage_ch(char c)
{ return c == '%' || c == '@' || c >= 'A' && c <= 'z'; }

// 001e9b15   8a10             mov dl,byte ptr ds:[eax]    ; jichi: here, word by word
void SpecialHook5pb2(DWORD esp_base, HookParam *, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  static DWORD lasttext;
  DWORD text = regof(eax, esp_base);
  if (lasttext == text)
    return;
  BYTE c = *(BYTE *)text;
  if (!c)
    return;
  BYTE size = ::LeadByteTable[c]; // 1, 2, or 3
  if (size == 1 && _5pb2garbage_ch(*(LPCSTR)text))
    return;
  lasttext = text;
  *data = text;
  *len = size;
}

bool Insert5pbHook2()
{
  const BYTE bytes[] = {
    0x8a,0x10, // 001e9b15   8a10             mov dl,byte ptr ds:[eax]    ; jichi: here, word by word
    0x84,0xd2, // 001e9b17   84d2             test dl,dl
    0x74,0x11  // 001e9b19   74 11            je short .001e9b2c
  };
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_limit_);
  //GROWL_DWORD3(addr, module_base_,module_limit_);
  if (!addr) {
    ConsoleOutput("vnreng:5pb2: pattern not found");
    return false;
  }

  HookParam hp = {};
  hp.address = addr;
  hp.type = USING_STRING;
  hp.text_fun = SpecialHook5pb2;
  //hp.offset = pusha_eax_off - 4;
  //hp.length_offset = 1;
  ConsoleOutput("vnreng: INSERT 5pb2");
  NewHook(hp, "5pb2");

  // GDI functions are not used by 5pb games anyway.
  //ConsoleOutput("vnreng:5pb: disable GDI hooks");
  //DisableGDIHooks();
  return true;
}

/** jichi 2/2/2015: New 5pb hook
 *  Sample game: Hyperdimension.Neptunia.ReBirth1
 *
 *  Debugging method: hardware breakpoint and find function in msvc110
 *  Then, backtrack the function stack to find proper function.
 *
 *  Hooked function: 558BEC56FF750C8BF1FF75088D460850
 *
 *  0025A12E   CC               INT3
 *  0025A12F   CC               INT3
 *  0025A130   55               PUSH EBP
 *  0025A131   8BEC             MOV EBP,ESP
 *  0025A133   56               PUSH ESI
 *  0025A134   FF75 0C          PUSH DWORD PTR SS:[EBP+0xC]
 *  0025A137   8BF1             MOV ESI,ECX
 *  0025A139   FF75 08          PUSH DWORD PTR SS:[EBP+0x8]
 *  0025A13C   8D46 08          LEA EAX,DWORD PTR DS:[ESI+0x8]
 *  0025A13F   50               PUSH EAX
 *  0025A140   E8 DB100100      CALL .0026B220
 *  0025A145   8B8E 988D0000    MOV ECX,DWORD PTR DS:[ESI+0x8D98]
 *  0025A14B   8988 80020000    MOV DWORD PTR DS:[EAX+0x280],ECX
 *  0025A151   8B8E A08D0000    MOV ECX,DWORD PTR DS:[ESI+0x8DA0]
 *  0025A157   8988 88020000    MOV DWORD PTR DS:[EAX+0x288],ECX
 *  0025A15D   8B8E A88D0000    MOV ECX,DWORD PTR DS:[ESI+0x8DA8]
 *  0025A163   8988 90020000    MOV DWORD PTR DS:[EAX+0x290],ECX
 *  0025A169   8B8E B08D0000    MOV ECX,DWORD PTR DS:[ESI+0x8DB0]
 *  0025A16F   8988 98020000    MOV DWORD PTR DS:[EAX+0x298],ECX
 *  0025A175   83C4 0C          ADD ESP,0xC
 *  0025A178   8D8E 188B0000    LEA ECX,DWORD PTR DS:[ESI+0x8B18]
 *  0025A17E   E8 DDD8FEFF      CALL .00247A60
 *  0025A183   5E               POP ESI
 *  0025A184   5D               POP EBP
 *  0025A185   C2 0800          RETN 0x8
 *  0025A188   CC               INT3
 *  0025A189   CC               INT3
 *
 *  Runtime stack, text in arg1, and name in arg2:
 *
 *  0015F93C   00252330  RETURN to .00252330 from .0025A130
 *  0015F940   181D0D4C  ASCII "That's my line! I won't let any of you
 *  take the title of True Goddess!"
 *  0015F944   0B8B4D20  ASCII "  White Heart  "
 *  0015F948   0B8B5528
 *  0015F94C   0B8B5524
 *  0015F950  /0015F980
 *  0015F954  |0026000F  RETURN to .0026000F from .002521D0
 *
 *
 *  Another candidate funciton for backup usage.
 *  Previous text in arg1.
 *  Current text in arg2.
 *  Current name in arg3.
 *
 *  0026B21C   CC               INT3
 *  0026B21D   CC               INT3
 *  0026B21E   CC               INT3
 *  0026B21F   CC               INT3
 *  0026B220   55               PUSH EBP
 *  0026B221   8BEC             MOV EBP,ESP
 *  0026B223   81EC A0020000    SUB ESP,0x2A0
 *  0026B229   BA A0020000      MOV EDX,0x2A0
 *  0026B22E   53               PUSH EBX
 *  0026B22F   8B5D 08          MOV EBX,DWORD PTR SS:[EBP+0x8]
 *  0026B232   56               PUSH ESI
 *  0026B233   57               PUSH EDI
 *  0026B234   8D041A           LEA EAX,DWORD PTR DS:[EDX+EBX]
 *  0026B237   B9 A8000000      MOV ECX,0xA8
 *  0026B23C   8BF3             MOV ESI,EBX
 *  0026B23E   8DBD 60FDFFFF    LEA EDI,DWORD PTR SS:[EBP-0x2A0]
 *  0026B244   F3:A5            REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS>
 *  0026B246   B9 A8000000      MOV ECX,0xA8
 *  0026B24B   8BF0             MOV ESI,EAX
 *  0026B24D   8BFB             MOV EDI,EBX
 *  0026B24F   F3:A5            REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS>
 *  0026B251   81C2 A0020000    ADD EDX,0x2A0
 *  0026B257   B9 A8000000      MOV ECX,0xA8
 *  0026B25C   8DB5 60FDFFFF    LEA ESI,DWORD PTR SS:[EBP-0x2A0]
 *  0026B262   8BF8             MOV EDI,EAX
 *  0026B264   F3:A5            REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS>
 *  0026B266   81FA 40830000    CMP EDX,0x8340
 *  0026B26C  ^7C C6            JL SHORT .0026B234
 *  0026B26E   8BCB             MOV ECX,EBX
 *  0026B270   E8 EBC7FDFF      CALL .00247A60
 *  0026B275   FF75 0C          PUSH DWORD PTR SS:[EBP+0xC]
 *  0026B278   8B35 D8525000    MOV ESI,DWORD PTR DS:[0x5052D8]          ; msvcr110.sprintf
 *  0026B27E   68 805C5000      PUSH .00505C80                           ; ASCII "%s"
 *  0026B283   53               PUSH EBX
 *  0026B284   FFD6             CALL ESI
 *  0026B286   FF75 10          PUSH DWORD PTR SS:[EBP+0x10]
 *  0026B289   8D83 00020000    LEA EAX,DWORD PTR DS:[EBX+0x200]
 *  0026B28F   68 805C5000      PUSH .00505C80                           ; ASCII "%s"
 *  0026B294   50               PUSH EAX
 *  0026B295   FFD6             CALL ESI
 *  0026B297   83C4 18          ADD ESP,0x18
 *  0026B29A   8BC3             MOV EAX,EBX
 *  0026B29C   5F               POP EDI
 *  0026B29D   5E               POP ESI
 *  0026B29E   5B               POP EBX
 *  0026B29F   8BE5             MOV ESP,EBP
 *  0026B2A1   5D               POP EBP
 *  0026B2A2   C3               RETN
 *  0026B2A3   CC               INT3
 *  0026B2A4   CC               INT3
 *  0026B2A5   CC               INT3
 *  0026B2A6   CC               INT3
 */
void SpecialHook5pb3(DWORD esp_base, HookParam *, BYTE index, DWORD *data, DWORD *split, DWORD *len)
{
  // Text in arg1, name in arg2
  if (LPCSTR text = (LPCSTR)argof(index+1, esp_base))
    if (*text) {
      if (index)  // trim spaces in character name
        while (*text == ' ') text++;
      size_t sz = ::strlen(text);
      if (index)
        while (sz && text[sz-1] == ' ') sz--;
      *data = (DWORD)text;
      *len = sz;
      *split = FIXED_SPLIT_VALUE << index;
    }
}
bool Insert5pbHook3()
{
  const BYTE bytes[] = { // function starts 
    0x55,            // 0025A130   55               PUSH EBP
    0x8b,0xec,       // 0025A131   8BEC             MOV EBP,ESP
    0x56,            // 0025A133   56               PUSH ESI
    0xff,0x75, 0x0c, // 0025A134   FF75 0C          PUSH DWORD PTR SS:[EBP+0xC]
    0x8b,0xf1,       // 0025A137   8BF1             MOV ESI,ECX
    0xff,0x75, 0x08, // 0025A139   FF75 08          PUSH DWORD PTR SS:[EBP+0x8]
    0x8d,0x46, 0x08, // 0025A13C   8D46 08          LEA EAX,DWORD PTR DS:[ESI+0x8]
    0x50,            // 0025A13F   50               PUSH EAX
    0xe8             // 0025A140   E8 DB100100      CALL .0026B220
  };
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_limit_);
  //GROWL_DWORD3(addr, module_base_,module_limit_);
  if (!addr) {
    ConsoleOutput("vnreng:5pb2: pattern not found");
    return false;
  }

  HookParam hp = {};
  hp.address = addr;
  hp.type = USING_STRING|NO_CONTEXT;
  hp.text_fun = SpecialHook5pb3;
  hp.extra_text_count = 1; // extract character name in arg1
  hp.filter_fun = NewLineCharToSpaceFilter; // replace '\n' by ' '
  ConsoleOutput("vnreng: INSERT 5pb3");
  NewHook(hp, "5pb3");
  // GDI functions are not used by 5pb games anyway.
  //ConsoleOutput("vnreng:5pb: disable GDI hooks");
  //DisableGDIHooks();
  return true;
}
} // unnamed namespace

bool Insert5pbHook()
{
  bool ok = Insert5pbHook1();
  ok = Insert5pbHook2() || ok;
  ok = Insert5pbHook3() || ok;
  return ok;
}

/** 12/23/2014 jichi: Mink games (not sure the engine name)
 *  Sample game:
 *  - [130111] [Mink EGO] お�ちも�にはぜったい言えなぁ�ぁ�つなこと�-- /HB-4*0:64@45164A
 *  - [141219] [Mink] しすた�・すきーむ3
 *
 *  Observations from sisters3:
 *  - GetGlyphOutlineA can get text, but it is cached.
 *  - It's caller's first argument is the correct text, but I failed to find where it is called
 *  - Debugging text in memory caused looping
 *
 *  /HB-4*0:64@45164A
 *  - addr: 0x45164a
 *  - length_offset: 1
 *  - split: 0x64
 *  - off: 0xfffffff8 = -8
 *  - type: 0x18
 *
 *  Observations from Onechan:
 *  - There are lots of threads
 *  - The one with -1 split value is correct, but not sure for all games
 *  - The result texts still contain garbage, but can be split using return values.
 *
 *  00451611   e9 ee000000      jmp .00451704
 *  00451616   8b45 0c          mov eax,dword ptr ss:[ebp+0xc]
 *  00451619   3bc3             cmp eax,ebx
 *  0045161b   75 2b            jnz short .00451648
 *  0045161d   e8 a9340000      call .00454acb
 *  00451622   53               push ebx
 *  00451623   53               push ebx
 *  00451624   53               push ebx
 *  00451625   53               push ebx
 *  00451626   53               push ebx
 *  00451627   c700 16000000    mov dword ptr ds:[eax],0x16
 *  0045162d   e8 16340000      call .00454a48
 *  00451632   83c4 14          add esp,0x14
 *  00451635   385d f4          cmp byte ptr ss:[ebp-0xc],bl
 *  00451638   74 07            je short .00451641
 *  0045163a   8b45 f0          mov eax,dword ptr ss:[ebp-0x10]
 *  0045163d   8360 70 fd       and dword ptr ds:[eax+0x70],0xfffffffd
 *  00451641   33c0             xor eax,eax
 *  00451643   e9 bc000000      jmp .00451704
 *  00451648   3818             cmp byte ptr ds:[eax],bl
 *  0045164a   75 14            jnz short .00451660         ; jichi: hook here
 *  0045164c   385d f4          cmp byte ptr ss:[ebp-0xc],bl
 *  0045164f   74 07            je short .00451658
 *  00451651   8b45 f0          mov eax,dword ptr ss:[ebp-0x10]
 *  00451654   8360 70 fd       and dword ptr ds:[eax+0x70],0xfffffffd
 *  00451658   8b45 08          mov eax,dword ptr ss:[ebp+0x8]
 *  0045165b   e9 a4000000      jmp .00451704
 *  00451660   56               push esi
 *  00451661   8b75 08          mov esi,dword ptr ss:[ebp+0x8]
 *  00451664   3bf3             cmp esi,ebx
 *  00451666   75 28            jnz short .00451690
 *  00451668   e8 5e340000      call .00454acb
 *  0045166d   53               push ebx
 *  0045166e   53               push ebx
 *  0045166f   53               push ebx
 *  00451670   53               push ebx
 *  00451671   53               push ebx
 *  00451672   c700 16000000    mov dword ptr ds:[eax],0x16
 *  00451678   e8 cb330000      call .00454a48
 *  0045167d   83c4 14          add esp,0x14
 *  00451680   385d f4          cmp byte ptr ss:[ebp-0xc],bl
 *  00451683   74 07            je short .0045168c
 *  00451685   8b45 f0          mov eax,dword ptr ss:[ebp-0x10]
 *  00451688   8360 70 fd       and dword ptr ds:[eax+0x70],0xfffffffd
 *  0045168c   33c0             xor eax,eax
 *  0045168e   eb 73            jmp short .00451703
 *  00451690   57               push edi
 *  00451691   50               push eax
 *  00451692   8bfe             mov edi,esi
 *  00451694   e8 a7600000      call .00457740
 *  00451699   8975 f8          mov dword ptr ss:[ebp-0x8],esi
 *  0045169c   2945 f8          sub dword ptr ss:[ebp-0x8],eax
 *  0045169f   56               push esi
 *  004516a0   e8 9b600000      call .00457740
 *  004516a5   0345 f8          add eax,dword ptr ss:[ebp-0x8]
 *  004516a8   59               pop ecx
 *  004516a9   59               pop ecx
 *  004516aa   381e             cmp byte ptr ds:[esi],bl
 *  004516ac   74 46            je short .004516f4
 *  004516ae   2b75 0c          sub esi,dword ptr ss:[ebp+0xc]
 *  004516b1   3bf8             cmp edi,eax
 *  004516b3   77 3f            ja short .004516f4
 *  004516b5   8a17             mov dl,byte ptr ds:[edi]
 *  004516b7   8b4d 0c          mov ecx,dword ptr ss:[ebp+0xc]
 *  004516ba   8855 ff          mov byte ptr ss:[ebp-0x1],dl
 *  004516bd   3ad3             cmp dl,bl
 *  004516bf   74 11            je short .004516d2
 *  004516c1   8a11             mov dl,byte ptr ds:[ecx]
 *  004516c3   3ad3             cmp dl,bl
 *  004516c5   74 40            je short .00451707
 *  004516c7   38140e           cmp byte ptr ds:[esi+ecx],dl
 *  004516ca   75 06            jnz short .004516d2
 *  004516cc   41               inc ecx
 *  004516cd   381c0e           cmp byte ptr ds:[esi+ecx],bl
 *  004516d0  ^75 ef            jnz short .004516c1
 *  004516d2   3819             cmp byte ptr ds:[ecx],bl
 *  004516d4   74 31            je short .00451707
 *  004516d6   0fb64d ff        movzx ecx,byte ptr ss:[ebp-0x1]
 *  004516da   8b55 ec          mov edx,dword ptr ss:[ebp-0x14]
 *  004516dd   8a4c11 1d        mov cl,byte ptr ds:[ecx+edx+0x1d]
 */

#if 0 // hook to the caller of dynamic GetGlyphOutlineA
/**
 *  @param  addr  function address
 *  @param  frame  real address of the function, supposed to be the same as addr
 *  @param  stack  address of current stack - 4
 *  @return  If suceess
 */
static bool InsertMinkDynamicHook(LPVOID fun, DWORD frame, DWORD stack)
{
  CC_UNUSED(frame);
  if (fun != ::GetGlyphOutlineA)
    return false;
  DWORD addr = *(DWORD *)(stack + 4);
  if (!addr) {
    ConsoleOutput("vnreng:Mink: missing function return addr, this should never happen");
    return true;
  }
  addr = MemDbg::findEnclosingAlignedFunction(addr, 0x200); // range is around 0x120
  if (!addr) {
    ConsoleOutput("vnreng:Mink: failed to caller address");
    return true;
  }

  HookParam hp = {};
  hp.address = addr; // hook to the beginning of the caller function
  hp.offset = 4 * 1; // text character is in arg1
  hp.length_offset = 1; // only 1 character
  hp.type = BIG_ENDIAN;
  NewHook(hp, "Mink");

  ConsoleOutput("vnreng:Mink: disable GDI hooks");
  DisableGDIHooks();
  return true;
}
#endif // 0

static void SpecialHookMink(DWORD esp_base, HookParam *, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  //DWORD addr = *(DWORD *)(esp_base + hp->offset); // default value
  DWORD addr = regof(eax, esp_base);
  if (!IthGetMemoryRange((LPVOID)(addr), 0, 0))
    return;
  DWORD ch = *(DWORD *)addr;
  DWORD size = LeadByteTable[ch & 0xff]; // Slightly faster than IsDBCSLeadByte
  if (size == 1 && ::ispunct(ch & 0xff)) // skip ascii punctuations, since garbage is like ":text:"
    return;

  *len = size;
  *data = ch;

  // Issue: still have lots of garbage
  *split = *(DWORD *)(esp_base + 0x64);
  //*split = *(DWORD *)(esp_base + 0x48);
}

bool InsertMinkHook()
{
  const BYTE bytes[] = {
    0x38,0x18,              // 00451648   3818             cmp byte ptr ds:[eax],bl
    0x75, 0x14,             // 0045164a   75 14            jnz short .00451660         ; jichi: hook here
    0x38,0x5d, 0xf4,        // 0045164c   385d f4          cmp byte ptr ss:[ebp-0xc],bl
    0x74, 0x07,             // 0045164f   74 07            je short .00451658
    0x8b,0x45, 0xf0,        // 00451651   8b45 f0          mov eax,dword ptr ss:[ebp-0x10]
    0x83,0x60, 0x70, 0xfd,  // 00451654   8360 70 fd       and dword ptr ds:[eax+0x70],0xfffffffd
    0x8b,0x45, 0x08         // 00451658   8b45 08          mov eax,dword ptr ss:[ebp+0x8]
  };
  enum { addr_offset = 2 };
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_limit_);
  //ULONG addr = 0x45164a;
  //ULONG addr = 0x451648;
  //ULONG addr = 0x4521a8;
  //GROWL_DWORD(addr);
  if (!addr) {
    ConsoleOutput("vnreng:Mink: pattern not found");
    return false;
  }

  HookParam hp = {};
  hp.address = addr + addr_offset;
  hp.offset = pusha_eax_off - 4; // -8
  hp.length_offset = 1;
  hp.split = 0x64;
  hp.type = USING_SPLIT|DATA_INDIRECT; // 0x18
  hp.text_fun = SpecialHookMink;
  ConsoleOutput("vnreng: INSERT Mink");
  NewHook(hp, "Mink");

  //ConsoleOutput("vnreng:Mink: disable GDI hooks");
  //DisableGDIHooks();
  return true;
}

/** jichi 12/25/2014: Leaf/AQUAPLUS
 *  Sample game: [141224] [AQUAPLUS] WHITE ALBUM2 ミニアフタースト�リー
 *  Debug method: hardware break found text
 *  The text address is fixed.
 *  There are three matched functions.
 *  It can find both character name and scenario.
 *
 *  The scenario text contains "\n" or "\k".
 *
 *  0045145C   CC               INT3
 *  0045145D   CC               INT3
 *  0045145E   CC               INT3
 *  0045145F   CC               INT3
 *  00451460   D9EE             FLDZ
 *  00451462   56               PUSH ESI
 *  00451463   8B7424 08        MOV ESI,DWORD PTR SS:[ESP+0x8]
 *  00451467   D95E 0C          FSTP DWORD PTR DS:[ESI+0xC]
 *  0045146A   57               PUSH EDI
 *  0045146B   8BF9             MOV EDI,ECX
 *  0045146D   8B97 B0A00000    MOV EDX,DWORD PTR DS:[EDI+0xA0B0]
 *  00451473   33C0             XOR EAX,EAX
 *  00451475   3BD0             CMP EDX,EAX
 *  00451477   C706 05000000    MOV DWORD PTR DS:[ESI],0x5
 *  0045147D   C746 04 03000000 MOV DWORD PTR DS:[ESI+0x4],0x3
 *  00451484   8946 10          MOV DWORD PTR DS:[ESI+0x10],EAX
 *  00451487   8946 08          MOV DWORD PTR DS:[ESI+0x8],EAX
 *  0045148A   7F 0D            JG SHORT .00451499
 *  0045148C   8987 B0A00000    MOV DWORD PTR DS:[EDI+0xA0B0],EAX
 *  00451492   5F               POP EDI
 *  00451493   8BC6             MOV EAX,ESI
 *  00451495   5E               POP ESI
 *  00451496   C2 0400          RETN 0x4
 *  00451499   8D0492           LEA EAX,DWORD PTR DS:[EDX+EDX*4]
 *  0045149C   53               PUSH EBX
 *  0045149D   8B9C87 B08C0000  MOV EBX,DWORD PTR DS:[EDI+EAX*4+0x8CB0]
 *  004514A4   8D0487           LEA EAX,DWORD PTR DS:[EDI+EAX*4]
 *  004514A7   55               PUSH EBP
 *  004514A8   8D6B FF          LEA EBP,DWORD PTR DS:[EBX-0x1]
 *  004514AB   B9 04000000      MOV ECX,0x4
 *  004514B0   3BE9             CMP EBP,ECX
 *  004514B2   0F87 10020000    JA .004516C8
 *  004514B8   FF24AD E8164500  JMP DWORD PTR DS:[EBP*4+0x4516E8]
 *  004514BF   8B80 C08C0000    MOV EAX,DWORD PTR DS:[EAX+0x8CC0]
 *  004514C5   8D0480           LEA EAX,DWORD PTR DS:[EAX+EAX*4]
 *  004514C8   03C0             ADD EAX,EAX
 *  004514CA   0FBE9400 6416BC0>MOVSX EDX,BYTE PTR DS:[EAX+EAX+0xBC1664]
 *  004514D2   03C0             ADD EAX,EAX
 *  004514D4   8D5A FF          LEA EBX,DWORD PTR DS:[EDX-0x1]
 *  004514D7   3BD9             CMP EBX,ECX
 *  004514D9   0F87 B9000000    JA .00451598
 *  004514DF   FF249D FC164500  JMP DWORD PTR DS:[EBX*4+0x4516FC]
 *  004514E6   0FB688 6516BC00  MOVZX ECX,BYTE PTR DS:[EAX+0xBC1665]
 *  004514ED   FF8F B0A00000    DEC DWORD PTR DS:[EDI+0xA0B0]
 *  004514F3   5D               POP EBP
 *  004514F4   5B               POP EBX
 *  004514F5   5F               POP EDI
 *  004514F6   894E 10          MOV DWORD PTR DS:[ESI+0x10],ECX
 *  004514F9   8BC6             MOV EAX,ESI
 *  004514FB   5E               POP ESI
 *  004514FC   C2 0400          RETN 0x4
 *  004514FF   0FBF90 6616BC00  MOVSX EDX,WORD PTR DS:[EAX+0xBC1666]
 *  00451506   FF8F B0A00000    DEC DWORD PTR DS:[EDI+0xA0B0]
 *  0045150C   5D               POP EBP
 *  0045150D   5B               POP EBX
 *  0045150E   5F               POP EDI
 *  0045150F   8956 10          MOV DWORD PTR DS:[ESI+0x10],EDX
 *  00451512   8BC6             MOV EAX,ESI
 *  00451514   5E               POP ESI
 *  00451515   C2 0400          RETN 0x4
 *  00451518   8B80 6816BC00    MOV EAX,DWORD PTR DS:[EAX+0xBC1668]
 *  0045151E   FF8F B0A00000    DEC DWORD PTR DS:[EDI+0xA0B0]
 *  00451524   5D               POP EBP
 *  00451525   5B               POP EBX
 *  00451526   8946 10          MOV DWORD PTR DS:[ESI+0x10],EAX
 *  00451529   5F               POP EDI
 *  0045152A   8BC6             MOV EAX,ESI
 *  0045152C   5E               POP ESI
 *  0045152D   C2 0400          RETN 0x4
 *  00451530   D980 6C16BC00    FLD DWORD PTR DS:[EAX+0xBC166C]
 *  00451536   FF8F B0A00000    DEC DWORD PTR DS:[EDI+0xA0B0]
 *  0045153C   5D               POP EBP
 *  0045153D   D95E 0C          FSTP DWORD PTR DS:[ESI+0xC]
 *  00451540   5B               POP EBX
 *  00451541   5F               POP EDI
 *  00451542   894E 04          MOV DWORD PTR DS:[ESI+0x4],ECX
 *  00451545   8BC6             MOV EAX,ESI
 *  00451547   5E               POP ESI
 *  00451548   C2 0400          RETN 0x4
 *  0045154B   8B80 7016BC00    MOV EAX,DWORD PTR DS:[EAX+0xBC1670]
 *  00451551   8D58 01          LEA EBX,DWORD PTR DS:[EAX+0x1]
 *  00451554   8A10             MOV DL,BYTE PTR DS:[EAX]
 *  00451556   40               INC EAX
 *  00451557   84D2             TEST DL,DL
 *  00451559  ^75 F9            JNZ SHORT .00451554
 *  0045155B   2BC3             SUB EAX,EBX
 *  0045155D   8D58 01          LEA EBX,DWORD PTR DS:[EAX+0x1]
 *  00451560   53               PUSH EBX
 *  00451561   6A 00            PUSH 0x0
 *  00451563   53               PUSH EBX
 *  00451564   6A 00            PUSH 0x0
 *  00451566   FF15 74104A00    CALL DWORD PTR DS:[0x4A1074]             ; kernel32.GetProcessHeap
 *  0045156C   50               PUSH EAX
 *  0045156D   FF15 B4104A00    CALL DWORD PTR DS:[0x4A10B4]             ; ntdll.RtlAllocateHeap
 *  00451573   50               PUSH EAX
 *  00451574   E8 373F0200      CALL .004754B0
 *  00451579   8B8F B0A00000    MOV ECX,DWORD PTR DS:[EDI+0xA0B0]
 *  0045157F   8D0C89           LEA ECX,DWORD PTR DS:[ECX+ECX*4]
 *  00451582   8B8C8F C08C0000  MOV ECX,DWORD PTR DS:[EDI+ECX*4+0x8CC0]
 *  00451589   8D1489           LEA EDX,DWORD PTR DS:[ECX+ECX*4]
 *  0045158C   8B0C95 7016BC00  MOV ECX,DWORD PTR DS:[EDX*4+0xBC1670]
 *  00451593   E9 0C010000      JMP .004516A4
 *  00451598   52               PUSH EDX
 *  00451599   68 A8644A00      PUSH .004A64A8
 *  0045159E   E9 2B010000      JMP .004516CE
 *  004515A3   8D9492 2D230000  LEA EDX,DWORD PTR DS:[EDX+EDX*4+0x232D]
 *  004515AA   8B1C97           MOV EBX,DWORD PTR DS:[EDI+EDX*4]
 *  004515AD   85DB             TEST EBX,EBX
 *  004515AF   0F8C 23010000    JL .004516D8
 *  004515B5   8B80 C08C0000    MOV EAX,DWORD PTR DS:[EAX+0x8CC0]
 *  004515BB   99               CDQ
 *  004515BC   BD 1A000000      MOV EBP,0x1A
 *  004515C1   F7FD             IDIV EBP
 *  004515C3   C1E2 04          SHL EDX,0x4
 *  004515C6   03D3             ADD EDX,EBX
 *  004515C8   85C0             TEST EAX,EAX
 *  004515CA   74 1C            JE SHORT .004515E8
 *  004515CC   D98497 34A70000  FLD DWORD PTR DS:[EDI+EDX*4+0xA734]
 *  004515D3   FF8F B0A00000    DEC DWORD PTR DS:[EDI+0xA0B0]
 *  004515D9   5D               POP EBP
 *  004515DA   D95E 0C          FSTP DWORD PTR DS:[ESI+0xC]
 *  004515DD   5B               POP EBX
 *  004515DE   5F               POP EDI
 *  004515DF   894E 04          MOV DWORD PTR DS:[ESI+0x4],ECX
 *  004515E2   8BC6             MOV EAX,ESI
 *  004515E4   5E               POP ESI
 *  004515E5   C2 0400          RETN 0x4
 *  004515E8   8B8497 B4A00000  MOV EAX,DWORD PTR DS:[EDI+EDX*4+0xA0B4]
 *  004515EF   FF8F B0A00000    DEC DWORD PTR DS:[EDI+0xA0B0]
 *  004515F5   5D               POP EBP
 *  004515F6   5B               POP EBX
 *  004515F7   8946 10          MOV DWORD PTR DS:[ESI+0x10],EAX
 *  004515FA   5F               POP EDI
 *  004515FB   8BC6             MOV EAX,ESI
 *  004515FD   5E               POP ESI
 *  004515FE   C2 0400          RETN 0x4
 *  00451601   8B88 C08C0000    MOV ECX,DWORD PTR DS:[EAX+0x8CC0]
 *  00451607   D980 BC8C0000    FLD DWORD PTR DS:[EAX+0x8CBC]
 *  0045160D   894E 10          MOV DWORD PTR DS:[ESI+0x10],ECX
 *  00451610   D95E 0C          FSTP DWORD PTR DS:[ESI+0xC]
 *  00451613   8B88 B88C0000    MOV ECX,DWORD PTR DS:[EAX+0x8CB8]
 *  00451619   894E 08          MOV DWORD PTR DS:[ESI+0x8],ECX
 *  0045161C   8D9492 2D230000  LEA EDX,DWORD PTR DS:[EDX+EDX*4+0x232D]
 *  00451623   8B0C97           MOV ECX,DWORD PTR DS:[EDI+EDX*4]
 *  00451626   894E 04          MOV DWORD PTR DS:[ESI+0x4],ECX
 *  00451629   33C9             XOR ECX,ECX
 *  0045162B   8988 B08C0000    MOV DWORD PTR DS:[EAX+0x8CB0],ECX
 *  00451631   8988 B48C0000    MOV DWORD PTR DS:[EAX+0x8CB4],ECX
 *  00451637   8988 B88C0000    MOV DWORD PTR DS:[EAX+0x8CB8],ECX
 *  0045163D   5D               POP EBP
 *  0045163E   8988 BC8C0000    MOV DWORD PTR DS:[EAX+0x8CBC],ECX
 *  00451644   8988 C08C0000    MOV DWORD PTR DS:[EAX+0x8CC0],ECX
 *  0045164A   FF8F B0A00000    DEC DWORD PTR DS:[EDI+0xA0B0]
 *  00451650   5B               POP EBX
 *  00451651   5F               POP EDI
 *  00451652   8BC6             MOV EAX,ESI
 *  00451654   5E               POP ESI
 *  00451655   C2 0400          RETN 0x4
 *  00451658   8B90 C08C0000    MOV EDX,DWORD PTR DS:[EAX+0x8CC0]
 *  0045165E   8B8497 14080000  MOV EAX,DWORD PTR DS:[EDI+EDX*4+0x814]  ; jichi: text in eax
 *  00451665   8D58 01          LEA EBX,DWORD PTR DS:[EAX+0x1]  ; jichi: hook here would crash
 *  00451668   8A10             MOV DL,BYTE PTR DS:[EAX]        ; jichi: text accessed here in eax
 *  0045166A   40               INC EAX
 *  0045166B   84D2             TEST DL,DL
 *  0045166D  ^75 F9            JNZ SHORT .00451668
 *  0045166F   2BC3             SUB EAX,EBX     ; jichi: hook here, text in ebx-1
 *  00451671   8D58 01          LEA EBX,DWORD PTR DS:[EAX+0X1]
 *  00451674   53               PUSH EBX
 *  00451675   6A 00            PUSH 0x0
 *  00451677   53               PUSH EBX
 *  00451678   6A 00            PUSH 0x0
 *  0045167A   FF15 74104A00    CALL DWORD PTR DS:[0x4A1074]             ; kernel32.GetProcessHeap
 *  00451680   50               PUSH EAX
 *  00451681   FF15 B4104A00    CALL DWORD PTR DS:[0x4A10B4]             ; ntdll.RtlAllocateHeap
 *  00451687   50               PUSH EAX
 *  00451688   E8 233E0200      CALL .004754B0
 *  0045168D   8B8F B0A00000    MOV ECX,DWORD PTR DS:[EDI+0xA0B0]
 *  00451693   8D0C89           LEA ECX,DWORD PTR DS:[ECX+ECX*4]
 *  00451696   8B948F C08C0000  MOV EDX,DWORD PTR DS:[EDI+ECX*4+0x8CC0]
 *  0045169D   8B8C97 14080000  MOV ECX,DWORD PTR DS:[EDI+EDX*4+0x814]  ; jichi: text in ecx
 *  004516A4   53               PUSH EBX
 *  004516A5   51               PUSH ECX
 *  004516A6   50               PUSH EAX
 *  004516A7   8946 08          MOV DWORD PTR DS:[ESI+0x8],EAX
 *  004516AA   E8 31410200      CALL .004757E0
 *  004516AF   83C4 18          ADD ESP,0x18
 *  004516B2   FF8F B0A00000    DEC DWORD PTR DS:[EDI+0xA0B0]
 *  004516B8   5D               POP EBP
 *  004516B9   5B               POP EBX
 *  004516BA   5F               POP EDI
 *  004516BB   C746 04 05000000 MOV DWORD PTR DS:[ESI+0x4],0x5
 *  004516C2   8BC6             MOV EAX,ESI
 *  004516C4   5E               POP ESI
 *  004516C5   C2 0400          RETN 0x4
 *  004516C8   53               PUSH EBX
 *  004516C9   68 8C644A00      PUSH .004A648C
 *  004516CE   6A 00            PUSH 0x0
 *  004516D0   E8 6BABFFFF      CALL .0044C240
 *  004516D5   83C4 0C          ADD ESP,0xC
 *  004516D8   FF8F B0A00000    DEC DWORD PTR DS:[EDI+0xA0B0]
 *  004516DE   5D               POP EBP
 *  004516DF   5B               POP EBX
 *  004516E0   5F               POP EDI
 *  004516E1   8BC6             MOV EAX,ESI
 *  004516E3   5E               POP ESI
 *  004516E4   C2 0400          RETN 0x4
 *  004516E7   90               NOP
 *  004516E8   BF 144500A3      MOV EDI,0xA3004514
 *  004516ED   15 45005816      ADC EAX,0x16580045
 *  004516F2   45               INC EBP
 *  004516F3   00C8             ADD AL,CL
 *  004516F5   16               PUSH SS
 *  004516F6   45               INC EBP
 *  004516F7   0001             ADD BYTE PTR DS:[ECX],AL
 *  004516F9   16               PUSH SS
 *  004516FA   45               INC EBP
 *  004516FB   00E6             ADD DH,AH
 *  004516FD   14 45            ADC AL,0x45
 *  004516FF   00FF             ADD BH,BH
 *  00451701   14 45            ADC AL,0x45
 *  00451703   0018             ADD BYTE PTR DS:[EAX],BL
 *  00451705   15 45003015      ADC EAX,0x15300045
 *  0045170A   45               INC EBP
 *  0045170B   004B 15          ADD BYTE PTR DS:[EBX+0x15],CL
 *  0045170E   45               INC EBP
 *  0045170F   0083 7C240800    ADD BYTE PTR DS:[EBX+0x8247C],AL
 *  00451715   56               PUSH ESI
 *  00451716   8BF1             MOV ESI,ECX
 *  00451718   74 29            JE SHORT .00451743
 *  0045171A   8B86 B0A00000    MOV EAX,DWORD PTR DS:[ESI+0xA0B0]
 *  00451720   3D FF000000      CMP EAX,0xFF
 *  00451725   7C 15            JL SHORT .0045173C
 *  00451727   68 74644A00      PUSH .004A6474
 *  0045172C   6A 00            PUSH 0x0
 *  0045172E   E8 0DABFFFF      CALL .0044C240
 *  00451733   83C4 08          ADD ESP,0x8
 *  00451736   33C0             XOR EAX,EAX
 *  00451738   5E               POP ESI
 *  00451739   C2 0800          RETN 0x8
 *  0045173C   40               INC EAX
 *  0045173D   8986 B0A00000    MOV DWORD PTR DS:[ESI+0xA0B0],EAX
 *  00451743   8B86 B0A00000    MOV EAX,DWORD PTR DS:[ESI+0xA0B0]
 *  00451749   8D0C80           LEA ECX,DWORD PTR DS:[EAX+EAX*4]
 *  0045174C   8D0C8E           LEA ECX,DWORD PTR DS:[ESI+ECX*4]
 *  0045174F   57               PUSH EDI
 *  00451750   8BB9 B08C0000    MOV EDI,DWORD PTR DS:[ECX+0x8CB0]
 *  00451756   8BD7             MOV EDX,EDI
 *  00451758   83EA 01          SUB EDX,0x1
 *  0045175B   74 70            JE SHORT .004517CD
 *  0045175D   83EA 01          SUB EDX,0x1
 *  00451760   74 1A            JE SHORT .0045177C
 *  00451762   57               PUSH EDI
 *  00451763   68 CC644A00      PUSH .004A64CC
 *  00451768   6A 00            PUSH 0x0
 *  0045176A   E8 D1AAFFFF      CALL .0044C240
 *  0045176F   83C4 0C          ADD ESP,0xC
 *  00451772   5F               POP EDI
 *  00451773   B8 01000000      MOV EAX,0x1
 *  00451778   5E               POP ESI
 *  00451779   C2 0800          RETN 0x8
 *  0045177C   8D9480 2D230000  LEA EDX,DWORD PTR DS:[EAX+EAX*4+0x232D]
 *  00451783   8B3C96           MOV EDI,DWORD PTR DS:[ESI+EDX*4]
 *  00451786   85FF             TEST EDI,EDI
 *  00451788   0F8C C8000000    JL .00451856
 *  0045178E   8B81 C08C0000    MOV EAX,DWORD PTR DS:[ECX+0x8CC0]
 *  00451794   99               CDQ
 *  00451795   B9 1A000000      MOV ECX,0x1A
 *  0045179A   F7F9             IDIV ECX
 *  0045179C   C1E2 04          SHL EDX,0x4
 *  0045179F   03D7             ADD EDX,EDI
 *  004517A1   85C0             TEST EAX,EAX
 *  004517A3   74 13            JE SHORT .004517B8
 *  004517A5   DB4424 0C        FILD DWORD PTR SS:[ESP+0xC]
 *  004517A9   5F               POP EDI
 *  004517AA   8D41 E7          LEA EAX,DWORD PTR DS:[ECX-0x19]
 *  004517AD   D99C96 34A70000  FSTP DWORD PTR DS:[ESI+EDX*4+0xA734]
 *  004517B4   5E               POP ESI
 *  004517B5   C2 0800          RETN 0x8
 *  004517B8   8B4424 0C        MOV EAX,DWORD PTR SS:[ESP+0xC]
 *  004517BC   898496 B4A00000  MOV DWORD PTR DS:[ESI+EDX*4+0xA0B4],EAX
 *  004517C3   5F               POP EDI
 *  004517C4   B8 01000000      MOV EAX,0x1
 *  004517C9   5E               POP ESI
 *  004517CA   C2 0800          RETN 0x8
 *  004517CD   8B89 C08C0000    MOV ECX,DWORD PTR DS:[ECX+0x8CC0]
 *  004517D3   8D0489           LEA EAX,DWORD PTR DS:[ECX+ECX*4]
 *  004517D6   03C0             ADD EAX,EAX
 *  004517D8   0FBE9400 6416BC0>MOVSX EDX,BYTE PTR DS:[EAX+EAX+0xBC1664]
 *  004517E0   03C0             ADD EAX,EAX
 *  004517E2   8D7A FF          LEA EDI,DWORD PTR DS:[EDX-0x1]
 *  004517E5   83FF 04          CMP EDI,0x4
 *  004517E8   77 41            JA SHORT .0045182B
 *  004517EA   FF24BD 60184500  JMP DWORD PTR DS:[EDI*4+0x451860]
 *  004517F1   8A4C24 0C        MOV CL,BYTE PTR SS:[ESP+0xC]
 *  004517F5   8888 6516BC00    MOV BYTE PTR DS:[EAX+0xBC1665],CL
 *  004517FB   EB 3E            JMP SHORT .0045183B
 *  004517FD   66:8B5424 0C     MOV DX,WORD PTR SS:[ESP+0xC]
 *  00451802   66:8990 6616BC00 MOV WORD PTR DS:[EAX+0xBC1666],DX
 *  00451809   EB 30            JMP SHORT .0045183B
 *  0045180B   8B4C24 0C        MOV ECX,DWORD PTR SS:[ESP+0xC]
 *  0045180F   8988 6816BC00    MOV DWORD PTR DS:[EAX+0xBC1668],ECX
 *  00451815   EB 24            JMP SHORT .0045183B
 *  00451817   DB4424 0C        FILD DWORD PTR SS:[ESP+0xC]
 *  0045181B   D998 6C16BC00    FSTP DWORD PTR DS:[EAX+0xBC166C]
 *  00451821   EB 18            JMP SHORT .0045183B
 *  00451823   51               PUSH ECX
 *  00451824   68 BC644A00      PUSH .004A64BC
 *  00451829   EB 06            JMP SHORT .00451831
 *  0045182B   52               PUSH EDX
 *  0045182C   68 A8644A00      PUSH .004A64A8
 *  00451831   6A 00            PUSH 0x0
 *  00451833   E8 08AAFFFF      CALL .0044C240
 *  00451838   83C4 0C          ADD ESP,0xC
 *  0045183B   8B86 B0A00000    MOV EAX,DWORD PTR DS:[ESI+0xA0B0]
 *  00451841   8D1480           LEA EDX,DWORD PTR DS:[EAX+EAX*4]
 *  00451844   8B8496 C08C0000  MOV EAX,DWORD PTR DS:[ESI+EDX*4+0x8CC0]
 *  0045184B   6A 00            PUSH 0x0
 *  0045184D   50               PUSH EAX
 *  0045184E   E8 FDF0FFFF      CALL .00450950
 *  00451853   83C4 08          ADD ESP,0x8
 *  00451856   5F               POP EDI
 *  00451857   B8 01000000      MOV EAX,0x1
 *  0045185C   5E               POP ESI
 *  0045185D   C2 0800          RETN 0x8
 *  00451860   F1               INT1
 *  00451861   17               POP SS                                   ; Modification of segment register
 *  00451862   45               INC EBP
 *  00451863   00FD             ADD CH,BH
 *  00451865   17               POP SS                                   ; Modification of segment register
 *  00451866   45               INC EBP
 *  00451867   000B             ADD BYTE PTR DS:[EBX],CL
 *  00451869   1845 00          SBB BYTE PTR SS:[EBP],AL
 *  0045186C   17               POP SS                                   ; Modification of segment register
 *  0045186D   1845 00          SBB BYTE PTR SS:[EBP],AL
 *  00451870   2318             AND EBX,DWORD PTR DS:[EAX]
 *  00451872   45               INC EBP
 *  00451873   00CC             ADD AH,CL
 *  00451875   CC               INT3
 *  00451876   CC               INT3
 *  00451877   CC               INT3
 *  00451878   CC               INT3
 *  00451879   CC               INT3
 *  0045187A   CC               INT3
 *  0045187B   CC               INT3
 *  0045187C   CC               INT3
 *  0045187D   CC               INT3
 *
 *  EAX 00000038
 *  ECX 00000004 ; jichi: fixed
 *  EDX 00000000 ; jichi: fixed
 *  EBX 00321221
 *  ESP 0012FD98
 *  EBP 00000002
 *  ESI 0012FDC4
 *  EDI 079047E0
 *  EIP 00451671 .00451671
 */
static void SpecialHookLeaf(DWORD esp_base, HookParam *, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  DWORD text = regof(ebx, esp_base) - 1; // = ebx -1
  *data = text;
  *len = ::strlen((LPCSTR)text);
  *split = FIXED_SPLIT_VALUE; // only caller's address use as split
}
// Remove both \n and \k
static bool LeafFilter(LPVOID data, DWORD *size, HookParam *, BYTE)
{
  LPSTR text = (LPSTR)data;
  if (::memchr(text, '\\', *size)) {
    StringFilter(text, reinterpret_cast<size_t *>(size), "\\n", 2);
    StringFilter(text, reinterpret_cast<size_t *>(size), "\\k", 2);
  }
  return true;
}
bool InsertLeafHook()
{
  const BYTE bytes[] = {
    0x8b,0x90, XX4,      // 00451658   8b90 c08c0000    mov edx,dword ptr ds:[eax+0x8cc0]
    0x8b,0x84,0x97, XX4, // 0045165e   8b8497 14080000  mov eax,dword ptr ds:[edi+edx*4+0x814]
    // The above is needed as there are other matches
    0x8d,0x58, 0x01,     // 00451665   8d58 01          lea ebx,dword ptr ds:[eax+0x1] ; jichi: hook here would crash because of jump
    0x8a,0x10,           // 00451668   8a10             mov dl,byte ptr ds:[eax]    ; jichi: text accessed here in eax
    0x40,                // 0045166a   40               inc eax
    0x84,0xd2,           // 0045166b   84d2             test dl,dl
    0x75, 0xf9,          // 0045166d  ^75 f9            jnz short .00451668
    0x2b,0xc3,           // 0045166f   2bc3             sub eax,ebx     ; jichi: hook here, text in ebx-1
    0x8d,0x58, 0x01      // 00451671   8d58 01           lea ebx,dword ptr ds:[eax+0x1]
    //0x53,               // 00451674   53               push ebx
    //0x6a, 0x00,         // 00451675   6a 00            push 0x0
    //0x53,               // 00451677   53               push ebx
    //0x6a, 0x00,         // 00451678   6a 00            push 0x0
    //0xff,0x15           // 0045167a   ff15 74104a00    call dword ptr ds:[0x4a1074]             ; kernel32.getprocessheap
  };
  ULONG addr = MemDbg::matchBytes(bytes, sizeof(bytes), module_base_, module_limit_);
  enum { addr_offset = 0x0045166f - 0x00451658 };
  //GROWL_DWORD(addr);
  if (!addr) {
    ConsoleOutput("vnreng:Leaf: pattern not found");
    return false;
  }

  HookParam hp = {};
  hp.address = addr + addr_offset;
  //hp.offset = pusha_eax_off - 4;
  hp.type = USING_STRING|USING_SPLIT; // use top of the stack as split
  hp.text_fun = SpecialHookLeaf;
  //hp.filter_fun = NewLineStringFilter; // remove two characters of "\\n"
  hp.filter_fun = LeafFilter; // remove two characters
  ConsoleOutput("vnreng: INSERT Leaf");
  NewHook(hp, "Leaf");

  //ConsoleOutput("vnreng:Leaf: disable GDI hooks");
  //DisableGDIHooks();
  return true;
}

/**
 *  mireado 8/01/2016: Add NekoPack hook
 *
 *  See: http://sakuradite.com/topic/1470
 *  https://arallab.hided.net/board_codetalk/2605967
 *
 *  [Pure More] 少女アクティビティ_trial 1.01
 *
 *  base: 0x4000000
 *	binary pattern :: 558BEC81C4C4FDFFFFB8
 */

bool InsertNekopackHook()
{
  const BYTE bytes[] = {
    0x55,			// 0069637C  /$  55            PUSH EBP
    0x8b,0xec,		// 0069637D  |.  8BEC          MOV EBP,ESP
    0x81,0xc4, 0xC4,0xFD,0xFF,0xFF,	// 0069637F  |.  81C4 C4FDFFFF ADD ESP,-23C
    0xb8, XX4,		// 00696385  |.  B8 A8FF7900   MOV EAX,OFFSET 0079FFA8
    0x53,       // 0069638A  |.  53            PUSH EBX
    0x56,       // 0069638B  |.  56            PUSH ESI
    0x57,       // 0069638C  |.  57            PUSH EDI
    0x8b,0x5d, 0x08       // 0069638D  |.  8B5D 08       MOV EBX,DWORD PTR SS:[ARG.1]
  };
  ULONG range = min(module_limit_ - module_base_, MAX_REL_ADDR);
  ULONG addr = MemDbg::matchBytes(bytes, sizeof(bytes), module_base_, module_base_ + range);
  enum { addr_offset = 0 }; // distance to the beginning of the function, which is 0x55 (push ebp)
  //GROWL(reladdr);
  if (!addr) {
    ConsoleOutput("vnreng:NekoPack: pattern not found");
    return false;
  }
  addr += addr_offset;
  //GROWL(addr);
  enum { push_ebp = 0x55 }; // beginning of the function
  if (*(BYTE *)addr != push_ebp) {
    ConsoleOutput("vnreng:NekoPack: beginning of the function not found");
    return false;
  }

  HookParam hp = {};
  hp.address = addr;
  hp.offset = 8;
  hp.type = USING_STRING;

  ConsoleOutput("vnreng: INSERT NekoPack");
  NewHook(hp, "NekoPack");

  // Disable GDIHook(um.. ?), which is cached and hence missing characters.
  //ConsoleOutput("vnreng:NekoPack: disable GDI hooks");
  //DisableGDIHooks();
  return true;
}

/** jichi 12/27/2014 LunaSoft
 * Sample game: [141226] [LunaSoft] 悪堕ラビリンス -- /hsn8@46C5EF
 *
 * /hsn8@46C5EF
 * - addr: 0x46C5EF
 * - off: 8
 * - type: 1025 = 0x401
 *
 * - 0046c57e   cc               int3
 * - 0046c57f   cc               int3
 * - 0046c580   55               push ebp       ; jichi: text in arg1
 * - 0046c581   8bec             mov ebp,esp
 * - 0046c583   83ec 08          sub esp,0x8
 * - 0046c586   894d f8          mov dword ptr ss:[ebp-0x8],ecx
 * - 0046c589   8b4d f8          mov ecx,dword ptr ss:[ebp-0x8]
 * - 0046c58c   83c1 1c          add ecx,0x1c
 * - 0046c58f   e8 2cebf9ff      call .0040b0c0
 * - 0046c594   8b00             mov eax,dword ptr ds:[eax]
 * - 0046c596   8945 fc          mov dword ptr ss:[ebp-0x4],eax
 * - 0046c599   837d fc 00       cmp dword ptr ss:[ebp-0x4],0x0
 * - 0046c59d   75 21            jnz short .0046c5c0
 * - 0046c59f   8b4d f8          mov ecx,dword ptr ss:[ebp-0x8]
 * - 0046c5a2   83c1 28          add ecx,0x28
 * - 0046c5a5   e8 16ebf9ff      call .0040b0c0
 * - 0046c5aa   8b08             mov ecx,dword ptr ds:[eax]
 * - 0046c5ac   894d fc          mov dword ptr ss:[ebp-0x4],ecx
 * - 0046c5af   8b55 fc          mov edx,dword ptr ss:[ebp-0x4]
 * - 0046c5b2   52               push edx
 * - 0046c5b3   8b4d f8          mov ecx,dword ptr ss:[ebp-0x8]
 * - 0046c5b6   83c1 28          add ecx,0x28
 * - 0046c5b9   e8 82d9f9ff      call .00409f40
 * - 0046c5be   eb 0f            jmp short .0046c5cf
 * - 0046c5c0   8b45 fc          mov eax,dword ptr ss:[ebp-0x4]
 * - 0046c5c3   50               push eax
 * - 0046c5c4   8b4d f8          mov ecx,dword ptr ss:[ebp-0x8]
 * - 0046c5c7   83c1 1c          add ecx,0x1c
 * - 0046c5ca   e8 71d9f9ff      call .00409f40
 * - 0046c5cf   837d fc 00       cmp dword ptr ss:[ebp-0x4],0x0
 * - 0046c5d3   75 02            jnz short .0046c5d7
 * - 0046c5d5   eb 61            jmp short .0046c638
 * - 0046c5d7   8b4d fc          mov ecx,dword ptr ss:[ebp-0x4]
 * - 0046c5da   e8 b1cdf9ff      call .00409390
 * - 0046c5df   8b4d 08          mov ecx,dword ptr ss:[ebp+0x8]
 * - 0046c5e2   51               push ecx                   ; jichi: text in ecx
 * - 0046c5e3   68 38010000      push 0x138
 * - 0046c5e8   8b55 fc          mov edx,dword ptr ss:[ebp-0x4]
 * - 0046c5eb   83c2 08          add edx,0x8
 * - 0046c5ee   52               push edx
 * - 0046c5ef   ff15 88b24c00    call dword ptr ds:[0x4cb288]  ; msvcr90.strcpy_s, jichi: text accessed here in arg2
 * - 0046c5f5   83c4 0c          add esp,0xc
 * - 0046c5f8   8b45 0c          mov eax,dword ptr ss:[ebp+0xc]
 * - 0046c5fb   50               push eax
 * - 0046c5fc   6a 10            push 0x10
 */
// Remove: \n\s*
// This is dangerous since \n could appear within SJIS
//static bool LunaSoftFilter(LPVOID data, DWORD *size, HookParam *, BYTE)
//{
//  size_t len = *size;
//  char *str = reinterpret_cast<char *>(data),
//       *cur;
//
//  while (len &&
//         (cur = ::memchr(str, '\n', len)) &&
//         --len) {
//    ::memmove(cur, cur + 1, len - (cur - str));
//    while (cur < str + len)
//      if (::isspace(*cur) && --len)
//        ::memmove(cur, cur + 1, len - (cur - str));
//      else if (len >= 2 && ::iswspace(*(LPCWSTR)cur) && (len-=2))
//        ::memmove(cur, cur + 2, len - (cur - str));
//      else
//        break;
//  }
//
//  *size = len;
//  return true;
//}
bool InsertLunaSoftHook()
{
  const BYTE bytes[] = {
    0xcc,            // 0046c57e   cc               int3
    0xcc,            // 0046c57f   cc               int3
    0x55,            // 0046c580   55               push ebp       ; jichi: text in arg1
    0x8b,0xec,       // 0046c581   8bec             mov ebp,esp
    0x83,0xec, 0x08, // 0046c583   83ec 08          sub esp,0x8
    0x89,0x4d, 0xf8, // 0046c586   894d f8          mov dword ptr ss:[ebp-0x8],ecx
    0x8b,0x4d, 0xf8, // 0046c589   8b4d f8          mov ecx,dword ptr ss:[ebp-0x8]
    0x83,0xc1, 0x1c, // 0046c58c   83c1 1c          add ecx,0x1c
    0xe8             // 0046c58f   e8 2cebf9ff      call .0040b0c0
  };
  enum { addr_offset = 2 };
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_limit_);
  //GROWL(addr);
  if (!addr) {
    ConsoleOutput("vnreng:LunaSoft: pattern not found");
    return false;
  }
  HookParam hp = {};
  hp.address = addr + addr_offset;
  hp.offset = 1 * 4; // arg1
  hp.type = USING_STRING;
  //hp.filter_fun = LunaSoftFilter; // remove \n
  ConsoleOutput("vnreng: INSERT LunaSoft");
  NewHook(hp, "LunaSoft");

  // There are no GDI functions anyway
  //ConsoleOutput("vnreng:LunaSoft: disable GDI hooks");
  //DisableGDIHooks();
  return true;
}

/** jichi 2/6/2015 FocasLens (Touhou)
 *  Sample game: [141227] [FocasLens] 幻想人形演�
 *
 *  Debugging method:
 *  1. Find first matched text, which has stable address
 *  2. Insert WRITE hw break point
 *  3. Find where the text is assigned
 *
 *  The game also invokes GDI functions (GetGlyphOutlineA), where the access is cached and looped.
 *
 *  Issues:
 *  - This hook cannot find name thread
 *  - Selected character name is hard-coded to the thread
 *
 *  001faaed   cc               int3
 *  001faaee   cc               int3
 *  001faaef   cc               int3
 *  001faaf0   55               push ebp
 *  001faaf1   8bec             mov ebp,esp
 *  001faaf3   51               push ecx
 *  001faaf4   53               push ebx
 *  001faaf5   56               push esi
 *  001faaf6   57               push edi
 *  001faaf7   8bf0             mov esi,eax
 *  001faaf9   e8 98281500      call .0034d396
 *  001faafe   50               push eax
 *  001faaff   a1 b08bb100      mov eax,dword ptr ds:[0xb18bb0]
 *  001fab04   03c6             add eax,esi
 *  001fab06   50               push eax
 *  001fab07   e8 9b241500      call .0034cfa7
 *  001fab0c   8b0d e88bb100    mov ecx,dword ptr ds:[0xb18be8]
 *  001fab12   8b3d b08bb100    mov edi,dword ptr ds:[0xb18bb0]
 *  001fab18   83c1 f7          add ecx,-0x9
 *  001fab1b   83c4 08          add esp,0x8
 *  001fab1e   8bd8             mov ebx,eax
 *  001fab20   390d ec8bb100    cmp dword ptr ds:[0xb18bec],ecx
 *  001fab26   7c 65            jl short .001fab8d
 *  001fab28   803c37 20        cmp byte ptr ds:[edi+esi],0x20
 *  001fab2c   74 41            je short .001fab6f
 *  001fab2e   803c37 81        cmp byte ptr ds:[edi+esi],0x81
 *  001fab32   75 4d            jnz short .001fab81
 *  001fab34   807c37 01 42     cmp byte ptr ds:[edi+esi+0x1],0x42
 *  001fab39   74 34            je short .001fab6f
 *  001fab3b   803c37 81        cmp byte ptr ds:[edi+esi],0x81
 *  001fab3f   75 40            jnz short .001fab81
 *  001fab41   807c37 01 41     cmp byte ptr ds:[edi+esi+0x1],0x41
 *  001fab46   74 27            je short .001fab6f
 *  001fab48   803c37 81        cmp byte ptr ds:[edi+esi],0x81
 *  001fab4c   75 33            jnz short .001fab81
 *  001fab4e   807c37 01 48     cmp byte ptr ds:[edi+esi+0x1],0x48
 *  001fab53   74 1a            je short .001fab6f
 *  001fab55   803c37 81        cmp byte ptr ds:[edi+esi],0x81
 *  001fab59   75 26            jnz short .001fab81
 *  001fab5b   807c37 01 49     cmp byte ptr ds:[edi+esi+0x1],0x49
 *  001fab60   74 0d            je short .001fab6f
 *  001fab62   803c37 81        cmp byte ptr ds:[edi+esi],0x81
 *  001fab66   75 19            jnz short .001fab81
 *  001fab68   807c37 01 40     cmp byte ptr ds:[edi+esi+0x1],0x40
 *  001fab6d   75 12            jnz short .001fab81
 *  001fab6f   803d c58bb100 00 cmp byte ptr ds:[0xb18bc5],0x0
 *  001fab76   75 09            jnz short .001fab81
 *  001fab78   c605 c58bb100 01 mov byte ptr ds:[0xb18bc5],0x1
 *  001fab7f   eb 0c            jmp short .001fab8d
 *  001fab81   e8 7a000000      call .001fac00
 *  001fab86   c605 c58bb100 00 mov byte ptr ds:[0xb18bc5],0x0
 *  001fab8d   8b0d e48bb100    mov ecx,dword ptr ds:[0xb18be4]
 *  001fab93   33c0             xor eax,eax
 *  001fab95   85db             test ebx,ebx
 *  001fab97   7e 2b            jle short .001fabc4
 *  001fab99   8d1437           lea edx,dword ptr ds:[edi+esi]
 *  001fab9c   8b35 ec8bb100    mov esi,dword ptr ds:[0xb18bec]
 *  001faba2   8955 fc          mov dword ptr ss:[ebp-0x4],edx
 *  001faba5   8bd1             mov edx,ecx
 *  001faba7   0faf15 e88bb100  imul edx,dword ptr ds:[0xb18be8]
 *  001fabae   0315 bc8bb100    add edx,dword ptr ds:[0xb18bbc]          ; .00b180f8
 *  001fabb4   03f2             add esi,edx
 *  001fabb6   8b55 fc          mov edx,dword ptr ss:[ebp-0x4]
 *  001fabb9   8a1402           mov dl,byte ptr ds:[edx+eax]
 *  001fabbc   881406           mov byte ptr ds:[esi+eax],dl    ; jichi: text is in dl in byte
 *  001fabbf   40               inc eax
 *  001fabc0   3bc3             cmp eax,ebx
 *  001fabc2  ^7c f2            jl short .001fabb6
 *  001fabc4   0faf0d e88bb100  imul ecx,dword ptr ds:[0xb18be8]
 *  001fabcb   030d bc8bb100    add ecx,dword ptr ds:[0xb18bbc]          ; .00b180f8
 *  001fabd1   a1 ec8bb100      mov eax,dword ptr ds:[0xb18bec]
 *  001fabd6   03fb             add edi,ebx
 *  001fabd8   893d b08bb100    mov dword ptr ds:[0xb18bb0],edi
 *  001fabde   5f               pop edi
 *  001fabdf   03c8             add ecx,eax
 *  001fabe1   03c3             add eax,ebx
 *  001fabe3   5e               pop esi
 *  001fabe4   c60419 00        mov byte ptr ds:[ecx+ebx],0x0
 *  001fabe8   a3 ec8bb100      mov dword ptr ds:[0xb18bec],eax
 *  001fabed   5b               pop ebx
 *  001fabee   8be5             mov esp,ebp
 *  001fabf0   5d               pop ebp
 *  001fabf1   c3               retn
 *  001fabf2   cc               int3
 *  001fabf3   cc               int3
 *  001fabf4   cc               int3
 *  001fabf5   cc               int3
 *  001fabf6   cc               int3
 *  001fabf7   cc               int3
 */
static void SpecialHookFocasLens(DWORD esp_base, HookParam *, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  DWORD addr  = esp_base + pusha_edx_off - 4;
  if (*(char *)addr) {
    *data = addr;
    *len = 1;
    *split = FIXED_SPLIT_VALUE;
  }
}
bool InsertFocasLensHook()
{
  const BYTE bytes[] = {
    0x8a,0x14,0x02, // 001fabb9   8a1402           mov dl,byte ptr ds:[edx+eax]
    0x88,0x14,0x06, // 001fabbc   881406           mov byte ptr ds:[esi+eax],dl    ; jichi: text is in dl in byte
    0x40,           // 001fabbf   40               inc eax
    0x3b,0xc3       // 001fabc0   3bc3             cmp eax,ebx
  };
  enum { addr_offset = 0x001fabbc - 0x001fabb9 };
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_limit_);
  //GROWL(addr);
  if (!addr) {
    ConsoleOutput("vnreng:FocasLens: pattern not found");
    return false;
  }
  HookParam hp = {};
  hp.address = addr + addr_offset;
  //hp.offset = pusha_edx_off - 4;
  //hp.length_offset = 1; // Why this does not work?!
  //hp.split = pusha_eax_off - 4; // use eax as split
  hp.text_fun = SpecialHookFocasLens; // use special hook to force byte access
  hp.type = USING_STRING|USING_SPLIT|FIXING_SPLIT|NO_CONTEXT; // no context to get rid of relative function address
  ConsoleOutput("vnreng: INSERT FocasLens");
  NewHook(hp, "FocasLens");

  // GDI functions are kept in case the font is not cached
  //DisableGDIHooks();
  return true;
}

/** jichi 2/6/2015 Syuntada
 *  Sample game: [140816] [平安亭] カノジョのお母さん�好きですか-- /HA-18@6944C:kanojo.exe
 *
 *  /HA-18@6944C:kanojo.exe
 *  - addr: 431180 = 0x6944c
 *  - module: 1301076281
 *  - off: 4294967268 = 0xffffffe4 = - 0x1c
 *  - length_offset: 1
 *  - type: 68 = 0x44
 *
 *  004692bd   cc               int3
 *  004692be   cc               int3
 *  004692bf   cc               int3
 *  004692c0   83ec 48          sub esp,0x48
 *  004692c3   53               push ebx
 *  004692c4   55               push ebp
 *  004692c5   56               push esi
 *  004692c6   8bf1             mov esi,ecx
 *  004692c8   8b86 d4000000    mov eax,dword ptr ds:[esi+0xd4]
 *  004692ce   0386 8c040000    add eax,dword ptr ds:[esi+0x48c]
 *  004692d4   8b8e c8010000    mov ecx,dword ptr ds:[esi+0x1c8]
 *  004692da   8b9e 90040000    mov ebx,dword ptr ds:[esi+0x490]
 *  004692e0   03c0             add eax,eax
 *  004692e2   03c0             add eax,eax
 *  004692e4   894424 24        mov dword ptr ss:[esp+0x24],eax
 *  004692e8   8b86 c4010000    mov eax,dword ptr ds:[esi+0x1c4]
 *  004692ee   8986 94040000    mov dword ptr ds:[esi+0x494],eax
 *  004692f4   8b4424 60        mov eax,dword ptr ss:[esp+0x60]
 *  004692f8   898e 98040000    mov dword ptr ds:[esi+0x498],ecx
 *  004692fe   0fb628           movzx ebp,byte ptr ds:[eax]
 *  00469301   0fb650 01        movzx edx,byte ptr ds:[eax+0x1]
 *  00469305   c1e5 08          shl ebp,0x8
 *  00469308   0bea             or ebp,edx
 *  0046930a   03db             add ebx,ebx
 *  0046930c   03db             add ebx,ebx
 *  0046930e   8d8d 617dffff    lea ecx,dword ptr ss:[ebp+0xffff7d61]
 *  00469314   57               push edi
 *  00469315   895c24 30        mov dword ptr ss:[esp+0x30],ebx
 *  00469319   c74424 38 100000>mov dword ptr ss:[esp+0x38],0x10
 *  00469321   896c24 34        mov dword ptr ss:[esp+0x34],ebp
 *  00469325   b8 02000000      mov eax,0x2
 *  0046932a   83f9 52          cmp ecx,0x52
 *  0046932d   77 02            ja short .00469331
 *  0046932f   33c0             xor eax,eax
 *  00469331   81fd 41810000    cmp ebp,0x8141
 *  00469337   7c 08            jl short .00469341
 *  00469339   81fd 9a820000    cmp ebp,0x829a
 *  0046933f   7e 0e            jle short .0046934f
 *  00469341   8d95 c07cffff    lea edx,dword ptr ss:[ebp+0xffff7cc0]
 *  00469347   81fa 4f040000    cmp edx,0x44f
 *  0046934d   77 09            ja short .00469358
 *  0046934f   bf 01000000      mov edi,0x1
 *  00469354   8bc7             mov eax,edi
 *  00469356   eb 05            jmp short .0046935d
 *  00469358   bf 01000000      mov edi,0x1
 *  0046935d   83e8 00          sub eax,0x0
 *  00469360   74 2a            je short .0046938c
 *  00469362   2bc7             sub eax,edi
 *  00469364   74 0c            je short .00469372
 *  00469366   2bc7             sub eax,edi
 *  00469368   75 3a            jnz short .004693a4
 *  0046936a   8b96 68010000    mov edx,dword ptr ds:[esi+0x168]
 *  00469370   eb 20            jmp short .00469392
 *  00469372   8b96 7c090000    mov edx,dword ptr ds:[esi+0x97c]
 *  00469378   8b86 64010000    mov eax,dword ptr ds:[esi+0x164]
 *  0046937e   8b52 28          mov edx,dword ptr ds:[edx+0x28]
 *  00469381   8d8e 7c090000    lea ecx,dword ptr ds:[esi+0x97c]
 *  00469387   50               push eax
 *  00469388   ffd2             call edx
 *  0046938a   eb 18            jmp short .004693a4
 *  0046938c   8b96 60010000    mov edx,dword ptr ds:[esi+0x160]
 *  00469392   8b86 7c090000    mov eax,dword ptr ds:[esi+0x97c]
 *  00469398   8b40 28          mov eax,dword ptr ds:[eax+0x28]
 *  0046939b   8d8e 7c090000    lea ecx,dword ptr ds:[esi+0x97c]
 *  004693a1   52               push edx
 *  004693a2   ffd0             call eax
 *  004693a4   39be d40f0000    cmp dword ptr ds:[esi+0xfd4],edi
 *  004693aa   75 45            jnz short .004693f1
 *  004693ac   8b8e 90040000    mov ecx,dword ptr ds:[esi+0x490]
 *  004693b2   b8 d0020000      mov eax,0x2d0
 *  004693b7   2bc1             sub eax,ecx
 *  004693b9   2b86 c8010000    sub eax,dword ptr ds:[esi+0x1c8]
 *  004693bf   68 000f0000      push 0xf00
 *  004693c4   8d0480           lea eax,dword ptr ds:[eax+eax*4]
 *  004693c7   c1e0 08          shl eax,0x8
 *  004693ca   0386 c4010000    add eax,dword ptr ds:[esi+0x1c4]
 *  004693d0   8d1440           lea edx,dword ptr ds:[eax+eax*2]
 *  004693d3   8b4424 60        mov eax,dword ptr ss:[esp+0x60]
 *  004693d7   52               push edx
 *  004693d8   8b50 40          mov edx,dword ptr ds:[eax+0x40]
 *  004693db   8b86 c8000000    mov eax,dword ptr ds:[esi+0xc8]
 *  004693e1   0386 8c040000    add eax,dword ptr ds:[esi+0x48c]
 *  004693e7   52               push edx
 *  004693e8   50               push eax
 *  004693e9   51               push ecx
 *  004693ea   8bce             mov ecx,esi
 *  004693ec   e8 9fc4ffff      call .00465890
 *  004693f1   39be d00f0000    cmp dword ptr ds:[esi+0xfd0],edi
 *  004693f7   0f85 f2010000    jnz .004695ef
 *  004693fd   8d86 20100000    lea eax,dword ptr ds:[esi+0x1020]
 *  00469403   50               push eax
 *  00469404   55               push ebp
 *  00469405   8bce             mov ecx,esi
 *  00469407   e8 64f4ffff      call .00468870
 *  0046940c   8a4e 25          mov cl,byte ptr ds:[esi+0x25]
 *  0046940f   8a56 26          mov dl,byte ptr ds:[esi+0x26]
 *  00469412   884c24 18        mov byte ptr ss:[esp+0x18],cl
 *  00469416   8b4c24 5c        mov ecx,dword ptr ss:[esp+0x5c]
 *  0046941a   885424 14        mov byte ptr ss:[esp+0x14],dl
 *  0046941e   8b51 40          mov edx,dword ptr ds:[ecx+0x40]
 *  00469421   895424 20        mov dword ptr ss:[esp+0x20],edx
 *  00469425   b9 d0020000      mov ecx,0x2d0
 *  0046942a   2bcb             sub ecx,ebx
 *  0046942c   ba 00000000      mov edx,0x0
 *  00469431   0f98c2           sets dl
 *  00469434   8bf8             mov edi,eax
 *  00469436   8a46 24          mov al,byte ptr ds:[esi+0x24]
 *  00469439   884424 1c        mov byte ptr ss:[esp+0x1c],al
 *  0046943d   4a               dec edx
 *  0046943e   23d1             and edx,ecx
 *  00469440   69d2 000f0000    imul edx,edx,0xf00
 *  00469446   8bca             mov ecx,edx
 *  00469448   894c24 24        mov dword ptr ss:[esp+0x24],ecx
 *  0046944c   85ff             test edi,edi    ; jichi: hook here
 *  0046944e   74 3a            je short .0046948a
 *  00469450   8b5424 14        mov edx,dword ptr ss:[esp+0x14]
 *  00469454   6a 00            push 0x0
 *  00469456   57               push edi
 *  00469457   8d86 c80c0000    lea eax,dword ptr ds:[esi+0xcc8]
 *  0046945d   50               push eax
 *  0046945e   8b4424 24        mov eax,dword ptr ss:[esp+0x24]
 *  00469462   6a 10            push 0x10
 *  00469464   52               push edx
 *  00469465   8b5424 30        mov edx,dword ptr ss:[esp+0x30]
 *  00469469   50               push eax
 *  0046946a   8b4424 38        mov eax,dword ptr ss:[esp+0x38]
 *  0046946e   52               push edx
 *  0046946f   68 000f0000      push 0xf00
 *  00469474   51               push ecx
 *  00469475   8b4c24 4c        mov ecx,dword ptr ss:[esp+0x4c]
 */
bool InsertSyuntadaHook()
{
  const BYTE bytes[] = {
    0x4a,                           // 0046943d   4a               dec edx
    0x23,0xd1,                      // 0046943e   23d1             and edx,ecx
    0x69,0xd2, 0x00,0x0f,0x00,0x00, // 00469440   69d2 000f0000    imul edx,edx,0xf00
    0x8b,0xca,                      // 00469446   8bca             mov ecx,edx
    0x89,0x4c,0x24, 0x24,           // 00469448   894c24 24        mov dword ptr ss:[esp+0x24],ecx
    0x85,0xff,                      // 0046944c   85ff             test edi,edi    ; jichi: hook here
    0x74, 0x3a                      // 0046944e   74 3a            je short .0046948a
  };
  enum { addr_offset = 0x0046944c - 0x0046943d };
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_limit_);
  //GROWL(addr);
  if (!addr) {
    ConsoleOutput("vnreng:Syuntada: pattern not found");
    return false;
  }
  HookParam hp = {};
  hp.address = addr + addr_offset;
  hp.offset = -0x1c;
  hp.length_offset = 1;
  hp.type = BIG_ENDIAN; // 0x4
  ConsoleOutput("vnreng: INSERT Syuntada");
  NewHook(hp, "Syuntada");

  // TextOutA will produce repeated texts
  ConsoleOutput("vnreng:Syuntada: disable GDI hooks");
  DisableGDIHooks();
  return true;
}

/**
 *  jichi 5/22/2015: Insert Bootup hook
 *  Sample games:
 *  - [090709] [PIL] 仏蘭西少女
 *  - [110318] [Daisy2] 三国恋戦� *  - [110329] [PIL/SLASH] 神学校
 *  - [150527] [Daisy2] 絶対階級学� *
 *  Properties
 *  - There is Bootup.dat existing in the game folder.
 *  - lstrlenW can find text repeating once
 *  - GetCharABCWidthsW and TextOutW can find cached text that missing characters
 *    GetCharABCWidthsA and TextOutA for old games.
 *  - There is only one TextOut (W for new and A for old).
 *
 *  Logic:
 *  + GDI hook
 *    - Hook to the caller of TextOut
 *  + Lstr hook
 *    - Find last (second) caller of the first GetCharABCWidths after int3
 *    - Find the lstrlen function in this caller, and hook to it
 *
 *  Full text is in arg1, shifted one by one.
 *  Character to paint is also in arg3
 *
 *  All Bootup games are slightly different
 *  - 三国恋戦�仏蘭西少女: text in both lstrlenA and caller of TextOutA
 *    But I didn't find correct lstrlenA to hook. BootupLstrA find nothing for 仏蘭西少女 and name for 三国恋戦�
 *  - 神学校: text in both lstrlenW and TextOutW, but lstrlenW has repetition
 *    Caller of TextOutW the same as that of TextOutA
 *  - 絶対階級学� text in both lstrlenW and TextOutW. But TextOutW's name has repetition
 *    Caller of TextOutW different 神学校
 *
 *  Here's the beginning of caller of TextOutW in 絶対階級学�
 *  00B61ADD   CC               INT3
 *  00B61ADE   CC               INT3
 *  00B61ADF   CC               INT3
 *  00B61AE0   55               PUSH EBP
 *  00B61AE1   8BEC             MOV EBP,ESP
 *  00B61AE3   81EC 98000000    SUB ESP,0x98
 *  00B61AE9   53               PUSH EBX
 *  00B61AEA   56               PUSH ESI
 *  00B61AEB   57               PUSH EDI
 *  00B61AEC   8BF2             MOV ESI,EDX
 *  00B61AEE   8BF9             MOV EDI,ECX
 *  00B61AF0   8975 D8          MOV DWORD PTR SS:[EBP-0x28],ESI
 *  00B61AF3   897D E0          MOV DWORD PTR SS:[EBP-0x20],EDI
 *  00B61AF6   E8 A5FEFFFF      CALL .00B619A0
 *  00B61AFB   8BD8             MOV EBX,EAX
 *  00B61AFD   895D CC          MOV DWORD PTR SS:[EBP-0x34],EBX
 *  00B61B00   66:833B 00       CMP WORD PTR DS:[EBX],0x0
 *  00B61B04   0F85 0B020000    JNZ .00B61D15
 *  00B61B0A   B8 00010000      MOV EAX,0x100
 *  00B61B0F   66:8933          MOV WORD PTR DS:[EBX],SI
 *  00B61B12   66:3BF0          CMP SI,AX
 *  00B61B15   72 26            JB SHORT .00B61B3D
 *  00B61B17   8B47 3C          MOV EAX,DWORD PTR DS:[EDI+0x3C]
 *  00B61B1A   85C0             TEST EAX,EAX
 *  00B61B1C   74 1F            JE SHORT .00B61B3D
 *  00B61B1E   8B57 44          MOV EDX,DWORD PTR DS:[EDI+0x44]
 *  00B61B21   85D2             TEST EDX,EDX
 *  00B61B23   7E 18            JLE SHORT .00B61B3D
 *  00B61B25   33C9             XOR ECX,ECX
 *  00B61B27   85D2             TEST EDX,EDX
 *  00B61B29   7E 12            JLE SHORT .00B61B3D
 *  00B61B2B   8B47 40          MOV EAX,DWORD PTR DS:[EDI+0x40]
 *  00B61B2E   8BFF             MOV EDI,EDI
 *  00B61B30   66:3930          CMP WORD PTR DS:[EAX],SI
 *  00B61B33   74 6F            JE SHORT .00B61BA4
 *  00B61B35   41               INC ECX
 *  00B61B36   83C0 02          ADD EAX,0x2
 *  00B61B39   3BCA             CMP ECX,EDX
 *  00B61B3B  ^7C F3            JL SHORT .00B61B30
 *  00B61B3D   33C0             XOR EAX,EAX
 *  00B61B3F   66:8945 9E       MOV WORD PTR SS:[EBP-0x62],AX
 *  00B61B43   8B47 04          MOV EAX,DWORD PTR DS:[EDI+0x4]
 *  00B61B46   0FAF47 1C        IMUL EAX,DWORD PTR DS:[EDI+0x1C]
 *  00B61B4A   0FAF47 1C        IMUL EAX,DWORD PTR DS:[EDI+0x1C]
 *  00B61B4E   0FAF47 18        IMUL EAX,DWORD PTR DS:[EDI+0x18]
 *  00B61B52   50               PUSH EAX
 *  00B61B53   6A 00            PUSH 0x0
 *  00B61B55   FF77 14          PUSH DWORD PTR DS:[EDI+0x14]
 *  00B61B58   66:8975 9C       MOV WORD PTR SS:[EBP-0x64],SI
 *  00B61B5C   E8 2FC20200      CALL .00B8DD90
 *  00B61B61   83C4 0C          ADD ESP,0xC
 *  00B61B64   8D45 9C          LEA EAX,DWORD PTR SS:[EBP-0x64]
 *  00B61B67   6A 01            PUSH 0x1
 *  00B61B69   50               PUSH EAX
 *  00B61B6A   6A 00            PUSH 0x0
 *  00B61B6C   6A 00            PUSH 0x0
 *  00B61B6E   FF77 10          PUSH DWORD PTR DS:[EDI+0x10]
 *  00B61B71   FF15 8820BB00    CALL DWORD PTR DS:[0xBB2088]             ; gdi32.TextOutW
 *  00B61B77   8B47 1C          MOV EAX,DWORD PTR DS:[EDI+0x1C]
 *  00B61B7A   8B57 14          MOV EDX,DWORD PTR DS:[EDI+0x14]
 *  00B61B7D   8B7F 04          MOV EDI,DWORD PTR DS:[EDI+0x4]
 *  00B61B80   8B73 0C          MOV ESI,DWORD PTR DS:[EBX+0xC]
 *  00B61B83   0FAFF8           IMUL EDI,EAX
 *  00B61B86   48               DEC EAX
 *  00B61B87   8975 C4          MOV DWORD PTR SS:[EBP-0x3C],ESI
 *  00B61B8A   897D C8          MOV DWORD PTR SS:[EBP-0x38],EDI
 *
 *  TextOutW's caller for 神学校
 *  0113183E   CC               INT3
 *  0113183F   CC               INT3
 *  01131840   55               PUSH EBP
 *  01131841   8BEC             MOV EBP,ESP
 *  01131843   83EC 74          SUB ESP,0x74
 *  01131846   53               PUSH EBX
 *  01131847   56               PUSH ESI
 *  01131848   8B75 08          MOV ESI,DWORD PTR SS:[EBP+0x8]
 *  0113184B   57               PUSH EDI
 *  0113184C   8B7D 0C          MOV EDI,DWORD PTR SS:[EBP+0xC]
 *  0113184F   8BCF             MOV ECX,EDI
 *  01131851   8BD6             MOV EDX,ESI
 *  01131853   E8 A8FEFFFF      CALL .01131700
 *  01131858   8BD8             MOV EBX,EAX
 *  0113185A   66:833B 00       CMP WORD PTR DS:[EBX],0x0
 *  0113185E   895D 90          MOV DWORD PTR SS:[EBP-0x70],EBX
 *  01131861   0F85 700F0000    JNZ .011327D7
 *  01131867   B8 00010000      MOV EAX,0x100
 *  0113186C   66:893B          MOV WORD PTR DS:[EBX],DI
 *  0113186F   66:3BF8          CMP DI,AX
 *  01131872   72 2E            JB SHORT .011318A2
 *  01131874   8B56 3C          MOV EDX,DWORD PTR DS:[ESI+0x3C]
 *  01131877   85D2             TEST EDX,EDX
 *  01131879   74 27            JE SHORT .011318A2
 *  0113187B   8B46 44          MOV EAX,DWORD PTR DS:[ESI+0x44]
 *  0113187E   85C0             TEST EAX,EAX
 *  01131880   7E 20            JLE SHORT .011318A2
 *  01131882   33FF             XOR EDI,EDI
 *  01131884   85C0             TEST EAX,EAX
 *  01131886   7E 1A            JLE SHORT .011318A2
 *  01131888   8B46 40          MOV EAX,DWORD PTR DS:[ESI+0x40]
 *  0113188B   EB 03            JMP SHORT .01131890
 *  0113188D   8D49 00          LEA ECX,DWORD PTR DS:[ECX]
 *  01131890   66:8B4D 0C       MOV CX,WORD PTR SS:[EBP+0xC]
 *  01131894   66:3908          CMP WORD PTR DS:[EAX],CX
 *  01131897   74 74            JE SHORT .0113190D
 *  01131899   47               INC EDI
 *  0113189A   83C0 02          ADD EAX,0x2
 *  0113189D   3B7E 44          CMP EDI,DWORD PTR DS:[ESI+0x44]
 *  011318A0  ^7C EE            JL SHORT .01131890
 *  011318A2   66:8B45 0C       MOV AX,WORD PTR SS:[EBP+0xC]
 *  011318A6   66:8945 8C       MOV WORD PTR SS:[EBP-0x74],AX
 *  011318AA   8B46 1C          MOV EAX,DWORD PTR DS:[ESI+0x1C]
 *  011318AD   0FAFC0           IMUL EAX,EAX
 *  011318B0   0FAF46 18        IMUL EAX,DWORD PTR DS:[ESI+0x18]
 *  011318B4   0FAF46 04        IMUL EAX,DWORD PTR DS:[ESI+0x4]
 *  011318B8   8B56 14          MOV EDX,DWORD PTR DS:[ESI+0x14]
 *  011318BB   33C9             XOR ECX,ECX
 *  011318BD   50               PUSH EAX
 *  011318BE   51               PUSH ECX
 *  011318BF   52               PUSH EDX
 *  011318C0   66:894D 8E       MOV WORD PTR SS:[EBP-0x72],CX
 *  011318C4   E8 87060200      CALL .01151F50
 *  011318C9   8B4E 10          MOV ECX,DWORD PTR DS:[ESI+0x10]
 *  011318CC   83C4 0C          ADD ESP,0xC
 *  011318CF   6A 01            PUSH 0x1
 *  011318D1   8D45 8C          LEA EAX,DWORD PTR SS:[EBP-0x74]
 *  011318D4   50               PUSH EAX
 *  011318D5   6A 00            PUSH 0x0
 *  011318D7   6A 00            PUSH 0x0
 *  011318D9   51               PUSH ECX
 *  011318DA   FF15 38101701    CALL DWORD PTR DS:[0x1171038]            ; gdi32.TextOutW
 *  011318E0   8B4E 1C          MOV ECX,DWORD PTR DS:[ESI+0x1C]
 *  011318E3   8B46 04          MOV EAX,DWORD PTR DS:[ESI+0x4]
 *  011318E6   8B56 14          MOV EDX,DWORD PTR DS:[ESI+0x14]
 *  011318E9   0FAFC1           IMUL EAX,ECX
 *  011318EC   8B7B 0C          MOV EDI,DWORD PTR DS:[EBX+0xC]
 */
namespace { // unnamed
bool BootupGDIHook(DWORD esp_base, HookParam *hp)
{
  DWORD arg2 = argof(2, esp_base);
  if ((arg2 & 0xffff0000)) { // if arg2 high bits are there, this is new Bootup game
    hp->type |= DATA_INDIRECT;
    hp->offset = 4 * 3; // arg3
    hp->split = pusha_ebx_off - 4; // use ebx value to split name out, which has repetitions
  }
  return false; // run once and stop hooking
}
bool InsertBootupGDIHook()
{
  bool widechar = true;
  ULONG addr = MemDbg::findCallerAddressAfterInt3((ULONG)TextOutW, module_base_, module_limit_);
  if (!addr) {
    addr = MemDbg::findCallerAddressAfterInt3((ULONG)TextOutA, module_base_, module_limit_);
    widechar = false;
  }
  if (!addr) {
    ConsoleOutput("vnreng:BootupGDI: failed to find TextOut");
    return false;
  }

  HookParam hp = {};
  hp.address = addr;
  hp.type = USING_SPLIT|NO_CONTEXT;   // use NO_CONTEXT to get rid of floating reladdr
  hp.type |= widechar ? USING_UNICODE : BIG_ENDIAN; // use context as split is sufficient, but will produce floating split
  hp.length_offset = 1; // character by character

  hp.offset = 4 * 2; // arg2, character in arg2, could be modified by hook
  if (widechar)
    hp.split = pusha_edx_off - 4; // split name out which contains repetitions
  else
    hp.split = 4 * 1; // use arg1 to split name out furigana, this cause split to be floating
  hp.hook_fun = BootupGDIHook; // adjust hook parameter at runtime

  ConsoleOutput("vnreng: INSERT BootupGDI");
  NewHook(hp, widechar ? "BootupW" : "BootupA");

  ConsoleOutput("vnreng:BootupGDI: disable GDI hooks");
  DisableGDIHooks();
  return true;
}
bool InsertBootupLstrHook() // for character name
{
  bool widechar = true;
  ULONG addr = MemDbg::findLastCallerAddressAfterInt3((ULONG)GetCharABCWidthsW, module_base_, module_limit_);
  if (!addr) {
    // Do not hook to lstrlenA, which causes text extraction to stop
    //addr = MemDbg::findLastCallerAddressAfterInt3((ULONG)GetCharABCWidthsA, module_base_, module_limit_);
    //widechar = false;
  }
  if (!addr) {
    ConsoleOutput("vnreng:BootupLstr: failed to find GetCharABCWidths");
    return false;
  }
  //GROWL_DWORD2(addr, module_base_);
  //enum { range = 0x200 }; // 0x012A2CCB  - 0x12A2CB0 = 0x1b
  addr = MemDbg::findCallAddress(widechar ? (ULONG)::lstrlenW : (ULONG)::lstrlenA,
      module_base_, module_limit_,
      addr - module_base_); //, range); // no range
  if (!addr) {
    ConsoleOutput("vnreng:BootupLstr: failed to find lstrlen");
    return false;
  }

  HookParam hp = {};
  hp.address = addr;
  hp.type = widechar ? USING_UNICODE : USING_STRING; // use context as split is sufficient, but will produce floating split
  //hp.type = USING_UNICODE|NO_CONTEXT|USING_SPLIT; // use text address as split
  //hp.split = 0;

  ConsoleOutput("vnreng: INSERT BootupLstr");
  NewHook(hp, widechar ? "BootupLstrW" : "BootupLstrA");
  return true;
}
} // unnamed namespace
bool InsertBootupHook()
{
  bool ret = InsertBootupGDIHook();
  InsertBootupLstrHook();
  return ret;
}

/** jichi 7/23/2015 Escude
 *  Sample game: Re;Lord ��ルフォルト�魔女とぬぁ�るみ *  See: http://capita.tistory.com/m/post/210
 *
 *  ENCODEKOR,FORCEFONT(5),HOOK(0x0042CB40,TRANS([[ESP+0x4]+0x20],PTRCHEAT,PTRBACKUP,SAFE),RETNPOS(SOURCE)),FONT(Malgun Gothic,-13)
 *
 *  GDI functions: TextOutA, GetTextExtentPoint32A
 *  It requires changing function to MS Gothic using configure.exe
 *
 *  Text in arg1 + 0x20
 *
 *  0042CB3C   CC               INT3
 *  0042CB3D   CC               INT3
 *  0042CB3E   CC               INT3
 *  0042CB3F   CC               INT3
 *  0042CB40   56               PUSH ESI
 *  0042CB41   8B7424 08        MOV ESI,DWORD PTR SS:[ESP+0x8]
 *  0042CB45   8B06             MOV EAX,DWORD PTR DS:[ESI]
 *  0042CB47   50               PUSH EAX
 *  0042CB48   E8 53FC0A00      CALL .004DC7A0
 *  0042CB4D   8B56 04          MOV EDX,DWORD PTR DS:[ESI+0x4]
 *  0042CB50   83C4 04          ADD ESP,0x4
 *  0042CB53   5E               POP ESI
 *  0042CB54   85D2             TEST EDX,EDX
 *  0042CB56   74 7E            JE SHORT .0042CBD6
 *  0042CB58   85C0             TEST EAX,EAX
 *  0042CB5A   74 07            JE SHORT .0042CB63
 *  0042CB5C   8B08             MOV ECX,DWORD PTR DS:[EAX]
 *  0042CB5E   8B49 04          MOV ECX,DWORD PTR DS:[ECX+0x4]
 *  0042CB61   EB 02            JMP SHORT .0042CB65
 *  0042CB63   33C9             XOR ECX,ECX
 *  0042CB65   890A             MOV DWORD PTR DS:[EDX],ECX
 *  0042CB67   85C0             TEST EAX,EAX
 *  0042CB69   74 07            JE SHORT .0042CB72
 *  0042CB6B   8B08             MOV ECX,DWORD PTR DS:[EAX]
 *  0042CB6D   8B49 08          MOV ECX,DWORD PTR DS:[ECX+0x8]
 *  0042CB70   EB 02            JMP SHORT .0042CB74
 *  0042CB72   33C9             XOR ECX,ECX
 *  0042CB74   894A 04          MOV DWORD PTR DS:[EDX+0x4],ECX
 *  0042CB77   85C0             TEST EAX,EAX
 *  0042CB79   74 08            JE SHORT .0042CB83
 *  0042CB7B   8B08             MOV ECX,DWORD PTR DS:[EAX]
 *  0042CB7D   0FB749 0E        MOVZX ECX,WORD PTR DS:[ECX+0xE]
 *  0042CB81   EB 02            JMP SHORT .0042CB85
 *  0042CB83   33C9             XOR ECX,ECX
 *  0042CB85   0FB7C9           MOVZX ECX,CX
 *  0042CB88   894A 08          MOV DWORD PTR DS:[EDX+0x8],ECX
 *  0042CB8B   85C0             TEST EAX,EAX
 *  0042CB8D   74 19            JE SHORT .0042CBA8
 *  0042CB8F   8B08             MOV ECX,DWORD PTR DS:[EAX]
 *  0042CB91   8379 04 00       CMP DWORD PTR DS:[ECX+0x4],0x0
 *  0042CB95   76 11            JBE SHORT .0042CBA8
 *  0042CB97   8B49 08          MOV ECX,DWORD PTR DS:[ECX+0x8]
 *  0042CB9A   85C9             TEST ECX,ECX
 *  0042CB9C   76 0A            JBE SHORT .0042CBA8
 *  0042CB9E   49               DEC ECX
 *  0042CB9F   0FAF48 0C        IMUL ECX,DWORD PTR DS:[EAX+0xC]
 *  0042CBA3   0348 04          ADD ECX,DWORD PTR DS:[EAX+0x4]
 *  0042CBA6   EB 02            JMP SHORT .0042CBAA
 *  0042CBA8   33C9             XOR ECX,ECX
 *  0042CBAA   894A 0C          MOV DWORD PTR DS:[EDX+0xC],ECX
 *  0042CBAD   85C0             TEST EAX,EAX
 *  0042CBAF   74 16            JE SHORT .0042CBC7
 *  0042CBB1   8B48 0C          MOV ECX,DWORD PTR DS:[EAX+0xC]
 *  0042CBB4   F7D9             NEG ECX
 *  0042CBB6   894A 10          MOV DWORD PTR DS:[EDX+0x10],ECX
 *  0042CBB9   8B00             MOV EAX,DWORD PTR DS:[EAX]
 *  0042CBBB   83C0 28          ADD EAX,0x28
 *  0042CBBE   8942 14          MOV DWORD PTR DS:[EDX+0x14],EAX
 *  0042CBC1   B8 01000000      MOV EAX,0x1
 *  0042CBC6   C3               RETN
 *  0042CBC7   33C9             XOR ECX,ECX
 *  0042CBC9   F7D9             NEG ECX
 *  0042CBCB   894A 10          MOV DWORD PTR DS:[EDX+0x10],ECX
 *  0042CBCE   8B00             MOV EAX,DWORD PTR DS:[EAX]
 *  0042CBD0   83C0 28          ADD EAX,0x28
 *  0042CBD3   8942 14          MOV DWORD PTR DS:[EDX+0x14],EAX
 *  0042CBD6   B8 01000000      MOV EAX,0x1
 *  0042CBDB   C3               RETN
 *  0042CBDC   CC               INT3
 *  0042CBDD   CC               INT3
 *  0042CBDE   CC               INT3
 *  0042CBDF   CC               INT3
 *  0042CBE0   8B4424 04        MOV EAX,DWORD PTR SS:[ESP+0x4]
 *  0042CBE4   8B48 10          MOV ECX,DWORD PTR DS:[EAX+0x10]
 *  0042CBE7   8B50 0C          MOV EDX,DWORD PTR DS:[EAX+0xC]
 *  0042CBEA   51               PUSH ECX
 *  0042CBEB   8B48 08          MOV ECX,DWORD PTR DS:[EAX+0x8]
 *  0042CBEE   52               PUSH EDX
 *  0042CBEF   8B50 04          MOV EDX,DWORD PTR DS:[EAX+0x4]
 *  0042CBF2   8B00             MOV EAX,DWORD PTR DS:[EAX]
 *  0042CBF4   51               PUSH ECX
 *  0042CBF5   52               PUSH EDX
 *  0042CBF6   50               PUSH EAX
 *  0042CBF7   E8 E4FD0A00      CALL .004DC9E0
 *  0042CBFC   83C4 14          ADD ESP,0x14
 *  0042CBFF   C3               RETN
 *  0042CC00   8B4424 04        MOV EAX,DWORD PTR SS:[ESP+0x4]
 *  0042CC04   8B48 10          MOV ECX,DWORD PTR DS:[EAX+0x10]
 *  0042CC07   8B50 0C          MOV EDX,DWORD PTR DS:[EAX+0xC]
 *  0042CC0A   51               PUSH ECX
 *  0042CC0B   8B48 08          MOV ECX,DWORD PTR DS:[EAX+0x8]
 *  0042CC0E   52               PUSH EDX
 *  0042CC0F   8B50 04          MOV EDX,DWORD PTR DS:[EAX+0x4]
 *  0042CC12   8B00             MOV EAX,DWORD PTR DS:[EAX]
 *  0042CC14   51               PUSH ECX
 *  0042CC15   52               PUSH EDX
 *  0042CC16   50               PUSH EAX
 *  0042CC17   E8 C4FF0A00      CALL .004DCBE0
 *  0042CC1C   83C4 14          ADD ESP,0x14
 *  0042CC1F   C3               RETN
 *  0042CC20   8B4424 04        MOV EAX,DWORD PTR SS:[ESP+0x4]
 *  0042CC24   8B08             MOV ECX,DWORD PTR DS:[EAX]
 *  0042CC26   894C24 04        MOV DWORD PTR SS:[ESP+0x4],ECX
 *  0042CC2A   E9 71FB0A00      JMP .004DC7A0
 *  0042CC2F   CC               INT3
 *  0042CC30   56               PUSH ESI
 *  0042CC31   8B7424 08        MOV ESI,DWORD PTR SS:[ESP+0x8]
 *  0042CC35   8B06             MOV EAX,DWORD PTR DS:[ESI]
 *  0042CC37   50               PUSH EAX
 *  0042CC38   E8 63FB0A00      CALL .004DC7A0
 *  0042CC3D   D946 0C          FLD DWORD PTR DS:[ESI+0xC]
 *  0042CC40   D91C24           FSTP DWORD PTR SS:[ESP]
 *  0042CC43   83EC 08          SUB ESP,0x8
 *  0042CC46   D946 08          FLD DWORD PTR DS:[ESI+0x8]
 *  0042CC49   D95C24 04        FSTP DWORD PTR SS:[ESP+0x4]
 *  0042CC4D   D946 04          FLD DWORD PTR DS:[ESI+0x4]
 *  0042CC50   D91C24           FSTP DWORD PTR SS:[ESP]
 *  0042CC53   50               PUSH EAX
 *  0042CC54   E8 27680400      CALL .00473480
 *  0042CC59   83C4 10          ADD ESP,0x10
 *  0042CC5C   B8 01000000      MOV EAX,0x1
 *  0042CC61   5E               POP ESI
 *  0042CC62   C3               RETN
 *  0042CC63   CC               INT3
 *  0042CC64   CC               INT3
 *  0042CC65   CC               INT3
 *  0042CC66   CC               INT3
 *  0042CC67   CC               INT3
 *  0042CC68   CC               INT3
 *  0042CC69   CC               INT3 *
 */
namespace { // unnamed
/**
 *  Handle new lines and ruby.
 *
 *  そ�日、彼の言葉に耳を傾ける�ぁ�かった� *  ザールラント歴丹�〹� 二ノ月二十日<r>グローセン州 ヘルフォルト区郊� *
 *  僁�な霋�の後�r><ruby text='まぶ�>瞼</ruby>の裏を焼く陽光に気付いた� *
 *  気�く重�ruby text='まぶ�>瞼</ruby>を開け��r>見覚えのある輪郭が瞳に�り込む� *
 *  そ�日、彼の言葉に耳を傾ける�ぁ�かった。――尊厳を捨てて媚�る。それが生きることか?――��ぁ�敗北したのた誰しも少年の声を聞かず、蔑み、そして冷笑してぁ�。安寧の世がぁ�までも続くと信じてぁ�から。それでも、私�――。ザールラント歴丹�〹� 二ノ月二十日<r>グローセン州 ヘルフォルト区郊外僅かな霋�の後�r><ruby text='まぶ�>瞼</ruby>の裏を焼く陽光に気付いた。気�く重�ruby text='まぶ�>瞼</ruby>を開け��r>見覚えのある輪郭が瞳に�り込む
 */
bool EscudeFilter(LPVOID data, DWORD *size, HookParam *, BYTE)
{
  auto text = reinterpret_cast<LPSTR>(data);
  auto len = reinterpret_cast<size_t *>(size);
  StringCharReplacer(text, len, "<r>", 3, '\n');

  if (cpp_strnstr(text, "<ruby", *len)) {
    StringFilter(text, len, "</ruby>", 7);
    StringFilterBetween(text, len, "<ruby", 5, "'>", 2);
  }
  return true;
}
LPCSTR _escudeltrim(LPCSTR text)
{
  if (text && *text == '<')
    for (auto p = text; (signed char)*p > 0; p++)
      if (*p == '>')
        return p + 1;
  return text;
}
void SpecialHookEscude(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  DWORD arg1 = argof(1, esp_base);
  if (!arg1 || (LONG)arg1 == -1 || ::IsBadWritePtr((LPVOID)arg1, 4)) // this is indispensable
    return;
  LPCSTR text = (LPCSTR)*(DWORD *)(arg1 + 0x20);
  if (!text || ::IsBadWritePtr((LPVOID)text, 1) || !*text) // this is indispensable
    return;
  text = _escudeltrim(text);
  if (!text)
    return;
  *data = (DWORD)text;
  *len = ::strlen(text);
  *split = *(DWORD *)arg1;
}
} // unnamed namespace
bool InsertEscudeHook()
{
  const BYTE bytes[] = {
    0x76, 0x0a,             // 0042cb9c   76 0a            jbe short .0042cba8
    0x49,                   // 0042cb9e   49               dec ecx
    0x0f,0xaf,0x48, 0x0c    // 0042cb9f   0faf48 0c        imul ecx,dword ptr ds:[eax+0xc]
  };
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_limit_);
  //GROWL(addr);
  if (!addr) {
    ConsoleOutput("vnreng:Escude: pattern not found");
    return false;
  }
  addr = MemDbg::findEnclosingAlignedFunction(addr);
  if (!addr) {
    ConsoleOutput("vnreng:Escude: enclosing function not found");
    return false;
  }
  HookParam hp = {};
  hp.address = addr;
  hp.text_fun = SpecialHookEscude;
  hp.filter_fun = EscudeFilter;
  hp.type = USING_STRING|USING_SPLIT|NO_CONTEXT; // NO_CONTEXT as this function is only called by one caller anyway
  ConsoleOutput("vnreng: INSERT Escude");
  NewHook(hp, "Escude");
  return true;
}

/** jichi 8/23/2015 Tamamo
 *  Sample game: 閃光の騎士 ~カリスティアナイト~ Ver1.03
 *
 *  Debugging method: insert hw breakpoint to the text in memory
 *
 *  006107A6   76 08              JBE SHORT .006107B0
 *  006107A8   3BF8               CMP EDI,EAX
 *  006107AA   0F82 68030000      JB .00610B18
 *  006107B0   0FBA25 F88E7300 01 BT DWORD PTR DS:[0x738EF8],0x1
 *  006107B8   73 07              JNB SHORT .006107C1
 *  006107BA   F3:A4              REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI] ; jichi: accessed here
 *  006107BC   E9 17030000        JMP .00610AD8
 *  006107C1   81F9 80000000      CMP ECX,0x80
 *  006107C7   0F82 CE010000      JB .0061099B
 *  006107CD   8BC7               MOV EAX,EDI
 *  006107CF   33C6               XOR EAX,ESI
 *  006107D1   A9 0F000000        TEST EAX,0xF
 *  006107D6   75 0E              JNZ SHORT .006107E6
 *
 *  0012FD7C   0012FE1C
 *  0012FD80   00000059
 *  0012FD84   0051C298  RETURN to .0051C298 from .00610790
 *  0012FD88   0207E490	; jichi: target
 *  0012FD8C   0C0BE768	; jichi: source text
 *  0012FD90   00000059	; jichi: source size
 *  0012FD94   002A7C58
 *  0012FD98   0C1E7338
 *  0012FD9C   0012FE1C
 *  0012FDA0  /0012FDC0 ; jichi: split
 *  0012FDA4  |0056A83F  RETURN to .0056A83F from .0051C1C0
 *  0012FDA8  |0C1E733C
 *  0012FDAC  |00000000
 *  0012FDB0  |FFFFFFFF
 *  0012FDB4  |020EDAD0
 *  0012FDB8  |0220CC28
 *  0012FDBC  |020EDAD0
 *  0012FDC0  ]0012FE44
 *  0012FDC4  |0055EF84  RETURN to .0055EF84 from .0056A7B0
 *  0012FDC8  |0012FE1C
 *  0012FDCC  |ED1BC1C5
 *  0012FDD0  |020EDAD0
 *  0012FDD4  |002998A8
 *  0012FDD8  |020EDAD0
 *
 *  Hooked call:
 *  0051C283   5D               POP EBP
 *  0051C284   C2 0C00          RETN 0xC
 *  0051C287   8BD6             MOV EDX,ESI
 *  0051C289   85FF             TEST EDI,EDI
 *  0051C28B   74 0E            JE SHORT .0051C29B
 *  0051C28D   57               PUSH EDI
 *  0051C28E   8D040B           LEA EAX,DWORD PTR DS:[EBX+ECX]
 *  0051C291   50               PUSH EAX
 *  0051C292   52               PUSH EDX
 *  0051C293   E8 F8440F00      CALL .00610790    ; jichi: copy invoked here
 *  0051C298   83C4 0C          ADD ESP,0xC
 *  0051C29B   837E 14 10       CMP DWORD PTR DS:[ESI+0x14],0x10
 *  0051C29F   897E 10          MOV DWORD PTR DS:[ESI+0x10],EDI
 *  0051C2A2   72 0F            JB SHORT .0051C2B3
 *  0051C2A4   8B06             MOV EAX,DWORD PTR DS:[ESI]
 *  0051C2A6   C60438 00        MOV BYTE PTR DS:[EAX+EDI],0x0
 *  0051C2AA   8BC6             MOV EAX,ESI
 *  0051C2AC   5F               POP EDI
 *  0051C2AD   5E               POP ESI
 *  0051C2AE   5B               POP EBX
 *  0051C2AF   5D               POP EBP
 *  0051C2B0   C2 0C00          RETN 0xC
 *  0051C2B3   8BC6             MOV EAX,ESI
 *
 *  Sample text with new lines:
 *
 *  0C0BE748  70 00 69 00 2E 00 64 00 6C 00 6C 00 00 00 6C 00  p.i...d.l.l...l.
 *  0C0BE758  00 00 00 00 0F 00 00 00 8B 91 3F 66 00 00 00 88  .......拒?f...・
 *  0C0BE768  83 4E 83 8B 83 67 83 93 81 75 8E 84 82 C9 82 CD  クルトン「私には
 *  0C0BE778  95 90 91 95 82 AA 82 C2 82 A2 82 C4 82 A2 82 DC  武装がついていま
 *  0C0BE788  82 B9 82 F1 82 A9 82 E7 81 41 0D 0A 81 40 8D 55  せんから、.. 攻
 *  0C0BE798  82 DF 82 C4 82 B1 82 E7 82 EA 82 BD 82 E7 82 D0  めてこられたらひ
 *  0C0BE7A8  82 C6 82 BD 82 DC 82 E8 82 E0 82 A0 82 E8 82 DC  とたまりもありま
 *  0C0BE7B8  82 B9 82 F1 81 76 3C 65 3E 00 3E 00 3E 00 00 00  せん」<e>.>.>...
 *  0C0BE7C8  9E 91 3F 66 99 82 00 88 83 53 83 8D 81 5B 83 93  梠?f凾.・Sローン
 *  0C0BE7D8  8C 5A 81 75 82 D6 82 D6 81 42 95 D4 82 B5 82 C4  兄「へへ。返して
 *  0C0BE7E8  82 D9 82 B5 82 AF 82 E8 82 E1 82 C2 82 A2 82 C4  ほしけりゃついて
 *  0C0BE7F8  82 AB 82 C8 81 42 83 49 83 8C 82 B3 82 DC 82 CC  きな。オレさまの
 *
 *  Sample game: 冒険者の町を作ろう!2 Ver1.01
 *
 *  0068028B   CC               INT3
 *  0068028C   CC               INT3
 *  0068028D   CC               INT3
 *  0068028E   CC               INT3
 *  0068028F   CC               INT3
 *  00680290   55               PUSH EBP
 *  00680291   8BEC             MOV EBP,ESP
 *  00680293   57               PUSH EDI
 *  00680294   56               PUSH ESI
 *  00680295   8B75 0C          MOV ESI,DWORD PTR SS:[EBP+0xC]
 *  00680298   8B4D 10          MOV ECX,DWORD PTR SS:[EBP+0x10]
 *  0068029B   8B7D 08          MOV EDI,DWORD PTR SS:[EBP+0x8]
 *  0068029E   8BC1             MOV EAX,ECX
 *  006802A0   8BD1             MOV EDX,ECX
 *  006802A2   03C6             ADD EAX,ESI
 *  006802A4   3BFE             CMP EDI,ESI
 *  006802A6   76 08            JBE SHORT .006802B0
 *  006802A8   3BF8             CMP EDI,EAX
 *  006802AA   0F82 A4010000    JB .00680454
 *  006802B0   81F9 00010000    CMP ECX,0x100
 *  006802B6   72 1F            JB SHORT .006802D7
 *  006802B8   833D 64FB8C00 00 CMP DWORD PTR DS:[0x8CFB64],0x0
 *  006802BF   74 16            JE SHORT .006802D7
 *  006802C1   57               PUSH EDI
 *  006802C2   56               PUSH ESI
 *  006802C3   83E7 0F          AND EDI,0xF
 *  006802C6   83E6 0F          AND ESI,0xF
 *  006802C9   3BFE             CMP EDI,ESI
 *  006802CB   5E               POP ESI
 *  006802CC   5F               POP EDI
 *  006802CD   75 08            JNZ SHORT .006802D7
 *  006802CF   5E               POP ESI
 *  006802D0   5F               POP EDI
 *  006802D1   5D               POP EBP
 *  006802D2   E9 FC090100      JMP .00690CD3
 *  006802D7   F7C7 03000000    TEST EDI,0x3
 *  006802DD   75 15            JNZ SHORT .006802F4
 *  006802DF   C1E9 02          SHR ECX,0x2
 *  006802E2   83E2 03          AND EDX,0x3
 *  006802E5   83F9 08          CMP ECX,0x8
 *  006802E8   72 2A            JB SHORT .00680314
 *  006802EA   F3:A5            REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]  jichi: here
 *  006802EC   FF2495 04046800  JMP DWORD PTR DS:[EDX*4+0x680404]
 *  006802F3   90               NOP
 *  006802F4   8BC7             MOV EAX,EDI
 *  006802F6   BA 03000000      MOV EDX,0x3
 *  006802FB   83E9 04          SUB ECX,0x4
 *  006802FE   72 0C            JB SHORT .0068030C
 *  00680300   83E0 03          AND EAX,0x3
 *  00680303   03C8             ADD ECX,EAX
 *  00680305   FF2485 18036800  JMP DWORD PTR DS:[EAX*4+0x680318]
 *  0068030C   FF248D 14046800  JMP DWORD PTR DS:[ECX*4+0x680414]
 *  00680313   90               NOP
 *  00680314   FF248D 98036800  JMP DWORD PTR DS:[ECX*4+0x680398]
 *  0068031B   90               NOP
 *  0068031C   2803             SUB BYTE PTR DS:[EBX],AL
 *  0068031E   68 00540368      PUSH 0x68035400
 *  00680323   0078 03          ADD BYTE PTR DS:[EAX+0x3],BH
 *  00680326   68 0023D18A      PUSH 0x8AD12300
 *  0068032B   06               PUSH ES
 *  0068032C   8807             MOV BYTE PTR DS:[EDI],AL
 *  0068032E   8A46 01          MOV AL,BYTE PTR DS:[ESI+0x1]
 *  00680331   8847 01          MOV BYTE PTR DS:[EDI+0x1],AL
 *  00680334   8A46 02          MOV AL,BYTE PTR DS:[ESI+0x2]
 *
 *  0067FA4F   8BC6             MOV EAX,ESI
 *  0067FA51   EB 45            JMP SHORT .0067FA98
 *  0067FA53   397D 10          CMP DWORD PTR SS:[EBP+0x10],EDI
 *  0067FA56   74 16            JE SHORT .0067FA6E
 *  0067FA58   3975 0C          CMP DWORD PTR SS:[EBP+0xC],ESI
 *  0067FA5B   72 11            JB SHORT .0067FA6E
 *  0067FA5D   56               PUSH ESI
 *  0067FA5E   FF75 10          PUSH DWORD PTR SS:[EBP+0x10]
 *  0067FA61   FF75 08          PUSH DWORD PTR SS:[EBP+0x8]
 *  0067FA64   E8 27080000      CALL .00680290  ; jichi: copy invoked here
 *  0067FA69   83C4 0C          ADD ESP,0xC
 *  0067FA6C  ^EB C1            JMP SHORT .0067FA2F
 *  0067FA6E   FF75 0C          PUSH DWORD PTR SS:[EBP+0xC]
 *  0067FA71   57               PUSH EDI
 *  0067FA72   FF75 08          PUSH DWORD PTR SS:[EBP+0x8]
 *
 * 0012FC04   00000059
 * 0012FC08   00000000
 * 0012FC0C  /0012FC28
 * 0012FC10  |0067FA69  RETURN to .0067FA69 from .00680290
 * 0012FC14  |072CEF78  ; jichi: target text
 * 0012FC18  |07261840	; jichi: source text
 * 0012FC1C  |00000059	; jichi: source size
 * 0012FC20  |FFFFFFFE
 * 0012FC24  |00000000
 * 0012FC28  ]0012FC40  ; jichi: split
 * 0012FC2C  |00404E58  RETURN to .00404E58 from .0067FA1F
 * 0012FC30  |072CEF78	; jichi: target text
 * 0012FC34  |0000005F	; jichi: target capacity
 * 0012FC38  |07261840	; jichi: source text
 * 0012FC3C  |00000059	; jichi: source size
 * 0012FC40  ]0012FC58
 * 0012FC44  |00404E38  RETURN to .00404E38 from .00404E40
 * 0012FC48  |072CEF78
 * 0012FC4C  |0000005F
 * 0012FC50  |07261840
 * 0012FC54  |00000059
 * 0012FC58  ]0012FC78
 * 0012FC5C  |00404B06  RETURN to .00404B06 from .00404E20
 * 0012FC60  |072CEF78
 * 0012FC64  |0000005F
 * 0012FC68  |07261840
 * 0012FC6C  |00000059
 * 0012FC70  |00000000
 * 0012FC74  |0012FD30
 * 0012FC78  ]0012FC98
 * 0012FC7C  |004025FE  RETURN to .004025FE from .00404AE0
 * 0012FC80  |072CEF78
 * 0012FC84  |0000005F
 * 0012FC88  |07261840
 * 0012FC8C  |00000059
 * 0012FC90  |0012FD30
 * 0012FC94  |00000059
 * 0012FC98  ]0012FCB0
 * 0012FC9C  |0040254B  RETURN to .0040254B from .00402560
 * 0012FCA0  |074B6EA4
 * 0012FCA4  |00000000
 * 0012FCA8  |FFFFFFFF
 *
 * 07261840  83 4A 83 43 81 75 82 A0 82 C6 82 CD 82 B1 82 EA  カイ「あとはこれ
 * 07261850  82 C9 81 41 91 BA 92 B7 82 CC 83 54 83 43 83 93  に、村長のサイン
 * 07261860  82 C6 88 F3 8A D3 82 F0 81 63 81 63 82 C1 82 C6  と印鑑を……っと
 * 07261870  81 42 0D 0A 81 40 82 6E 82 6A 81 41 82 AB 82 E5  。.. OK、きょ
 * 07261880  82 A4 82 CC 83 66 83 58 83 4E 83 8F 81 5B 83 4E  うのデスクワーク
 * 07261890  8F 49 97 B9 81 76 3C 65 3E 00 81 76 3C 65 3E 00  終了」<e>.」<e>.
 * 072618A0  98 DD 95 48 00 40 00 88 83 4A 83 43 81 75 81 63  俤菱.@.・Jイ「…
 * 072618B0  81 63 82 A4 82 F1 81 41 82 BB 82 A4 82 B5 82 E6  …うん、そうしよ
 */
namespace { // unnamed
bool TamamoFilter(LPVOID data, DWORD *size, HookParam *, BYTE)
{
  LPSTR text = (LPSTR)data;
  if (::memchr(text, '<', *size))
    StringFilter(text, reinterpret_cast<size_t *>(size), "<e>", 3);
  StringFilter(text, reinterpret_cast<size_t *>(size), "\x0d\x0a\x81\x40", 4); // remove \n before space
  return true;
}
void SpecialHookTamamo(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  auto text = (LPCSTR)argof(2 - 1, esp_base); // arg2
  auto size = argof(3 - 1, esp_base); // arg3
  size = ::strlen(text);
  if (0 < size && size < VNR_TEXT_CAPACITY && size == ::strlen(text) && !all_ascii(text)) {
    *data = (DWORD)text;
    //*len = argof(esp_base, 3 - 1);
    *len = size;
    //*split = argof(8 - 1, esp_base); // use parent return address as split
    //*split = argof(7 - 1, esp_base); // use the address just before parent retaddr
    *split = argof(6 - 1, esp_base);
    //if (hp.split)
    //  *split = *(DWORD *)(esp_base + hp.split);
  }
}
} // unnamed namespace
bool InsertTamamoHook()
{
  ULONG addr = 0;
  { // for new games
    const BYTE bytes[] = {
      0x8b,0xd6,      // 0051c287   8bd6             mov edx,esi
      0x85,0xff,      // 0051c289   85ff             test edi,edi
      0x74, 0x0e,     // 0051c28b   74 0e            je short .0051c29b
      0x57,           // 0051c28d   57               push edi
      0x8d,0x04,0x0b, // 0051c28e   8d040b           lea eax,dword ptr ds:[ebx+ecx]
      0x50,           // 0051c291   50               push eax
      0x52,           // 0051c292   52               push edx
      0xe8 //f8440f00 // 0051c293   e8 f8440f00      call .00610790    ; jichi: copy invoked here
    };
    enum { addr_offset = sizeof(bytes) - 1 };
    addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_limit_);
    if (addr) {
      addr += addr_offset;
      ConsoleOutput("vnreng:Tamamo: pattern for new version found");
    }
  }
  if (!addr) { // for old games
    const BYTE bytes[] = {
      0x72, 0x11,       // 0067fa5b   72 11            jb short .0067fa6e
      0x56,             // 0067fa5d   56               push esi
      0xff,0x75, 0x10,  // 0067fa5e   ff75 10          push dword ptr ss:[ebp+0x10]
      0xff,0x75, 0x08,  // 0067fa61   ff75 08          push dword ptr ss:[ebp+0x8]
      0xe8 // 27080000  // 0067fa64   e8 27080000      call .00680290  ; jichi: copy invoked here
    };
    enum { addr_offset = sizeof(bytes) - 1 };
    addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_limit_);
    if (addr) {
      addr += addr_offset;
      ConsoleOutput("vnreng:Tamamo: pattern for old version found");
    }
  }
  if (!addr) {
    ConsoleOutput("vnreng:Tamamo: pattern not found");
    return false;
  }
  HookParam hp = {};
  hp.address = addr;
  hp.text_fun = SpecialHookTamamo;
  hp.filter_fun = TamamoFilter;
  hp.type = USING_STRING|USING_SPLIT|NO_CONTEXT;
  ConsoleOutput("vnreng: INSERT Tamamo");
  NewHook(hp, "Tamamo");
  return true;
}

/** Game-specific engines */

//static char* ShinyDaysQueueString[0x10];
//static int ShinyDaysQueueStringLen[0x10];
//static int ShinyDaysQueueIndex, ShinyDaysQueueNext;
static void SpecialGameHookShinyDays(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  static int ShinyDaysQueueStringLen;
  LPWSTR fun_str;
  char *text_str;
  DWORD l = 0;
  __asm
  {
    mov eax,esp_base
    mov ecx,[eax+0x4C]
    mov fun_str,ecx
    mov esi,[eax+0x70]
    mov edi,[eax+0x74]
    add esi,0x3C
    cmp esi,edi
    jae _no_text
    mov edx,[esi+0x10]
    mov ecx,esi
    cmp edx,8
    cmovae ecx,[ecx]
    add edx,edx
    mov text_str,ecx
    mov l,edx
_no_text:
  }
  if (::memcmp(fun_str, L"[PlayVoice]",0x18) == 0) {
    *data = (DWORD)text_buffer;
    *len = ShinyDaysQueueStringLen;
  }
  else if (::memcmp(fun_str, L"[PrintText]",0x18) == 0) {
    memcpy(text_buffer, text_str, l);
    ShinyDaysQueueStringLen = l;
  }
}
bool InsertShinyDaysGameHook()
{
  const BYTE bytes[] = {
    0xff,0x83,0x70,0x03,0x00,0x00,0x33,0xf6,
    0xc6,0x84,0x24,0x90,0x02,0x00,0x00,0x02
  };
  LPVOID addr = (LPVOID)0x42ad94;
  if (::memcmp(addr, bytes, sizeof(bytes)) != 0) {
    ConsoleOutput("vnreng:ShinyDays: only work for 1.00");
    return false;
  }

  HookParam hp = {};
  hp.address = 0x42ad9c;
  hp.text_fun = SpecialGameHookShinyDays;
  hp.type = USING_UNICODE|USING_STRING|NO_CONTEXT;
  ConsoleOutput("vnreng: INSERT ShinyDays");
  NewHook(hp, "ShinyDays 1.00");
  return true;
}

#if 0 // disabled as lova does not allow module from being modified
/** 7/19/2015: Game engine specific for http://lova.jp
 *
 *  No idea why hooking to this place will crash the game.
 *
 *  Debugging method:
 *  - Find text in UTF8/UTF16
 *    There is one UTF8 matched, and 2 UTF16
 *  - Use virtual machine to find where UTF8 is MODIFIED
 *    It is modified in msvcrt
 *  - Backtrack the stack to find where text is accessed in main module
 *
 *  Base addr = 05f0000
 *
 *  012FF246   C64418 08 00     MOV BYTE PTR DS:[EAX+EBX+0x8],0x0
 *  012FF24B   C740 04 01000000 MOV DWORD PTR DS:[EAX+0x4],0x1
 *  012FF252   8918             MOV DWORD PTR DS:[EAX],EBX
 *  012FF254   8BF0             MOV ESI,EAX
 *  012FF256   8B45 08          MOV EAX,DWORD PTR SS:[EBP+0x8]
 *  012FF259   53               PUSH EBX
 *  012FF25A   50               PUSH EAX
 *  012FF25B   8D4E 08          LEA ECX,DWORD PTR DS:[ESI+0x8]
 *  012FF25E   51               PUSH ECX
 *  012FF25F   E8 CEAE2A00      CALL .015AA132                           ; JMP to msvcr100.memcpy, copied here
 *  012FF264   8B07             MOV EAX,DWORD PTR DS:[EDI]
 *  012FF266   83E0 03          AND EAX,0x3
 *  012FF269   0BF0             OR ESI,EAX
 *  012FF26B   83C4 0C          ADD ESP,0xC
 *  012FF26E   8937             MOV DWORD PTR DS:[EDI],ESI
 *  012FF270   8B75 FC          MOV ESI,DWORD PTR SS:[EBP-0x4]
 */
bool InsertLovaGameHook()
{
  ULONG startAddress, stopAddress;
  if (!NtInspect::getProcessMemoryRange(&startAddress, &stopAddress)) { // need accurate stopAddress
    ConsoleOutput("vnreng:LOVA: failed to get memory range");
    return false;
  }

  const BYTE bytes[] = {
    0xC6,0x44,0x18, 0x08, 0x00,           // 012FF246   C64418 08 00     MOV BYTE PTR DS:[EAX+EBX+0x8],0x0
    0xC7,0x40, 0x04, 0x01,0x00,0x00,0x00, // 012FF24B   C740 04 01000000 MOV DWORD PTR DS:[EAX+0x4],0x1
    0x89,0x18,                            // 012FF252   8918             MOV DWORD PTR DS:[EAX],EBX
    0x8B,0xF0,                            // 012FF254   8BF0             MOV ESI,EAX
    0x8B,0x45, 0x08,                      // 012FF256   8B45 08          MOV EAX,DWORD PTR SS:[EBP+0x8]
    0x53,                                 // 012FF259   53               PUSH EBX
    0x50,                                 // 012FF25A   50               PUSH EAX
    0x8D,0x4E, 0x08,                      // 012FF25B   8D4E 08          LEA ECX,DWORD PTR DS:[ESI+0x8]
    0x51,                                 // 012FF25E   51               PUSH ECX
    0xE8 //CEAE2A00                       // 012FF25F   E8 CEAE2A00      CALL .015AA132                           ; JMP to msvcr100.memcpy, copied here
  };
  enum { addr_offset = sizeof(bytes) - 1 };
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress);
  if (!addr) {
    ConsoleOutput("vnreng:LOVA: could not find instruction pattern");
    return false;
  }

  HookParam hp = {};
  hp.address = addr + addr_offset;
  //hp.text_fun = SpecialGameHookLova;
  hp.offset = 4 * 2; // source in arg2
  hp.type = USING_STRING|RELATIVE_SPLIT;
  ConsoleOutput("vnreng: INSERT LOVA");
  NewHook(hp, "LOVA");
  return true;
}
#endif // 0

/**
 *  jichi 4/15/2014: Insert Adobe AIR hook
 *  Sample games:
 *  華アワセ 蛟編: /HW-C*0:D8@4D04B5:Adobe AIR.dll
 *  華アワセ 姫空木編: /HW-C*0:d8@4E69A7:Adobe AIR.dll
 *
 *  Issue: The game will hang if the hook is injected before loading
 *
 *  /HW-C*0:D8@4D04B5:ADOBE AIR.DLL
 *  - addr: 5047477 = 0x4d04b5
 *  -length_offset: 1
 *  - module: 3506957663 = 0xd107ed5f
 *  - off: 4294967280 = 0xfffffff0 = -0x10
 *  - split: 216 = 0xd8
 *  - type: 90 = 0x5a
 *
 *  0f8f0497  |. eb 69         jmp short adobe_ai.0f8f0502
 *  0f8f0499  |> 83c8 ff       or eax,0xffffffff
 *  0f8f049c  |. eb 67         jmp short adobe_ai.0f8f0505
 *  0f8f049e  |> 8b7d 0c       mov edi,dword ptr ss:[ebp+0xc]
 *  0f8f04a1  |. 85ff          test edi,edi
 *  0f8f04a3  |. 7e 5d         jle short adobe_ai.0f8f0502
 *  0f8f04a5  |. 8b55 08       mov edx,dword ptr ss:[ebp+0x8]
 *  0f8f04a8  |. b8 80000000   mov eax,0x80
 *  0f8f04ad  |. be ff030000   mov esi,0x3ff
 *  0f8f04b2  |> 0fb70a        /movzx ecx,word ptr ds:[edx]
 *  0f8f04b5  |. 8bd8          |mov ebx,eax ; jichi: hook here
 *  0f8f04b7  |. 4f            |dec edi
 *  0f8f04b8  |. 66:3bcb       |cmp cx,bx
 *  0f8f04bb  |. 73 05         |jnb short adobe_ai.0f8f04c2
 *  0f8f04bd  |. ff45 fc       |inc dword ptr ss:[ebp-0x4]
 *  0f8f04c0  |. eb 3a         |jmp short adobe_ai.0f8f04fc
 *  0f8f04c2  |> bb 00080000   |mov ebx,0x800
 *  0f8f04c7  |. 66:3bcb       |cmp cx,bx
 *  0f8f04ca  |. 73 06         |jnb short adobe_ai.0f8f04d2
 *  0f8f04cc  |. 8345 fc 02    |add dword ptr ss:[ebp-0x4],0x2
 *  0f8f04d0  |. eb 2a         |jmp short adobe_ai.0f8f04fc
 *  0f8f04d2  |> 81c1 00280000 |add ecx,0x2800
 *  0f8f04d8  |. 8bde          |mov ebx,esi
 *  0f8f04da  |. 66:3bcb       |cmp cx,bx
 *  0f8f04dd  |. 77 19         |ja short adobe_ai.0f8f04f8
 *  0f8f04df  |. 4f            |dec edi
 *  0f8f04e0  |.^78 b7         |js short adobe_ai.0f8f0499
 *  0f8f04e2  |. 42            |inc edx
 *  0f8f04e3  |. 42            |inc edx
 *  0f8f04e4  |. 0fb70a        |movzx ecx,word ptr ds:[edx]
 *  0f8f04e7  |. 81c1 00240000 |add ecx,0x2400
 *  0f8f04ed  |. 66:3bcb       |cmp cx,bx
 *  0f8f04f0  |. 77 06         |ja short adobe_ai.0f8f04f8
 *  0f8f04f2  |. 8345 fc 04    |add dword ptr ss:[ebp-0x4],0x4
 *  0f8f04f6  |. eb 04         |jmp short adobe_ai.0f8f04fc
 *  0f8f04f8  |> 8345 fc 03    |add dword ptr ss:[ebp-0x4],0x3
 *  0f8f04fc  |> 42            |inc edx
 *  0f8f04fd  |. 42            |inc edx
 *  0f8f04fe  |. 85ff          |test edi,edi
 *  0f8f0500  |.^7f b0         \jg short adobe_ai.0f8f04b2
 *  0f8f0502  |> 8b45 fc       mov eax,dword ptr ss:[ebp-0x4]
 *  0f8f0505  |> 5f            pop edi
 *  0f8f0506  |. 5e            pop esi
 *  0f8f0507  |. 5b            pop ebx
 *  0f8f0508  |. c9            leave
 *  0f8f0509  \. c3            retn
 */
bool InsertAdobeAirHook()
{
  enum { module = 0xd107ed5f }; // hash of "Adobe AIR.dll"
  DWORD base = Util::FindModuleBase(module);
  if (!base) {
    ConsoleOutput("vnreng:Adobe AIR: module not found");
    return false;
  }

  //ULONG startAddress, stopAddress;
  //if (!NtInspect::getModuleMemoryRange(L"Adobe AIR.dll", &startAddress, &stopAddress)) {
  //  ConsoleOutput("vnreng:Adobe AIR: module not found");
  //  return false;
  //}

  const BYTE bytes[] = {
    0x0f,0xb7,0x0a,  // 0f8f04b2  |> 0fb70a        /movzx ecx,word ptr ds:[edx]
    0x8b,0xd8,       // 0f8f04b5  |. 8bd8          |mov ebx,eax ; jichi: hook here
    0x4f,            // 0f8f04b7  |. 4f            |dec edi
    0x66,0x3b,0xcb,  // 0f8f04b8  |. 66:3bcb       |cmp cx,bx
    0x73, 0x05,      // 0f8f04bb  |. 73 05         |jnb short adobe_ai.0f8f04c2
    0xff,0x45, 0xfc, // 0f8f04bd  |. ff45 fc       |inc dword ptr ss:[ebp-0x4]
    0xeb, 0x3a       // 0f8f04c0  |. eb 3a         |jmp short adobe_ai.0f8f04fc
  };
  enum { addr_offset = 0x0f8f04b5 - 0x0f8f04b2 }; // = 3. 0 also works.
  enum { range = 0x600000 }; // larger than relative addresses
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), base, base + range);
  //GROWL(reladdr);
  if (!addr) {
    ConsoleOutput("vnreng:Adobe AIR: pattern not found");
    return false;
  }

  HookParam hp = {};
  hp.address = addr + addr_offset;
  //hp.module = module;
  hp.length_offset = 1;
  hp.offset = -0x10;
  hp.split = 0xd8;
  //hp.type = USING_SPLIT|MODULE_OFFSET|USING_UNICODE|DATA_INDIRECT; // 0x5a;
  hp.type = USING_SPLIT|USING_UNICODE|DATA_INDIRECT;

  ConsoleOutput("vnreng: INSERT Adobe AIR");
  NewHook(hp, "Adobe AIR");
  return true;
}

/** jichi 10/31/2014 Adobe Flash Player v10
 *
 *  Sample game: [141031] [ヂ�ンクルベル] 輪舞曲Duo
 *
 *  Debug method: Hex utf16 text, then insert hw breakpoints
 *    21:51 3110% hexstr 『何よ utf16
 *    0e30554f8830
 *
 *  There are also UTF-8 strings in the memory. I could not find a good place to hook
 *  using hw breakpoints.
 *
 *  There are lots of matches. One is selected. Then, the enclosing function is selected.
 *  arg1 is the UNICODE text.
 *
 *  Pattern:
 *
 *  0161293a   8bc6             mov eax,esi
 *  0161293c   5e               pop esi
 *  0161293d   c2 0800          retn 0x8
 *
 *  Function starts
 *  01612940   8b4c24 0c        mov ecx,dword ptr ss:[esp+0xc] ; jichi: hook here
 *  01612944   53               push ebx
 *  01612945   55               push ebp
 *  01612946   56               push esi
 *  01612947   57               push edi
 *  01612948   33ff             xor edi,edi
 *  0161294a   85c9             test ecx,ecx
 *  0161294c   0f84 5f010000    je ron2.01612ab1
 *  01612952   397c24 18        cmp dword ptr ss:[esp+0x18],edi
 *  01612956   0f8e ba010000    jle ron2.01612b16
 *  0161295c   8b6c24 14        mov ebp,dword ptr ss:[esp+0x14]
 *  01612960   be 01000000      mov esi,0x1
 *  01612965   eb 09            jmp short ron2.01612970
 *  01612967   8da424 00000000  lea esp,dword ptr ss:[esp]
 *  0161296e   8bff             mov edi,edi
 *  01612970   0fb755 00        movzx edx,word ptr ss:[ebp]
 *  01612974   297424 18        sub dword ptr ss:[esp+0x18],esi
 *  01612978   b8 80000000      mov eax,0x80
 *  0161297d   66:3bd0          cmp dx,ax
 *  01612980   73 15            jnb short ron2.01612997
 *  01612982   297424 20        sub dword ptr ss:[esp+0x20],esi
 *  01612986   0f88 1d010000    js ron2.01612aa9
 *  0161298c   8811             mov byte ptr ds:[ecx],dl
 *  0161298e   03ce             add ecx,esi
 *  01612990   03fe             add edi,esi
 *  01612992   e9 fd000000      jmp ron2.01612a94
 *  01612997   b8 00080000      mov eax,0x800
 *  0161299c   66:3bd0          cmp dx,ax
 *  0161299f   73 2a            jnb short ron2.016129cb
 *  016129a1   836c24 20 02     sub dword ptr ss:[esp+0x20],0x2
 *  016129a6   0f88 fd000000    js ron2.01612aa9
 *  016129ac   8bc2             mov eax,edx
 *  016129ae   c1e8 06          shr eax,0x6
 *  016129b1   24 1f            and al,0x1f
 *  016129b3   0c c0            or al,0xc0
 *  016129b5   8801             mov byte ptr ds:[ecx],al
 *  016129b7   80e2 3f          and dl,0x3f
 *  016129ba   03ce             add ecx,esi
 *  016129bc   80ca 80          or dl,0x80
 *  016129bf   8811             mov byte ptr ds:[ecx],dl
 *  016129c1   03ce             add ecx,esi
 *  016129c3   83c7 02          add edi,0x2
 *  016129c6   e9 c9000000      jmp ron2.01612a94
 *  016129cb   8d82 00280000    lea eax,dword ptr ds:[edx+0x2800]
 *  016129d1   bb ff030000      mov ebx,0x3ff
 *  016129d6   66:3bc3          cmp ax,bx
 *  016129d9   77 7b            ja short ron2.01612a56
 *  016129db   297424 18        sub dword ptr ss:[esp+0x18],esi
 *  016129df   0f88 c4000000    js ron2.01612aa9
 *  016129e5   0fb775 02        movzx esi,word ptr ss:[ebp+0x2]
 *  016129e9   83c5 02          add ebp,0x2
 *  016129ec   8d86 00240000    lea eax,dword ptr ds:[esi+0x2400]
 *  016129f2   66:3bc3          cmp ax,bx
 *  016129f5   77 58            ja short ron2.01612a4f
 *  016129f7   0fb7d2           movzx edx,dx
 *  016129fa   81ea f7d70000    sub edx,0xd7f7
 *  01612a00   0fb7c6           movzx eax,si
 *  01612a03   c1e2 0a          shl edx,0xa
 *  01612a06   03d0             add edx,eax
 *  01612a08   836c24 20 04     sub dword ptr ss:[esp+0x20],0x4
 *  01612a0d   0f88 96000000    js ron2.01612aa9
 *  01612a13   8bc2             mov eax,edx
 *  01612a15   c1e8 12          shr eax,0x12
 *  01612a18   24 07            and al,0x7
 *  01612a1a   0c f0            or al,0xf0
 *  01612a1c   8801             mov byte ptr ds:[ecx],al
 *  01612a1e   8bc2             mov eax,edx
 *  01612a20   c1e8 0c          shr eax,0xc
 *  01612a23   24 3f            and al,0x3f
 *  01612a25   be 01000000      mov esi,0x1
 *  01612a2a   0c 80            or al,0x80
 *  01612a2c   880431           mov byte ptr ds:[ecx+esi],al
 *  01612a2f   03ce             add ecx,esi
 *  01612a31   8bc2             mov eax,edx
 *  01612a33   c1e8 06          shr eax,0x6
 *  01612a36   03ce             add ecx,esi
 *  01612a38   24 3f            and al,0x3f
 *  01612a3a   0c 80            or al,0x80
 *  01612a3c   8801             mov byte ptr ds:[ecx],al
 *  01612a3e   80e2 3f          and dl,0x3f
 *  01612a41   03ce             add ecx,esi
 *  01612a43   80ca 80          or dl,0x80
 *  01612a46   8811             mov byte ptr ds:[ecx],dl
 *  01612a48   03ce             add ecx,esi
 *  01612a4a   83c7 04          add edi,0x4
 *  01612a4d   eb 45            jmp short ron2.01612a94
 *  01612a4f   be 01000000      mov esi,0x1
 *  01612a54   eb 0b            jmp short ron2.01612a61
 *  01612a56   8d82 00240000    lea eax,dword ptr ds:[edx+0x2400]
 *  01612a5c   66:3bc3          cmp ax,bx
 *  01612a5f   77 05            ja short ron2.01612a66
 *  01612a61   ba fdff0000      mov edx,0xfffd
 *  01612a66   836c24 20 03     sub dword ptr ss:[esp+0x20],0x3
 *  01612a6b   78 3c            js short ron2.01612aa9
 *  01612a6d   8bc2             mov eax,edx
 *  01612a6f   c1e8 0c          shr eax,0xc
 *  01612a72   24 0f            and al,0xf
 *  01612a74   0c e0            or al,0xe0
 *  01612a76   8801             mov byte ptr ds:[ecx],al
 *  01612a78   8bc2             mov eax,edx
 *  01612a7a   c1e8 06          shr eax,0x6
 *  01612a7d   03ce             add ecx,esi
 *  01612a7f   24 3f            and al,0x3f
 *  01612a81   0c 80            or al,0x80
 *  01612a83   8801             mov byte ptr ds:[ecx],al
 *  01612a85   80e2 3f          and dl,0x3f
 *  01612a88   03ce             add ecx,esi
 *  01612a8a   80ca 80          or dl,0x80
 *  01612a8d   8811             mov byte ptr ds:[ecx],dl
 *  01612a8f   03ce             add ecx,esi
 *  01612a91   83c7 03          add edi,0x3
 *  01612a94   83c5 02          add ebp,0x2
 *  01612a97   837c24 18 00     cmp dword ptr ss:[esp+0x18],0x0
 *  01612a9c  ^0f8f cefeffff    jg ron2.01612970
 *  01612aa2   8bc7             mov eax,edi
 *  01612aa4   5f               pop edi
 *  01612aa5   5e               pop esi
 *  01612aa6   5d               pop ebp
 *  01612aa7   5b               pop ebx
 *  01612aa8   c3               retn
 *  01612aa9   5f               pop edi
 *  01612aaa   5e               pop esi
 *  01612aab   5d               pop ebp
 *  01612aac   83c8 ff          or eax,0xffffffff
 *  01612aaf   5b               pop ebx
 *  01612ab0   c3               retn
 *  01612ab1   8b4424 18        mov eax,dword ptr ss:[esp+0x18]
 *  01612ab5   85c0             test eax,eax
 *  01612ab7   7e 5d            jle short ron2.01612b16
 *  01612ab9   8b5424 14        mov edx,dword ptr ss:[esp+0x14]
 *  01612abd   8d49 00          lea ecx,dword ptr ds:[ecx]
 *  01612ac0   0fb70a           movzx ecx,word ptr ds:[edx] ; jichi: this is where the text is accessed
 *  01612ac3   be 80000000      mov esi,0x80
 *  01612ac8   48               dec eax
 *  01612ac9   66:3bce          cmp cx,si
 *  01612acc   73 03            jnb short ron2.01612ad1
 *  01612ace   47               inc edi
 *  01612acf   eb 3e            jmp short ron2.01612b0f
 *  01612ad1   be 00080000      mov esi,0x800
 *  01612ad6   66:3bce          cmp cx,si
 *  01612ad9   73 05            jnb short ron2.01612ae0
 *  01612adb   83c7 02          add edi,0x2
 *  01612ade   eb 2f            jmp short ron2.01612b0f
 *  01612ae0   81c1 00280000    add ecx,0x2800
 *  01612ae6   be ff030000      mov esi,0x3ff
 *  01612aeb   66:3bce          cmp cx,si
 *  01612aee   77 1c            ja short ron2.01612b0c
 *  01612af0   83e8 01          sub eax,0x1
 *  01612af3  ^78 b4            js short ron2.01612aa9
 *  01612af5   0fb74a 02        movzx ecx,word ptr ds:[edx+0x2]
 *  01612af9   83c2 02          add edx,0x2
 *  01612afc   81c1 00240000    add ecx,0x2400
 *  01612b02   66:3bce          cmp cx,si
 *  01612b05   77 05            ja short ron2.01612b0c
 *  01612b07   83c7 04          add edi,0x4
 *  01612b0a   eb 03            jmp short ron2.01612b0f
 *  01612b0c   83c7 03          add edi,0x3
 *  01612b0f   83c2 02          add edx,0x2
 *  01612b12   85c0             test eax,eax
 *  01612b14  ^7f aa            jg short ron2.01612ac0
 *  01612b16   8bc7             mov eax,edi
 *  01612b18   5f               pop edi
 *  01612b19   5e               pop esi
 *  01612b1a   5d               pop ebp
 *  01612b1b   5b               pop ebx
 *  01612b1c   c3               retn
 *  01612b1d   cc               int3
 *  01612b1e   cc               int3
 *  01612b1f   cc               int3
 *
 *  Runtime stack:
 *  0019e974   0161640e  return to Ron2.0161640e from Ron2.01612940
 *  0019e978   1216c180  UNICODE "Dat/Chr/HAL_061.swf"
 *  0019e97c   00000013
 *  0019e980   12522838
 *  0019e984   00000013
 *  0019e988   0210da80
 *  0019e98c   0019ecb0
 *  0019e990   0019e9e0
 *  0019e994   0019ea24
 *  0019e998   0019e9cc
 *
 *  Runtime registers:
 *  EAX 12522838
 *  ECX 1216C180 UNICODE "Dat/Chr/HAL_061.swf"
 *  EDX 0C5E9898
 *  EBX 12532838
 *  ESP 0019E974
 *  EBP 00000013
 *  ESI 00000013
 *  EDI 0019E9CC
 *  EIP 01612940 Ron2.01612940
 */
// Skip ASCII garbage such as: Dat/Chr/HAL_061.swf
static bool AdobeFlashFilter(LPVOID data, DWORD *size, HookParam *, BYTE)
{
  // TODO: Remove [0-9a-zA-Z./]{4,} as garbage
  LPCWSTR p = reinterpret_cast<LPCWSTR>(data);
  size_t len = *size / 2;
  for (size_t i = 0; i < len; i++)
    if (p[i] & 0xff00)
      return true;
  return false;
}
bool InsertAdobeFlash10Hook()
{
  const BYTE bytes[] = {
    0x8b,0x4c,0x24, 0x0c,   // 01612940   8b4c24 0c        mov ecx,dword ptr ss:[esp+0xc] ; jichi: hook here
    0x53,                   // 01612944   53               push ebx
    0x55,                   // 01612945   55               push ebp
    0x56,                   // 01612946   56               push esi
    0x57,                   // 01612947   57               push edi
    0x33,0xff,              // 01612948   33ff             xor edi,edi
    0x85,0xc9,              // 0161294a   85c9             test ecx,ecx
    0x0f,0x84 //, 5f010000  // 0161294c   0f84 5f010000    je ron2.01612ab1
  };
  ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), module_base_, module_limit_);
  //addr = 0x01612940;
  //addr = 0x01612AC0;
  if (!addr) {
    ConsoleOutput("vnreng:AdobeFlash10: pattern not found");
    return false;
  }

  HookParam hp = {};
  hp.address = addr;
  hp.offset = 1 * 4; // arg1
  //hp.length_offset = 2 * 4; // arg2 might be the length
  hp.type = USING_UNICODE;
  hp.filter_fun = AdobeFlashFilter;
  ConsoleOutput("vnreng: INSERT Adobe Flash 10");
  NewHook(hp, "Adobe Flash 10");

  ConsoleOutput("vnreng:AdobeFlash10: disable GDI hooks");
  DisableGDIHooks();
  return true;
}

/** jichi 12/26/2014 Mono
 *  Sample game: [141226] ハ�レ�めいと
 */
static void SpecialHookMonoString(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  if (auto p = (MonoString *)argof(1, esp_base)) {
    *data = (DWORD)p->chars;
    *len = p->length * 2; // for widechar

    auto s = regof(ecx, esp_base);
    for (int i = 0; i < 0x10; i++) // traverse pointers until a non-readable address is met
      if (s && !::IsBadReadPtr((LPCVOID)s, sizeof(DWORD)))
        s = *(DWORD *)s;
      else
        break;
    if (!s)
      s = hp->address;
    *split = s;
  }
}

bool InsertMonoHooks()
{
  HMODULE h = ::GetModuleHandleA("mono.dll");
  if (!h)
    return false;

  bool ret = false;

  // mono_unichar2* mono_string_to_utf16       (MonoString *s);
  // char*          mono_string_to_utf8        (MonoString *s);
  HookParam hp = {};
  const MonoFunction funcs[] = { MONO_FUNCTIONS_INITIALIZER };
  enum { FunctionCount = sizeof(funcs) / sizeof(*funcs) };
  for (int i = 0; i < FunctionCount; i++) {
    const auto &it = funcs[i];
    if (FARPROC addr = ::GetProcAddress(h, it.functionName)) {
      hp.address = (DWORD)addr;
      hp.type = it.hookType|NO_ASCII; // 11/27/2015: Disable ascii string
      hp.offset = it.textIndex * 4;
      hp.length_offset = it.lengthIndex * 4;
      hp.text_fun = (HookParam::text_fun_t)it.text_fun;
      ConsoleOutput("vnreng: Mono: INSERT");
      NewHook(hp, it.functionName);
      ret = true;
    }
  }

  if (!ret)
    ConsoleOutput("vnreng: Mono: failed to find function address");
  return ret;
}

/** jichi 7/20/2014 Dolphin
 *  Tested with Dolphin 4.0
 */
bool InsertGCHooks()
{
  // TODO: Add generic hooks
  return InsertVanillawareGCHook();
  //return false;
}

/** jichi 7/20/2014 Vanillaware
 *  Tested game: 朧村正
 *
 *  Debugging method: grep the saving message
 *
 *  1609415e   cc               int3
 *  1609415f   cc               int3
 *  16094160   77 0f            ja short 16094171
 *  16094162   c705 00fb6701 80>mov dword ptr ds:[0x167fb00],0x80216b80
 *  1609416c  -e9 f9be06f1      jmp 0710006a
 *  16094171   8b35 8cf86701    mov esi,dword ptr ds:[0x167f88c]
 *  16094177   81c6 ffffffff    add esi,-0x1
 *  1609417d   8bce             mov ecx,esi
 *  1609417f   81c1 01000000    add ecx,0x1
 *  16094185   f7c1 0000000c    test ecx,0xc000000
 *  1609418b   74 0b            je short 16094198
 *  1609418d   51               push ecx
 *  1609418e   e8 36bff9f2      call 090300c9
 *  16094193   83c4 04          add esp,0x4
 *  16094196   eb 11            jmp short 160941a9
 *  16094198   8bc1             mov eax,ecx
 *  1609419a   81e0 ffffff3f    and eax,0x3fffffff
 *  160941a0   0fb680 00000810  movzx eax,byte ptr ds:[eax+0x10080000] ; jichi: hook here
 *  160941a7   66:90            nop
 *  160941a9   81c6 01000000    add esi,0x1
 *  160941af   8905 80f86701    mov dword ptr ds:[0x167f880],eax
 *  160941b5   813d 80f86701 00>cmp dword ptr ds:[0x167f880],0x0
 *  160941bf   c705 8cf86701 00>mov dword ptr ds:[0x167f88c],0x0
 *  160941c9   8935 90f86701    mov dword ptr ds:[0x167f890],esi
 *  160941cf   7c 14            jl short 160941e5
 *  160941d1   7f 09            jg short 160941dc
 *  160941d3   c605 0cfb6701 02 mov byte ptr ds:[0x167fb0c],0x2
 *  160941da   eb 26            jmp short 16094202
 *  160941dc   c605 0cfb6701 04 mov byte ptr ds:[0x167fb0c],0x4
 *  160941e3   eb 07            jmp short 160941ec
 *  160941e5   c605 0cfb6701 08 mov byte ptr ds:[0x167fb0c],0x8
 *  160941ec   832d 7c4cb101 06 sub dword ptr ds:[0x1b14c7c],0x6
 *  160941f3   e9 20000000      jmp 16094218
 *  160941f8   0188 6b2180e9    add dword ptr ds:[eax+0xe980216b],ecx
 *  160941fe   0e               push cs
 *  160941ff   be 06f1832d      mov esi,0x2d83f106
 *  16094204   7c 4c            jl short 16094252
 *  16094206   b1 01            mov cl,0x1
 *  16094208   06               push es
 *  16094209   e9 c2000000      jmp 160942d0
 *  1609420e   0198 6b2180e9    add dword ptr ds:[eax+0xe980216b],ebx
 *  16094214   f8               clc
 *  16094215   bd 06f1770f      mov ebp,0xf77f106
 *  1609421a   c705 00fb6701 88>mov dword ptr ds:[0x167fb00],0x80216b88
 *  16094224  -e9 41be06f1      jmp 0710006a
 *  16094229   8b0d 90f86701    mov ecx,dword ptr ds:[0x167f890]
 *  1609422f   81c1 01000000    add ecx,0x1
 *  16094235   f7c1 0000000c    test ecx,0xc000000
 *  1609423b   74 0b            je short 16094248
 *  1609423d   51               push ecx
 *  1609423e   e8 86bef9f2      call 090300c9
 *  16094243   83c4 04          add esp,0x4
 *  16094246   eb 11            jmp short 16094259
 *  16094248   8bc1             mov eax,ecx
 *  1609424a   81e0 ffffff3f    and eax,0x3fffffff
 *  16094250   0fb680 00000810  movzx eax,byte ptr ds:[eax+0x10080000]
 *  16094257   66:90            nop
 *  16094259   8b35 90f86701    mov esi,dword ptr ds:[0x167f890]
 *  1609425f   81c6 01000000    add esi,0x1
 *  16094265   8905 80f86701    mov dword ptr ds:[0x167f880],eax
 *  1609426b   8105 8cf86701 01>add dword ptr ds:[0x167f88c],0x1
 *  16094275   813d 80f86701 00>cmp dword ptr ds:[0x167f880],0x0
 *  1609427f   8935 90f86701    mov dword ptr ds:[0x167f890],esi
 *  16094285   7c 14            jl short 1609429b
 *  16094287   7f 09            jg short 16094292
 *  16094289   c605 0cfb6701 02 mov byte ptr ds:[0x167fb0c],0x2
 *  16094290   eb 26            jmp short 160942b8
 *  16094292   c605 0cfb6701 04 mov byte ptr ds:[0x167fb0c],0x4
 *  16094299   eb 07            jmp short 160942a2
 *  1609429b   c605 0cfb6701 08 mov byte ptr ds:[0x167fb0c],0x8
 *  160942a2   832d 7c4cb101 04 sub dword ptr ds:[0x1b14c7c],0x4
 *  160942a9  ^e9 6affffff      jmp 16094218
 *  160942ae   0188 6b2180e9    add dword ptr ds:[eax+0xe980216b],ecx
 *  160942b4   58               pop eax
 *  160942b5   bd 06f1832d      mov ebp,0x2d83f106
 *  160942ba   7c 4c            jl short 16094308
 *  160942bc   b1 01            mov cl,0x1
 *  160942be   04 e9            add al,0xe9
 *  160942c0   0c 00            or al,0x0
 *  160942c2   0000             add byte ptr ds:[eax],al
 *  160942c4   0198 6b2180e9    add dword ptr ds:[eax+0xe980216b],ebx
 *  160942ca   42               inc edx
 *  160942cb   bd 06f1cccc      mov ebp,0xccccf106
 *  160942d0   77 0f            ja short 160942e1
 *  160942d2   c705 00fb6701 98>mov dword ptr ds:[0x167fb00],0x80216b98
 *  160942dc  -e9 89bd06f1      jmp 0710006a
 *  160942e1   8b05 84fb6701    mov eax,dword ptr ds:[0x167fb84]
 *  160942e7   81e0 fcffffff    and eax,0xfffffffc
 *  160942ed   8905 00fb6701    mov dword ptr ds:[0x167fb00],eax
 *  160942f3   832d 7c4cb101 01 sub dword ptr ds:[0x1b14c7c],0x1
 *  160942fa  -e9 11bd06f1      jmp 07100010
 *  160942ff   832d 7c4cb101 01 sub dword ptr ds:[0x1b14c7c],0x1
 *  16094306  ^e9 91f8ffff      jmp 16093b9c
 *  1609430b   cc               int3
 */
namespace { // unnamed

// Return true if the text is a garbage character
inline bool _vanillawaregarbage_ch(char c)
{
  return c == ' ' || c == '.' || c == '/'
      || c >= '0' && c <= '9'
      || c >= 'A' && c <= 'z' // also ignore ASCII 91-96: [ \ ] ^ _ `
  ;
}

// Return true if the text is full of garbage characters
bool _vanillawaregarbage(LPCSTR p)
{
  enum { MAX_LENGTH = VNR_TEXT_CAPACITY };
  for (int count = 0; *p && count < MAX_LENGTH; count++, p++)
    if (!_vanillawaregarbage_ch(*p))
      return false;
  return true;
}
} // unnamed namespace

static void SpecialGCHookVanillaware(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  DWORD eax = regof(eax, esp_base);
  LPCSTR text = LPCSTR(eax + hp->user_value);
  static LPCSTR lasttext;
  if (lasttext != text && *text && !_vanillawaregarbage(text)) {
    lasttext = text;
    *data = (DWORD)text;
    *len = ::strlen(text); // SHIFT-JIS
    *split = regof(ecx, esp_base);
    //*split = FIXED_SPLIT_VALUE;
  }
}

bool InsertVanillawareGCHook()
{
  ConsoleOutput("vnreng: Vanillaware GC: enter");

  const BYTE bytes[] =  {
    0x83,0xc4, 0x04,                // 16094193   83c4 04          add esp,0x4
    0xeb, 0x11,                     // 16094196   eb 11            jmp short 160941a9
    0x8b,0xc1,                      // 16094198   8bc1             mov eax,ecx
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 1609419a   81e0 ffffff3f    and eax,0x3fffffff
    0x0f,0xb6,0x80, XX4,            // 160941a0   0fb680 00000810  movzx eax,byte ptr ds:[eax+0x10080000] ; jichi: hook here
    0x66,0x90,                      // 160941a7   66:90            nop
    0x81,0xc6, 0x01,0x00,0x00,0x00  // 160941a9   81c6 01000000    add esi,0x1
    //0x89,05 80f86701      // 160941af   8905 80f86701    mov dword ptr ds:[0x167f880],eax
    //0x81,3d 80f86701 00   // 160941b5   813d 80f86701 00>cmp dword ptr ds:[0x167f880],0x0
    //0xc7,05 8cf86701 00   // 160941bf   c705 8cf86701 00>mov dword ptr ds:[0x167f88c],0x0
    //0x89,35 90f86701      // 160941c9   8935 90f86701    mov dword ptr ds:[0x167f890],esi
    //0x7c, 14              // 160941cf   7c 14            jl short 160941e5
    //0x7f, 09              // 160941d1   7f 09            jg short 160941dc
    //0xc6,05 0cfb6701 02   // 160941d3   c605 0cfb6701 02 mov byte ptr ds:[0x167fb0c],0x2
    //0xeb, 26              // 160941da   eb 26            jmp short 16094202
  };
  enum { memory_offset = 3 }; // 160941a0   0fb680 00000810  movzx eax,byte ptr ds:[eax+0x10080000]
  enum { addr_offset = 0x160941a0 - 0x16094193 };

  DWORD addr = SafeMatchBytesInGCMemory(bytes, sizeof(bytes));
  if (!addr)
    ConsoleOutput("vnreng: Vanillaware GC: pattern not found");
  else {
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.user_value = *(DWORD *)(hp.address + memory_offset);
    hp.text_fun = SpecialGCHookVanillaware;
    hp.type = USING_STRING|NO_CONTEXT; // no context is needed to get rid of variant retaddr
    ConsoleOutput("vnreng: Vanillaware GC: INSERT");
    NewHook(hp, "Vanillaware GC");
  }

  ConsoleOutput("vnreng: Vanillaware GC: leave");
  return addr;
}

/** jichi 7/12/2014 PPSSPP
 *  Tested with PPSSPP 0.9.8.
 */
void SpecialPSPHook(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  DWORD offset = *(DWORD *)(esp_base + hp->offset);
  LPCSTR text = LPCSTR(offset + hp->user_value);
  static LPCSTR lasttext;
  if (*text) {
    if (hp->user_flags & HPF_IgnoreSameAddress) {
      if (text == lasttext)
        return;
      lasttext = text;
    }
    *data = (DWORD)text;
    // I only considered SHIFT-JIS/UTF-8 case
    if (hp->length_offset == 1)
      *len = 1; // only read 1 byte
    else if (hp->length_offset)
      *len = *(DWORD *)(esp_base + hp->length_offset);
    else
      *len = ::strlen(text); // should only be applied to hp->type|USING_STRING
    if (hp->type & USING_SPLIT) {
      if (hp->type & FIXING_SPLIT)
        *split = FIXED_SPLIT_VALUE;
      else
        *split = *(DWORD *)(esp_base + hp->split);
    }
  }
}

bool InsertPPSSPPHLEHooks()
{
  ConsoleOutput("vnreng: PPSSPP HLE: enter");
  ULONG startAddress, stopAddress;
  if (!NtInspect::getProcessMemoryRange(&startAddress, &stopAddress)) { // need accurate stopAddress
    ConsoleOutput("vnreng:PPSSPP HLE: failed to get memory range");
    return false;
  }

  // 0x400000 - 0x139f000
  //GROWL_DWORD2(startAddress, stopAddress);

  HookParam hp = {};
  hp.length_offset = 1; // determine string length at runtime

  const PPSSPPFunction funcs[] = { PPSSPP_FUNCTIONS_INITIALIZER };
  enum { FunctionCount = sizeof(funcs) / sizeof(*funcs) };
  for (size_t i = 0; i < FunctionCount; i++) {
    const auto &it = funcs[i];
    ULONG addr = MemDbg::findBytes(it.pattern, ::strlen(it.pattern), startAddress, stopAddress);
    if (addr
        && (addr = MemDbg::findPushAddress(addr, startAddress, stopAddress))
        && (addr = SafeFindEnclosingAlignedFunction(addr, 0x200)) // range = 0x200, use the safe version or it might raise
       ) {
       hp.address = addr;
       hp.type = it.hookType;
       hp.offset = 4 * it.argIndex;
       hp.split = it.hookSplit;
       if (hp.split)
         hp.type |= USING_SPLIT;
       NewHook(hp, it.hookName);
    }
    if (addr)
      ConsoleOutput("vnreng: PPSSPP HLE: found pattern");
    else
      ConsoleOutput("vnreng: PPSSPP HLE: not found pattern");
    //ConsoleOutput(it.hookName); // wchar_t not supported
    ConsoleOutput(it.pattern);
  }
  ConsoleOutput("vnreng: PPSSPP HLE: leave");
  return true;
}

bool InsertPPSSPPHooks()
{
  //if (PPSSPP_VERSION[1] == 9 && (PPSSPP_VERSION[2] > 9 || PPSSPP_VERSION[2] == 9 && PPSSPP_VERSION[3] >= 1)) // >= 0.9.9.1

  ConsoleOutput("vnreng: PPSSPP: enter");

  if (!WinVersion::queryFileVersion(process_path_, PPSSPP_VERSION))
    ConsoleOutput("vnreng: failed to get PPSSPP version");

  InsertPPSSPPHLEHooks();

  if (PPSSPP_VERSION[1] == 9 && PPSSPP_VERSION[2] == 9 && PPSSPP_VERSION[3] == 0) // 0.9.9.0
    InsertOtomatePPSSPPHook();

  //bool engineFound = false;
  Insert5pbPSPHook();
  InsertCyberfrontPSPHook();
  InsertImageepoch2PSPHook();
  InsertFelistellaPSPHook();

  InsertBroccoliPSPHook();
  InsertIntensePSPHook();
  //InsertKadokawaNamePSPHook(); // disabled
  InsertKonamiPSPHook();

  if (PPSSPP_VERSION[1] == 9 && PPSSPP_VERSION[2] == 8) { // only works for 0.9.8 anyway
    InsertNippon1PSPHook();
    InsertNippon2PSPHook(); // This could crash PPSSPP 099 just like 5pb
  }

  //InsertTecmoPSPHook();

  // Generic hooks

  bool bandaiFound = InsertBandaiPSPHook();
  InsertBandaiNamePSPHook();

  // Hooks whose pattern is not generic enouph

  InsertYetiPSPHook();
  InsertYeti2PSPHook();

  InsertAlchemistPSPHook();
  InsertAlchemist2PSPHook();

  //InsertTypeMoonPSPHook() // otomate is creating too many garbage
  //|| InsertOtomatePSPHook();
  InsertOtomatePSPHook();

  if (!bandaiFound) {
    // KID pattern is a subset of BANDAI, and hence MUST NOT be together with BANDAI
    // Sample BANDAI game that could be broken by KID: 寮�のサクリファイス
    InsertKidPSPHook(); // KID could lose text, could exist in multiple game

    InsertImageepochPSPHook();  // Imageepoch could crash vnrcli for School Rumble PSP
  }

  ConsoleOutput("vnreng: PPSSPP: leave");
  return true;
}

/** 7/13/2014 jichi alchemist-net.co.jp PSP engine, 0.9.8 only, not work on 0.9.9
 *  Sample game: your diary+ (moe-ydp.iso)
 *  The memory address is fixed.
 *  Note: This pattern seems to be common that not only exists in Alchemist games.
 *
 *  Not work on 0.9.9: Amnesia Crowd
 *
 *  Debug method: simply add hardware break points to the matched memory
 *
 *  PPSSPP 0.9.8, your diary+
 *  134076f2   cc               int3
 *  134076f3   cc               int3
 *  134076f4   77 0f            ja short 13407705
 *  134076f6   c705 a8aa1001 40>mov dword ptr ds:[0x110aaa8],0x8931040
 *  13407700  -e9 ff88f2f3      jmp 07330004
 *  13407705   8b05 7ca71001    mov eax,dword ptr ds:[0x110a77c]
 *  1340770b   81e0 ffffff3f    and eax,0x3fffffff
 *  13407711   0fbeb0 00004007  movsx esi,byte ptr ds:[eax+0x7400000]   // jichi: hook here
 *  13407718   8b3d 78a71001    mov edi,dword ptr ds:[0x110a778]
 *  1340771e   8b2d 7ca71001    mov ebp,dword ptr ds:[0x110a77c]
 *  13407724   81c5 01000000    add ebp,0x1
 *  1340772a   8b05 78a71001    mov eax,dword ptr ds:[0x110a778]
 *  13407730   81e0 ffffff3f    and eax,0x3fffffff
 *  13407736   8bd6             mov edx,esi
 *  13407738   8890 00004007    mov byte ptr ds:[eax+0x7400000],dl      // jichi: alternatively hook here
 *  1340773e   8b15 78a71001    mov edx,dword ptr ds:[0x110a778]
 *  13407744   81c2 01000000    add edx,0x1
 *  1340774a   8bcd             mov ecx,ebp
 *  1340774c   8935 88a71001    mov dword ptr ds:[0x110a788],esi
 *  13407752   8bf2             mov esi,edx
 *  13407754   813d 88a71001 00>cmp dword ptr ds:[0x110a788],0x0
 *  1340775e   893d 70a71001    mov dword ptr ds:[0x110a770],edi
 *  13407764   8935 78a71001    mov dword ptr ds:[0x110a778],esi
 *  1340776a   890d 7ca71001    mov dword ptr ds:[0x110a77c],ecx
 *  13407770   8915 80a71001    mov dword ptr ds:[0x110a780],edx
 *  13407776   892d 84a71001    mov dword ptr ds:[0x110a784],ebp
 *  1340777c   0f85 16000000    jnz 13407798
 *  13407782   832d c4aa1001 08 sub dword ptr ds:[0x110aac4],0x8
 *  13407789   e9 ce000000      jmp 1340785c
 *  1340778e   017c10 93        add dword ptr ds:[eax+edx-0x6d],edi
 *  13407792   08e9             or cl,ch
 *  13407794   8b88 f2f3832d    mov ecx,dword ptr ds:[eax+0x2d83f3f2]
 *  1340779a   c4aa 100108e9    les ebp,fword ptr ds:[edx+0xe9080110]    ; modification of segment register
 *  134077a0   0c 00            or al,0x0
 *  134077a2   0000             add byte ptr ds:[eax],al
 *  134077a4   0160 10          add dword ptr ds:[eax+0x10],esp
 *  134077a7   93               xchg eax,ebx
 *  134077a8   08e9             or cl,ch
 *  134077aa  ^75 88            jnz short 13407734
 *  134077ac   f2:              prefix repne:                            ; superfluous prefix
 *  134077ad   f3:              prefix rep:                              ; superfluous prefix
 *  134077ae   90               nop
 *  134077af   cc               int3
 */

namespace { // unnamed

// Return true if the text is a garbage character
inline bool _alchemistgarbage_ch(char c)
{
  return c == '.' || c == '/'
      || c == '#' || c == ':' // garbage in alchemist2 hook
      || c >= '0' && c <= '9'
      || c >= 'A' && c <= 'z' // also ignore ASCII 91-96: [ \ ] ^ _ `
  ;
}

// Return true if the text is full of garbage characters
bool _alchemistgarbage(LPCSTR p)
{
  enum { MAX_LENGTH = VNR_TEXT_CAPACITY };
  for (int count = 0; *p && count < MAX_LENGTH; count++, p++)
    if (!_alchemistgarbage_ch(*p))
      return false;
  return true;
}

// 7/20/2014 jichi: Trim Rejet garbage. Sample game: 月華繚乱ROMANCE
// Such as: #Pos[1,2]
inline bool _rejetgarbage_ch(char c)
{
  return c == '#' || c == ' ' || c == '[' || c == ']' || c == ','
      || c >= 'A' && c <= 'z' // also ignore ASCII 91-96: [ \ ] ^ _ `
      || c >= '0' && c <= '9';
}

bool _rejetgarbage(LPCSTR p)
{
  enum { MAX_LENGTH = VNR_TEXT_CAPACITY };
  for (int count = 0; *p && count < MAX_LENGTH; count++, p++)
    if (!_rejetgarbage_ch(*p))
      return false;
  return true;
}

// Trim leading garbage
LPCSTR _rejetltrim(LPCSTR p)
{
  enum { MAX_LENGTH = VNR_TEXT_CAPACITY };
  if (p)
    for (int count = 0; *p && count < MAX_LENGTH; count++, p++)
      if (!_rejetgarbage_ch(*p))
        return p;
  return nullptr;
}

// Trim trailing garbage
size_t _rejetstrlen(LPCSTR text)
{
  if (!text)
    return 0;
  size_t len = ::strlen(text),
         ret = len;
  while (len && _rejetgarbage_ch(text[len - 1])) {
    len--;
    if (text[len] == '#') // in case trim UTF-8 trailing bytes
      ret = len;
  }
  return ret;
}

} // unnamed namespace

static void SpecialPSPHookAlchemist(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  DWORD eax = regof(eax, esp_base);
  LPCSTR text = LPCSTR(eax + hp->user_value);
  if (*text && !_alchemistgarbage(text)) {
    text = _rejetltrim(text);
    *data = (DWORD)text;
    *len = _rejetstrlen(text);
    *split = regof(ecx, esp_base);
  }
}

bool InsertAlchemistPSPHook()
{
  ConsoleOutput("vnreng: Alchemist PSP: enter");
  const BYTE bytes[] =  {
     //0xcc,                         // 134076f2   cc               int3
     //0xcc,                         // 134076f3   cc               int3
     0x77, 0x0f,                     // 134076f4   77 0f            ja short 13407705
     0xc7,0x05, XX8,                 // 134076f6   c705 a8aa1001 40>mov dword ptr ds:[0x110aaa8],0x8931040
     0xe9, XX4,                      // 13407700  -e9 ff88f2f3      jmp 07330004
     0x8b,0x05, XX4,                 // 13407705   8b05 7ca71001    mov eax,dword ptr ds:[0x110a77c]
     0x81,0xe0, 0xff,0xff,0xff,0x3f, // 1340770b   81e0 ffffff3f    and eax,0x3fffffff
     0x0f,0xbe,0xb0, XX4,            // 13407711   0fbeb0 00004007  movsx esi,byte ptr ds:[eax+0x7400000]   // jichi: hook here
     0x8b,0x3d, XX4,                 // 13407718   8b3d 78a71001    mov edi,dword ptr ds:[0x110a778]
     0x8b,0x2d, XX4,                 // 1340771e   8b2d 7ca71001    mov ebp,dword ptr ds:[0x110a77c]
     0x81,0xc5, 0x01,0x00,0x00,0x00, // 13407724   81c5 01000000    add ebp,0x1
     0x8b,0x05, XX4,                 // 1340772a   8b05 78a71001    mov eax,dword ptr ds:[0x110a778]
     0x81,0xe0, 0xff,0xff,0xff,0x3f, // 13407730   81e0 ffffff3f    and eax,0x3fffffff
     0x8b,0xd6,                      // 13407736   8bd6             mov edx,esi
     0x88,0x90 //, XX4               // 13407738   8890 00004007    mov byte ptr ds:[eax+0x7400000],dl      // jichi: alternatively hook here
  };
  enum { memory_offset = 3 }; // 13407711   0fbeb0 00004007  movsx esi,byte ptr ds:[eax+0x7400000]
  enum { addr_offset = 0x13407711 - 0x134076f4 };

  DWORD addr = SafeMatchBytesInPSPMemory(bytes, sizeof(bytes));
  //GROWL_DWORD(addr);
  if (!addr)
    ConsoleOutput("vnreng: Alchemist PSP: pattern not found");
  else {
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.user_value = *(DWORD *)(hp.address + memory_offset);
    hp.text_fun = SpecialPSPHookAlchemist;
    hp.type = USING_STRING|NO_CONTEXT; // no context is needed to get rid of variant retaddr
    ConsoleOutput("vnreng: Alchemist PSP: INSERT");
    NewHook(hp, "Alchemist PSP");
  }

  ConsoleOutput("vnreng: Alchemist PSP: leave");
  return addr;
}

/** 7/20/2014 jichi alchemist-net.co.jp PSP engine, 0.9.8, 0.9.9
 *  An alternative alchemist hook for old alchemist games.
 *  Sample game: のーふぁ�と (No Fate)
 *  The memory address is fixed.
 *
 *  Also work on 0.9.9 Otoboku PSP
 *
 *  Debug method: simply add hardware break points to the matched memory
 *
 *  Two candidate functions are seems OK.
 *
 *  Instruction pattern: 81e580808080    // and ebp,0x80808080
 *
 *  0.9.8 のーふぁ�と
 *  13400ef3   90               nop
 *  13400ef4   77 0f            ja short 13400f05
 *  13400ef6   c705 a8aa1001 d0>mov dword ptr ds:[0x110aaa8],0x889aad0
 *  13400f00  -e9 fff050f0      jmp 03910004
 *  13400f05   8b35 78a71001    mov esi,dword ptr ds:[0x110a778]
 *  13400f0b   8bc6             mov eax,esi
 *  13400f0d   81e0 ffffff3f    and eax,0x3fffffff
 *  13400f13   8bb8 00004007    mov edi,dword ptr ds:[eax+0x7400000] ; jichi
 *  13400f19   8bef             mov ebp,edi
 *  13400f1b   81ed 01010101    sub ebp,0x1010101
 *  13400f21   f7d7             not edi
 *  13400f23   23ef             and ebp,edi
 *  13400f25   81e5 80808080    and ebp,0x80808080
 *  13400f2b   81fd 00000000    cmp ebp,0x0
 *  13400f31   c705 78a71001 80>mov dword ptr ds:[0x110a778],0x80808080
 *  13400f3b   c705 7ca71001 01>mov dword ptr ds:[0x110a77c],0x1010101
 *  13400f45   8935 80a71001    mov dword ptr ds:[0x110a780],esi
 *  13400f4b   892d 88a71001    mov dword ptr ds:[0x110a788],ebp
 *  13400f51   0f84 22000000    je 13400f79
 *  13400f57   8b35 80a71001    mov esi,dword ptr ds:[0x110a780]
 *  13400f5d   8935 78a71001    mov dword ptr ds:[0x110a778],esi
 *  13400f63   832d c4aa1001 0c sub dword ptr ds:[0x110aac4],0xc
 *  13400f6a   e9 35ba0000      jmp 1340c9a4
 *  13400f6f   0124ab           add dword ptr ds:[ebx+ebp*4],esp
 *  13400f72   8908             mov dword ptr ds:[eax],ecx
 *  13400f74  -e9 aaf050f0      jmp 03910023
 *  13400f79   832d c4aa1001 0c sub dword ptr ds:[0x110aac4],0xc
 *  13400f80   e9 0b000000      jmp 13400f90
 *  13400f85   0100             add dword ptr ds:[eax],eax
 *  13400f87   ab               stos dword ptr es:[edi]
 *  13400f88   8908             mov dword ptr ds:[eax],ecx
 *  13400f8a  -e9 94f050f0      jmp 03910023
 *  13400f8f   90               nop
 */

static void SpecialPSPHookAlchemist2(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  DWORD eax = regof(eax, esp_base);
  LPCSTR text = LPCSTR(eax + hp->user_value);
  if (*text && !_alchemistgarbage(text)) {
    *data = (DWORD)text;
    *len = ::strlen(text);
    *split = regof(ecx, esp_base);
  }
}

bool InsertAlchemist2PSPHook()
{
  ConsoleOutput("vnreng: Alchemist2 PSP: enter");
  const BYTE bytes[] =  {
    0x77, 0x0f,                     // 13400ef4   77 0f            ja short 13400f05
    0xc7,0x05, XX8,                 // 13400ef6   c705 a8aa1001 d0>mov dword ptr ds:[0x110aaa8],0x889aad0
    0xe9, XX4,                      // 13400f00  -e9 fff050f0      jmp 03910004
    0x8b,0x35, XX4,                 // 13400f05   8b35 78a71001    mov esi,dword ptr ds:[0x110a778]
    0x8b,0xc6,                      // 13400f0b   8bc6             mov eax,esi
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 13400f0d   81e0 ffffff3f    and eax,0x3fffffff
    0x8b,0xb8, XX4,                 // 13400f13   8bb8 00004007    mov edi,dword ptr ds:[eax+0x7400000] ; jichi: hook here
    0x8b,0xef,                      // 13400f19   8bef             mov ebp,edi
    0x81,0xed, 0x01,0x01,0x01,0x01, // 13400f1b   81ed 01010101    sub ebp,0x1010101
    0xf7,0xd7,                      // 13400f21   f7d7             not edi
    0x23,0xef,                      // 13400f23   23ef             and ebp,edi
    0x81,0xe5, 0x80,0x80,0x80,0x80, // 13400f25   81e5 80808080    and ebp,0x80808080
    0x81,0xfd, 0x00,0x00,0x00,0x00  // 13400f2b   81fd 00000000    cmp ebp,0x0
  };
  enum { memory_offset = 2 }; // 13400f13   8bb8 00004007    mov edi,dword ptr ds:[eax+0x7400000]
  enum { addr_offset = 0x13400f13 - 0x13400ef4 };

  DWORD addr = SafeMatchBytesInPSPMemory(bytes, sizeof(bytes));
  //GROWL_DWORD(addr);
  if (!addr)
    ConsoleOutput("vnreng: Alchemist2 PSP: pattern not found");
  else {
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.user_value = *(DWORD *)(hp.address + memory_offset);
    hp.text_fun = SpecialPSPHookAlchemist2;
    hp.type = USING_STRING|NO_CONTEXT; // no context is needed to get rid of variant retaddr
    ConsoleOutput("vnreng: Alchemist2 PSP: INSERT");
    NewHook(hp, "Alchemist2 PSP");
  }

  ConsoleOutput("vnreng: Alchemist2 PSP: leave");
  return addr;
}

/** 7/13/2014 jichi 5pb.jp PSP engine, 0.9.8, 0.9.9
 *  Sample game: STEINS;GATE
 *
 *  FIXME: The current pattern could crash VNR
 *
 *  Note: searching after 0x15000000 would found a wrong address on 0.9.9.
 *  Hooking to it would crash PPSSPP.
 *
 *  Float memory addresses: two matches
 *
 *  Debug method: precompute memory address and set break points, then navigate to that scene
 *
 *  Attach to this function for wrong game might cause BEX (buffer overflow) exception.
 *
 *  135752c7   90               nop
 *  135752c8   77 0f            ja short 135752d9
 *  135752ca   c705 a8aa1001 d4>mov dword ptr ds:[0x110aaa8],0x8888ed4
 *  135752d4  -e9 2badf3ef      jmp 034b0004
 *  135752d9   8b35 dca71001    mov esi,dword ptr ds:[0x110a7dc]
 *  135752df   8d76 a0          lea esi,dword ptr ds:[esi-0x60]
 *  135752e2   8b3d e4a71001    mov edi,dword ptr ds:[0x110a7e4]
 *  135752e8   8bc6             mov eax,esi
 *  135752ea   81e0 ffffff3f    and eax,0x3fffffff
 *  135752f0   89b8 1c004007    mov dword ptr ds:[eax+0x740001c],edi
 *  135752f6   8b2d bca71001    mov ebp,dword ptr ds:[0x110a7bc]
 *  135752fc   8bc6             mov eax,esi
 *  135752fe   81e0 ffffff3f    and eax,0x3fffffff
 *  13575304   89a8 18004007    mov dword ptr ds:[eax+0x7400018],ebp
 *  1357530a   8b15 b8a71001    mov edx,dword ptr ds:[0x110a7b8]
 *  13575310   8bc6             mov eax,esi
 *  13575312   81e0 ffffff3f    and eax,0x3fffffff
 *  13575318   8990 14004007    mov dword ptr ds:[eax+0x7400014],edx
 *  1357531e   8b0d b4a71001    mov ecx,dword ptr ds:[0x110a7b4]
 *  13575324   8bc6             mov eax,esi
 *  13575326   81e0 ffffff3f    and eax,0x3fffffff
 *  1357532c   8988 10004007    mov dword ptr ds:[eax+0x7400010],ecx
 *  13575332   8b3d b0a71001    mov edi,dword ptr ds:[0x110a7b0]
 *  13575338   8bc6             mov eax,esi
 *  1357533a   81e0 ffffff3f    and eax,0x3fffffff
 *  13575340   89b8 0c004007    mov dword ptr ds:[eax+0x740000c],edi
 *  13575346   8b3d aca71001    mov edi,dword ptr ds:[0x110a7ac]
 *  1357534c   8bc6             mov eax,esi
 *  1357534e   81e0 ffffff3f    and eax,0x3fffffff
 *  13575354   89b8 08004007    mov dword ptr ds:[eax+0x7400008],edi
 *  1357535a   8b3d a8a71001    mov edi,dword ptr ds:[0x110a7a8]
 *  13575360   8bc6             mov eax,esi
 *  13575362   81e0 ffffff3f    and eax,0x3fffffff
 *  13575368   89b8 04004007    mov dword ptr ds:[eax+0x7400004],edi
 *  1357536e   8b15 78a71001    mov edx,dword ptr ds:[0x110a778]
 *  13575374   8935 dca71001    mov dword ptr ds:[0x110a7dc],esi
 *  1357537a   8b05 7ca71001    mov eax,dword ptr ds:[0x110a77c]
 *  13575380   81e0 ffffff3f    and eax,0x3fffffff
 *  13575386   0fbeb0 00004007  movsx esi,byte ptr ds:[eax+0x7400000] ; jichi: hook here
 *  1357538d   8935 78a71001    mov dword ptr ds:[0x110a778],esi
 *  13575393   8b35 80a71001    mov esi,dword ptr ds:[0x110a780]
 *  13575399   8935 b0a71001    mov dword ptr ds:[0x110a7b0],esi
 *  1357539f   8b35 84a71001    mov esi,dword ptr ds:[0x110a784]
 *  135753a5   8b0d 7ca71001    mov ecx,dword ptr ds:[0x110a77c]
 *  135753ab   813d 78a71001 00>cmp dword ptr ds:[0x110a778],0x0
 *  135753b5   c705 a8a71001 00>mov dword ptr ds:[0x110a7a8],0x0
 *  135753bf   8935 aca71001    mov dword ptr ds:[0x110a7ac],esi
 *  135753c5   890d b4a71001    mov dword ptr ds:[0x110a7b4],ecx
 *  135753cb   8915 b8a71001    mov dword ptr ds:[0x110a7b8],edx
 *  135753d1   0f85 16000000    jnz 135753ed
 *  135753d7   832d c4aa1001 0f sub dword ptr ds:[0x110aac4],0xf
 *  135753de   e9 e5010000      jmp 135755c8
 *  135753e3   01f0             add eax,esi
 *  135753e5   90               nop
 *  135753e6   8808             mov byte ptr ds:[eax],cl
 *  135753e8  -e9 36acf3ef      jmp 034b0023
 *  135753ed   832d c4aa1001 0f sub dword ptr ds:[0x110aac4],0xf
 *  135753f4   e9 0b000000      jmp 13575404
 *  135753f9   0110             add dword ptr ds:[eax],edx
 *  135753fb   8f               ???                                      ; unknown command
 *  135753fc   8808             mov byte ptr ds:[eax],cl
 *  135753fe  -e9 20acf3ef      jmp 034b0023
 *  13575403   90               nop
 *  13575404   77 0f            ja short 13575415
 *  13575406   c705 a8aa1001 10>mov dword ptr ds:[0x110aaa8],0x8888f10
 *  13575410  -e9 efabf3ef      jmp 034b0004
 *  13575415   8b35 a8a71001    mov esi,dword ptr ds:[0x110a7a8]
 *  1357541b   33c0             xor eax,eax
 *  1357541d   3b35 b0a71001    cmp esi,dword ptr ds:[0x110a7b0]
 *  13575423   0f9cc0           setl al
 *  13575426   8bf8             mov edi,eax
 *  13575428   81ff 00000000    cmp edi,0x0
 *  1357542e   893d 74a71001    mov dword ptr ds:[0x110a774],edi
 *  13575434   0f84 22000000    je 1357545c
 *  1357543a   8b35 b4a71001    mov esi,dword ptr ds:[0x110a7b4]
 *  13575440   8935 78a71001    mov dword ptr ds:[0x110a778],esi
 *  13575446   832d c4aa1001 03 sub dword ptr ds:[0x110aac4],0x3
 *  1357544d   c705 a8aa1001 2c>mov dword ptr ds:[0x110aaa8],0x8888f2c
 *  13575457  -e9 c7abf3ef      jmp 034b0023
 *  1357545c   832d c4aa1001 03 sub dword ptr ds:[0x110aac4],0x3
 *  13575463   e9 0c000000      jmp 13575474
 *  13575468   011c8f           add dword ptr ds:[edi+ecx*4],ebx
 *  1357546b   8808             mov byte ptr ds:[eax],cl
 *  1357546d  -e9 b1abf3ef      jmp 034b0023
 *  13575472   90               nop
 *  13575473   cc               int3
 *  13575474   77 0f            ja short 13575485
 *  13575476   c705 a8aa1001 1c>mov dword ptr ds:[0x110aaa8],0x8888f1c
 *  13575480  -e9 7fabf3ef      jmp 034b0004
 *  13575485   8b35 78a71001    mov esi,dword ptr ds:[0x110a778]
 *  1357548b   8b05 b8a71001    mov eax,dword ptr ds:[0x110a7b8]
 *  13575491   81e0 ffffff3f    and eax,0x3fffffff
 *  13575497   8bd6             mov edx,esi
 *  13575499   8890 00004007    mov byte ptr ds:[eax+0x7400000],dl
 *  1357549f   8b3d b4a71001    mov edi,dword ptr ds:[0x110a7b4]
 *  135754a5   8d7f 01          lea edi,dword ptr ds:[edi+0x1]
 *  135754a8   8b2d b8a71001    mov ebp,dword ptr ds:[0x110a7b8]
 *  135754ae   8d6d 01          lea ebp,dword ptr ss:[ebp+0x1]
 *  135754b1   813d 68a71001 00>cmp dword ptr ds:[0x110a768],0x0
 *  135754bb   893d b4a71001    mov dword ptr ds:[0x110a7b4],edi
 *  135754c1   892d b8a71001    mov dword ptr ds:[0x110a7b8],ebp
 *  135754c7   0f85 16000000    jnz 135754e3
 *  135754cd   832d c4aa1001 04 sub dword ptr ds:[0x110aac4],0x4
 *  135754d4   e9 23000000      jmp 135754fc
 *  135754d9   01e4             add esp,esp
 *  135754db   90               nop
 *  135754dc   8808             mov byte ptr ds:[eax],cl
 *  135754de  -e9 40abf3ef      jmp 034b0023
 *  135754e3   832d c4aa1001 04 sub dword ptr ds:[0x110aac4],0x4
 *  135754ea   c705 a8aa1001 2c>mov dword ptr ds:[0x110aaa8],0x8888f2c
 *  135754f4  -e9 2aabf3ef      jmp 034b0023
 *  135754f9   90               nop
 *  135754fa   cc               int3
 *  135754fb   cc               int3
 */
namespace { // unnamed

// Characters to ignore: [%0-9A-Z]
inline bool _5pbgarbage_ch(char c)
{ return c == '%' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9'; }

// Trim leading garbage
LPCSTR _5pbltrim(LPCSTR p)
{
  enum { MAX_LENGTH = VNR_TEXT_CAPACITY };
  if (p)
    for (int count = 0; *p && count < MAX_LENGTH; count++, p++)
      if (!_5pbgarbage_ch(*p))
        return p;
  return nullptr;
}

// Trim trailing garbage
size_t _5pbstrlen(LPCSTR text)
{
  if (!text)
    return 0;
  size_t len = ::strlen(text),
         ret = len;
  while (len && _5pbgarbage_ch(text[len - 1])) {
    len--;
    if (text[len] == '%') // in case trim UTF-8 trailing bytes
      ret = len;
  }
  return ret;
}

} // unnamed namespace

static void SpecialPSPHook5pb(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  DWORD eax = regof(eax, esp_base);
  LPCSTR text = LPCSTR(eax + hp->user_value);
  if (*text) {
    text = _5pbltrim(text);
    *data = (DWORD)text;
    *len = _5pbstrlen(text);
    *split = regof(ecx, esp_base);
    //*split = FIXED_SPLIT_VALUE; // there is only one thread, no split used
  }
}

bool Insert5pbPSPHook()
{
  ConsoleOutput("vnreng: 5pb PSP: enter");

  const BYTE bytes[] =  {
    //0x90,                         // 135752c7   90               nop
    0x77, 0x0f,                     // 135752c8   77 0f            ja short 135752d9
    0xc7,0x05, XX8,                 // 135752ca   c705 a8aa1001 d4>mov dword ptr ds:[0x110aaa8],0x8888ed4
    0xe9, XX4,                      // 135752d4  -e9 2badf3ef      jmp 034b0004
    0x8b,0x35, XX4,                 // 135752d9   8b35 dca71001    mov esi,dword ptr ds:[0x110a7dc]
    0x8d,0x76, 0xa0,                // 135752df   8d76 a0          lea esi,dword ptr ds:[esi-0x60]
    0x8b,0x3d, XX4,                 // 135752e2   8b3d e4a71001    mov edi,dword ptr ds:[0x110a7e4]
    0x8b,0xc6,                      // 135752e8   8bc6             mov eax,esi
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 135752ea   81e0 ffffff3f    and eax,0x3fffffff
    0x89,0xb8, XX4,                 // 135752f0   89b8 1c004007    mov dword ptr ds:[eax+0x740001c],edi
    0x8b,0x2d, XX4,                 // 135752f6   8b2d bca71001    mov ebp,dword ptr ds:[0x110a7bc]
    0x8b,0xc6,                      // 135752fc   8bc6             mov eax,esi
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 135752fe   81e0 ffffff3f    and eax,0x3fffffff
    0x89,0xa8, XX4,                 // 13575304   89a8 18004007    mov dword ptr ds:[eax+0x7400018],ebp
    0x8b,0x15, XX4,                 // 1357530a   8b15 b8a71001    mov edx,dword ptr ds:[0x110a7b8]
    0x8b,0xc6,                      // 13575310   8bc6             mov eax,esi
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 13575312   81e0 ffffff3f    and eax,0x3fffffff
    0x89,0x90, XX4,                 // 13575318   8990 14004007    mov dword ptr ds:[eax+0x7400014],edx
    0x8b,0x0d, XX4,                 // 1357531e   8b0d b4a71001    mov ecx,dword ptr ds:[0x110a7b4]
    0x8b,0xc6,                      // 13575324   8bc6             mov eax,esi
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 13575326   81e0 ffffff3f    and eax,0x3fffffff
    0x89,0x88, XX4,                 // 1357532c   8988 10004007    mov dword ptr ds:[eax+0x7400010],ecx
    0x8b,0x3d, XX4,                 // 13575332   8b3d b0a71001    mov edi,dword ptr ds:[0x110a7b0]
    0x8b,0xc6,                      // 13575338   8bc6             mov eax,esi
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 1357533a   81e0 ffffff3f    and eax,0x3fffffff
    0x89,0xb8, XX4,                 // 13575340   89b8 0c004007    mov dword ptr ds:[eax+0x740000c],edi
    0x8b,0x3d, XX4,                 // 13575346   8b3d aca71001    mov edi,dword ptr ds:[0x110a7ac]
    0x8b,0xc6,                      // 1357534c   8bc6             mov eax,esi
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 1357534e   81e0 ffffff3f    and eax,0x3fffffff
    0x89,0xb8, XX4,                 // 13575354   89b8 08004007    mov dword ptr ds:[eax+0x7400008],edi
    0x8b,0x3d, XX4,                 // 1357535a   8b3d a8a71001    mov edi,dword ptr ds:[0x110a7a8]
    0x8b,0xc6,                      // 13575360   8bc6             mov eax,esi
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 13575362   81e0 ffffff3f    and eax,0x3fffffff
    0x89,0xb8, XX4,                 // 13575368   89b8 04004007    mov dword ptr ds:[eax+0x7400004],edi
    0x8b,0x15, XX4,                 // 1357536e   8b15 78a71001    mov edx,dword ptr ds:[0x110a778]
    0x89,0x35, XX4,                 // 13575374   8935 dca71001    mov dword ptr ds:[0x110a7dc],esi
    0x8b,0x05, XX4,                 // 1357537a   8b05 7ca71001    mov eax,dword ptr ds:[0x110a77c]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 13575380   81e0 ffffff3f    and eax,0x3fffffff
    0x0f,0xbe,0xb0 //, XX4          // 13575386   0fbeb0 00004007  movsx esi,byte ptr ds:[eax+0x7400000] ; jichi: hook here
  };
  enum { memory_offset = 3 }; // 13575386   0fbeb0 00004007  movsx esi,byte ptr ds:[eax+0x7400000]
  enum { addr_offset = sizeof(bytes) - memory_offset };

  enum : DWORD { start = MemDbg::MappedMemoryStartAddress };
  DWORD stop = PPSSPP_VERSION[1] == 9 && PPSSPP_VERSION[2] == 8 ? MemDbg::MemoryStopAddress : 0x15000000;
  DWORD addr = SafeMatchBytesInPSPMemory(bytes, sizeof(bytes), start, stop);
  //GROWL_DWORD(addr);
  if (!addr)
    ConsoleOutput("vnreng: 5pb PSP: pattern not found");
  else {
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.user_value = *(DWORD *)(hp.address + memory_offset);
    hp.text_fun = SpecialPSPHook5pb;
    hp.type = USING_STRING|NO_CONTEXT; // no context is needed to get rid of variant retaddr
    ConsoleOutput("vnreng: 5pb PSP: INSERT");
    NewHook(hp, "5pb PSP");
  }

  ConsoleOutput("vnreng: 5pb PSP: leave");
  return addr;
}

/** 7/13/2014 jichi imageepoch.co.jp PSP engine, 0.9.8, 0.9.9
 *  Sample game: BLACK�OCK SHOOTER
 *
 *  Float memory addresses: two matches, UTF-8
 *
 *  7/29/2014: seems to work on 0.9.9
 *
 *  Debug method: find current sentence, then find next sentence in the memory
 *  and add break-points
 *
 *  1346d34b   f0:90            lock nop                                 ; lock prefix is not allowed
 *  1346d34d   cc               int3
 *  1346d34e   cc               int3
 *  1346d34f   cc               int3
 *  1346d350   77 0f            ja short 1346d361
 *  1346d352   c705 a8aa1001 e4>mov dword ptr ds:[0x110aaa8],0x89609e4
 *  1346d35c  -e9 a32c27f0      jmp 036e0004
 *  1346d361   8b05 78a71001    mov eax,dword ptr ds:[0x110a778]
 *  1346d367   81e0 ffffff3f    and eax,0x3fffffff
 *  1346d36d   8bb0 00004007    mov esi,dword ptr ds:[eax+0x7400000] ; jichi: or hook here
 *  1346d373   8b3d 78a71001    mov edi,dword ptr ds:[0x110a778]
 *  1346d379   8bc6             mov eax,esi
 *  1346d37b   81e0 ffffff3f    and eax,0x3fffffff
 *  1346d381   0fb6a8 00004007  movzx ebp,byte ptr ds:[eax+0x7400000] ; jichi: hook here
 *  1346d388   8d56 01          lea edx,dword ptr ds:[esi+0x1]
 *  1346d38b   8bc5             mov eax,ebp
 *  1346d38d   0fbec8           movsx ecx,al
 *  1346d390   8935 70a71001    mov dword ptr ds:[0x110a770],esi
 *  1346d396   8bf5             mov esi,ebp
 *  1346d398   81f9 00000000    cmp ecx,0x0
 *  1346d39e   892d 74a71001    mov dword ptr ds:[0x110a774],ebp
 *  1346d3a4   8935 78a71001    mov dword ptr ds:[0x110a778],esi
 *  1346d3aa   8915 7ca71001    mov dword ptr ds:[0x110a77c],edx
 *  1346d3b0   890d 80a71001    mov dword ptr ds:[0x110a780],ecx
 *  1346d3b6   893d 84a71001    mov dword ptr ds:[0x110a784],edi
 *  1346d3bc   0f8d 16000000    jge 1346d3d8
 *  1346d3c2   832d c4aa1001 07 sub dword ptr ds:[0x110aac4],0x7
 *  1346d3c9   e9 22000000      jmp 1346d3f0
 *  1346d3ce   010c0a           add dword ptr ds:[edx+ecx],ecx
 *  1346d3d1   96               xchg eax,esi
 *  1346d3d2   08e9             or cl,ch
 *  1346d3d4   4b               dec ebx
 *  1346d3d5   2c 27            sub al,0x27
 *  1346d3d7   f0:832d c4aa1001>lock sub dword ptr ds:[0x110aac4],0x7    ; lock prefix
 *  1346d3df   e9 bc380000      jmp 13470ca0
 *  1346d3e4   0100             add dword ptr ds:[eax],eax
 *  1346d3e6   0a96 08e9352c    or dl,byte ptr ds:[esi+0x2c35e908]
 *  1346d3ec   27               daa
 *  1346d3ed   f0:90            lock nop                                 ; lock prefix is not allowed
 *  1346d3ef   cc               int3
 */
static void SpecialPSPHookImageepoch(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  // 7/25/2014: I tried using uniquemap to eliminate duplication, which does not work
  DWORD eax = regof(eax, esp_base);
  DWORD text = eax + hp->user_value;
  static DWORD lasttext; // Prevent reading the same address multiple times
  if (text != lasttext && *(LPCSTR)text) {
    *data = lasttext = text;
    *len = ::strlen((LPCSTR)text); // UTF-8 is null-terminated
    *split = regof(ecx, esp_base); // use ecx = "this" to split?
  }
}

bool InsertImageepochPSPHook()
{
  ConsoleOutput("vnreng: Imageepoch PSP: enter");

  const BYTE bytes[] =  {
    //0xcc,                         // 1346d34f   cc               int3
    0x77, 0x0f,                     // 1346d350   77 0f            ja short 1346d361
    0xc7,0x05, XX8,                 // 1346d352   c705 a8aa1001 e4>mov dword ptr ds:[0x110aaa8],0x89609e4
    0xe9, XX4,                      // 1346d35c  -e9 a32c27f0      jmp 036e0004
    0x8b,0x05, XX4,                 // 1346d361   8b05 78a71001    mov eax,dword ptr ds:[0x110a778]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 1346d367   81e0 ffffff3f    and eax,0x3fffffff
    0x8b,0xb0, XX4,                 // 1346d36d   8bb0 00004007    mov esi,dword ptr ds:[eax+0x7400000] ; jichi: or hook here
    0x8b,0x3d, XX4,                 // 1346d373   8b3d 78a71001    mov edi,dword ptr ds:[0x110a778]
    0x8b,0xc6,                      // 1346d379   8bc6             mov eax,esi
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 1346d37b   81e0 ffffff3f    and eax,0x3fffffff
    0x0f,0xb6,0xa8, XX4,            // 1346d381   0fb6a8 00004007  movzx ebp,byte ptr ds:[eax+0x7400000] ; jichi: hook here
    0x8d,0x56, 0x01,                // 1346d388   8d56 01          lea edx,dword ptr ds:[esi+0x1]
    0x8b,0xc5,                      // 1346d38b   8bc5             mov eax,ebp
    0x0f,0xbe,0xc8                  // 1346d38d   0fbec8           movsx ecx,al
  };
  enum { memory_offset = 3 }; // 1346d381   0fb6a8 00004007  movzx ebp,byte ptr ds:[eax+0x7400000]
  enum { addr_offset = 0x1346d381 - 0x1346d350 };
  //enum { addr_offset = sizeof(bytes) - memory_offset };

  DWORD addr = SafeMatchBytesInPSPMemory(bytes, sizeof(bytes));
  if (!addr)
    ConsoleOutput("vnreng: Imageepoch PSP: pattern not found");
  else {
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.user_value = *(DWORD *)(hp.address + memory_offset);
    hp.type = USING_STRING|USING_SPLIT|NO_CONTEXT; // UTF-8, though
    hp.offset = pusha_eax_off - 4;
    hp.split = pusha_ecx_off - 4;
    hp.user_flags = HPF_IgnoreSameAddress;
    //hp.text_fun = SpecialPSPHook;
    hp.text_fun = SpecialPSPHookImageepoch; // since this function is common, use its own static lasttext for HPF_IgnoreSameAddress
    ConsoleOutput("vnreng: Imageepoch PSP: INSERT");
    NewHook(hp, "Imageepoch PSP");
  }

  ConsoleOutput("vnreng: Imageepoch PSP: leave");
  return addr;
}

/** 8/9/2014 jichi imageepoch.co.jp PSP engine, 0.9.8, 0.9.9
 *  Sample game: Sol Trigger (0.9.8, 0.9.9)
 *
 *  Though Imageepoch1 also exists, it cannot find scenario text.
 *
 *  FIXED memory addresses (different from Imageepoch1): two matches, UTF-8
 *
 *  Debug method: find current text and add breakpoint.
 *
 *  There a couple of good functions. The first one is used.
 *  There is only one text threads. But it cannot extract character names.
 *
 *  135fd497   cc               int3
 *  135fd498   77 0f            ja short 135fd4a9
 *  135fd49a   c705 a8aa1001 20>mov dword ptr ds:[0x110aaa8],0x8952d20
 *  135fd4a4  -e9 5b2b2ef0      jmp 038e0004
 *  135fd4a9   8b35 dca71001    mov esi,dword ptr ds:[0x110a7dc]
 *  135fd4af   81c6 04000000    add esi,0x4
 *  135fd4b5   8b05 a8a71001    mov eax,dword ptr ds:[0x110a7a8]
 *  135fd4bb   81e0 ffffff3f    and eax,0x3fffffff
 *  135fd4c1   0fb6b8 00004007  movzx edi,byte ptr ds:[eax+0x7400000]   ; jichi: hook here
 *  135fd4c8   813d 68a71001 00>cmp dword ptr ds:[0x110a768],0x0
 *  135fd4d2   893d 78a71001    mov dword ptr ds:[0x110a778],edi
 *  135fd4d8   c705 aca71001 23>mov dword ptr ds:[0x110a7ac],0x23434623
 *  135fd4e2   c705 b0a71001 30>mov dword ptr ds:[0x110a7b0],0x30303030
 *  135fd4ec   8935 b4a71001    mov dword ptr ds:[0x110a7b4],esi
 *  135fd4f2   c705 b8a71001 00>mov dword ptr ds:[0x110a7b8],0x0
 *  135fd4fc   0f85 16000000    jnz 135fd518
 *  135fd502   832d c4aa1001 08 sub dword ptr ds:[0x110aac4],0x8
 *  135fd509   e9 22000000      jmp 135fd530
 *  135fd50e   01642d 95        add dword ptr ss:[ebp+ebp-0x6b],esp
 *  135fd512   08e9             or cl,ch
 *  135fd514   0b2b             or ebp,dword ptr ds:[ebx]
 *  135fd516   2e:f0:832d c4aa1>lock sub dword ptr cs:[0x110aac4],0x8    ; lock prefix
 *  135fd51f   c705 a8aa1001 40>mov dword ptr ds:[0x110aaa8],0x8952d40
 *  135fd529  -e9 f52a2ef0      jmp 038e0023
 *  135fd52e   90               nop
 *  135fd52f   cc               int3
 */
bool InsertImageepoch2PSPHook()
{
  ConsoleOutput("vnreng: Imageepoch2 PSP: enter");

  const BYTE bytes[] =  {
                                         // 135fd497   cc               int3
    0x77, 0x0f,                          // 135fd498   77 0f            ja short 135fd4a9
    0xc7,0x05, XX8,                      // 135fd49a   c705 a8aa1001 20>mov dword ptr ds:[0x110aaa8],0x8952d20
    0xe9, XX4,                           // 135fd4a4  -e9 5b2b2ef0      jmp 038e0004
    0x8b,0x35, XX4,                      // 135fd4a9   8b35 dca71001    mov esi,dword ptr ds:[0x110a7dc]
    0x81,0xc6, 0x04,0x00,0x00,0x00,      // 135fd4af   81c6 04000000    add esi,0x4
    0x8b,0x05, XX4,                      // 135fd4b5   8b05 a8a71001    mov eax,dword ptr ds:[0x110a7a8]
    0x81,0xe0, 0xff,0xff,0xff,0x3f,      // 135fd4bb   81e0 ffffff3f    and eax,0x3fffffff
    0x0f,0xb6,0xb8, XX4,                 // 135fd4c1   0fb6b8 00004007  movzx edi,byte ptr ds:[eax+0x7400000]   ; jichi: hook here
    0x81,0x3d, XX4, 0x00,0x00,0x00,0x00, // 135fd4c8   813d 68a71001 00>cmp dword ptr ds:[0x110a768],0x0
    0x89,0x3d, XX4,                      // 135fd4d2   893d 78a71001    mov dword ptr ds:[0x110a778],edi
    0xc7,0x05, XX8,                      // 135fd4d8   c705 aca71001 23>mov dword ptr ds:[0x110a7ac],0x23434623
    0xc7,0x05, XX8,                      // 135fd4e2   c705 b0a71001 30>mov dword ptr ds:[0x110a7b0],0x30303030
    0x89,0x35, XX4,                      // 135fd4ec   8935 b4a71001    mov dword ptr ds:[0x110a7b4],esi
    0xc7,0x05, XX4, 0x00,0x00,0x00,0x00, // 135fd4f2   c705 b8a71001 00>mov dword ptr ds:[0x110a7b8],0x0
    0x0f,0x85 //, XX4,                   // 135fd4fc   0f85 16000000    jnz 135fd518
  };
  enum { memory_offset = 3 }; // 1346d381   0fb6a8 00004007  movzx ebp,byte ptr ds:[eax+0x7400000]
  enum { addr_offset = 0x135fd4c1 - 0x135fd498 };

  DWORD addr = SafeMatchBytesInPSPMemory(bytes, sizeof(bytes));
  if (!addr)
    ConsoleOutput("vnreng: Imageepoch2 PSP: pattern not found");
  else {
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.user_value = *(DWORD *)(hp.address + memory_offset);
    hp.type = USING_STRING|USING_SPLIT|NO_CONTEXT; // UTF-8, though
    hp.offset = pusha_eax_off - 4;
    hp.split = pusha_ecx_off - 4;
    hp.text_fun = SpecialPSPHook;
    ConsoleOutput("vnreng: Imageepoch2 PSP: INSERT");
    NewHook(hp, "Imageepoch2 PSP");
  }

  ConsoleOutput("vnreng: Imageepoch2 PSP: leave");
  return addr;
}

/** 7/19/2014 jichi yetigame.jp PSP engine, 0.9.8, 0.9.9
 *  Sample game: Secret Game Portable 0.9.8/0.9.9
 *
 *  Float memory addresses: two matches
 *
 *  Debug method: find current sentence, then find next sentence in the memory
 *  and add break-points. Need to patch 1 leading \u3000 space.
 *
 *  It seems that each time I ran the game, the instruction pattern would change?!
 *  == The second time I ran the game ==
 *
 *  14e49ed9   90               nop
 *  14e49eda   cc               int3
 *  14e49edb   cc               int3
 *  14e49edc   77 0f            ja short 14e49eed
 *  14e49ede   c705 a8aa1001 98>mov dword ptr ds:[0x110aaa8],0x885ff98
 *  14e49ee8  -e9 17619eee      jmp 03830004
 *  14e49eed   8b35 70a71001    mov esi,dword ptr ds:[0x110a770]
 *  14e49ef3   c1ee 1f          shr esi,0x1f
 *  14e49ef6   8b05 b4a71001    mov eax,dword ptr ds:[0x110a7b4]
 *  14e49efc   81e0 ffffff3f    and eax,0x3fffffff
 *  14e49f02   8bb8 14deff07    mov edi,dword ptr ds:[eax+0x7ffde14]
 *  14e49f08   0335 70a71001    add esi,dword ptr ds:[0x110a770]
 *  14e49f0e   d1fe             sar esi,1
 *  14e49f10   8b05 b0a71001    mov eax,dword ptr ds:[0x110a7b0]
 *  14e49f16   81e0 ffffff3f    and eax,0x3fffffff
 *  14e49f1c   89b8 00000008    mov dword ptr ds:[eax+0x8000000],edi
 *  14e49f22   8b05 dca71001    mov eax,dword ptr ds:[0x110a7dc]
 *  14e49f28   81e0 ffffff3f    and eax,0x3fffffff
 *  14e49f2e   89b0 30000008    mov dword ptr ds:[eax+0x8000030],esi
 *  14e49f34   8b05 b4a71001    mov eax,dword ptr ds:[0x110a7b4]
 *  14e49f3a   81e0 ffffff3f    and eax,0x3fffffff
 *  14e49f40   8ba8 14deff07    mov ebp,dword ptr ds:[eax+0x7ffde14]
 *  14e49f46   8bc5             mov eax,ebp
 *  14e49f48   81e0 ffffff3f    and eax,0x3fffffff
 *  14e49f4e   0fb6b0 00000008  movzx esi,byte ptr ds:[eax+0x8000000] ; jichi: hook here
 *  14e49f55   8d6d 01          lea ebp,dword ptr ss:[ebp+0x1]
 *  14e49f58   8b05 b4a71001    mov eax,dword ptr ds:[0x110a7b4]
 *
 *  == The first time I ran the game ==
 *  There are a couple of good break-points, as follows.
 *  Only the second function is hooked.
 *
 *  138cf7a2   cc               int3
 *  138cf7a3   cc               int3
 *  138cf7a4   77 0f            ja short 138cf7b5
 *  138cf7a6   c705 a8aa1001 90>mov dword ptr ds:[0x110aaa8],0x885ff90
 *  138cf7b0  -e9 4f08a9f3      jmp 07360004
 *  138cf7b5   8b05 b4a71001    mov eax,dword ptr ds:[0x110a7b4]
 *  138cf7bb   81e0 ffffff3f    and eax,0x3fffffff
 *  138cf7c1   8bb0 14de7f07    mov esi,dword ptr ds:[eax+0x77fde14]
 *  138cf7c7   8935 78a71001    mov dword ptr ds:[0x110a778],esi
 *  138cf7cd   c705 e4a71001 98>mov dword ptr ds:[0x110a7e4],0x885ff98
 *  138cf7d7   832d c4aa1001 02 sub dword ptr ds:[0x110aac4],0x2
 *  138cf7de   e9 0d000000      jmp 138cf7f0
 *  138cf7e3   015c48 85        add dword ptr ds:[eax+ecx*2-0x7b],ebx
 *  138cf7e7   08e9             or cl,ch
 *  138cf7e9   36:08a9 f390cccc or byte ptr ss:[ecx+0xcccc90f3],ch
 *  138cf7f0   77 0f            ja short 138cf801
 *  138cf7f2   c705 a8aa1001 5c>mov dword ptr ds:[0x110aaa8],0x885485c
 *  138cf7fc  -e9 0308a9f3      jmp 07360004
 *  138cf801   8b05 78a71001    mov eax,dword ptr ds:[0x110a778]
 *  138cf807   81e0 ffffff3f    and eax,0x3fffffff
 *  138cf80d   0fb6b0 00008007  movzx esi,byte ptr ds:[eax+0x7800000] ; jichi: hook here
 *  138cf814   81fe 00000000    cmp esi,0x0
 *  138cf81a   8935 74a71001    mov dword ptr ds:[0x110a774],esi
 *  138cf820   c705 80a71001 00>mov dword ptr ds:[0x110a780],0x0
 *  138cf82a   c705 84a71001 25>mov dword ptr ds:[0x110a784],0x25
 *  138cf834   c705 88a71001 4e>mov dword ptr ds:[0x110a788],0x4e
 *  138cf83e   c705 8ca71001 6e>mov dword ptr ds:[0x110a78c],0x6e
 *  138cf848   0f85 16000000    jnz 138cf864
 *  138cf84e   832d c4aa1001 06 sub dword ptr ds:[0x110aac4],0x6
 *  138cf855   e9 b6010000      jmp 138cfa10
 *  138cf85a   01bc48 8508e9bf  add dword ptr ds:[eax+ecx*2+0xbfe90885],>
 *  138cf861   07               pop es                                   ; modification of segment register
 *  138cf862   a9 f3832dc4      test eax,0xc42d83f3
 *  138cf867   aa               stos byte ptr es:[edi]
 *  138cf868   1001             adc byte ptr ds:[ecx],al
 *  138cf86a   06               push es
 *  138cf86b   e9 0c000000      jmp 138cf87c
 *  138cf870   017448 85        add dword ptr ds:[eax+ecx*2-0x7b],esi
 *  138cf874   08e9             or cl,ch
 *  138cf876   a9 07a9f390      test eax,0x90f3a907
 *  138cf87b   cc               int3
 *
 *  This function is used.
 *  138cfa46   cc               int3
 *  138cfa47   cc               int3
 *  138cfa48   77 0f            ja short 138cfa59
 *  138cfa4a   c705 a8aa1001 98>mov dword ptr ds:[0x110aaa8],0x885ff98
 *  138cfa54  -e9 ab05a9f3      jmp 07360004
 *  138cfa59   8b35 70a71001    mov esi,dword ptr ds:[0x110a770]
 *  138cfa5f   c1ee 1f          shr esi,0x1f
 *  138cfa62   8b05 b4a71001    mov eax,dword ptr ds:[0x110a7b4]
 *  138cfa68   81e0 ffffff3f    and eax,0x3fffffff
 *  138cfa6e   8bb8 14de7f07    mov edi,dword ptr ds:[eax+0x77fde14]
 *  138cfa74   0335 70a71001    add esi,dword ptr ds:[0x110a770]
 *  138cfa7a   d1fe             sar esi,1
 *  138cfa7c   8b05 b0a71001    mov eax,dword ptr ds:[0x110a7b0]
 *  138cfa82   81e0 ffffff3f    and eax,0x3fffffff
 *  138cfa88   89b8 00008007    mov dword ptr ds:[eax+0x7800000],edi
 *  138cfa8e   8b05 dca71001    mov eax,dword ptr ds:[0x110a7dc]
 *  138cfa94   81e0 ffffff3f    and eax,0x3fffffff
 *  138cfa9a   89b0 30008007    mov dword ptr ds:[eax+0x7800030],esi
 *  138cfaa0   8b05 b4a71001    mov eax,dword ptr ds:[0x110a7b4]
 *  138cfaa6   81e0 ffffff3f    and eax,0x3fffffff
 *  138cfaac   8ba8 14de7f07    mov ebp,dword ptr ds:[eax+0x77fde14]
 *  138cfab2   8bc5             mov eax,ebp
 *  138cfab4   81e0 ffffff3f    and eax,0x3fffffff
 *  138cfaba   0fb6b0 00008007  movzx esi,byte ptr ds:[eax+0x7800000] ; jichi: hook here
 *  138cfac1   8d6d 01          lea ebp,dword ptr ss:[ebp+0x1]
 *  138cfac4   8b05 b4a71001    mov eax,dword ptr ds:[0x110a7b4]
 *  138cfaca   81e0 ffffff3f    and eax,0x3fffffff
 *  138cfad0   89a8 14de7f07    mov dword ptr ds:[eax+0x77fde14],ebp
 *  138cfad6   81fe 00000000    cmp esi,0x0
 *  138cfadc   892d 70a71001    mov dword ptr ds:[0x110a770],ebp
 *  138cfae2   8935 74a71001    mov dword ptr ds:[0x110a774],esi
 *  138cfae8   893d aca71001    mov dword ptr ds:[0x110a7ac],edi
 *  138cfaee   0f84 16000000    je 138cfb0a
 *  138cfaf4   832d c4aa1001 0b sub dword ptr ds:[0x110aac4],0xb
 *  138cfafb   e9 24000000      jmp 138cfb24
 *  138cfb00   01b0 ff8508e9    add dword ptr ds:[eax+0xe90885ff],esi
 *  138cfb06   1905 a9f3832d    sbb dword ptr ds:[0x2d83f3a9],eax
 *  138cfb0c   c4aa 10010be9    les ebp,fword ptr ds:[edx+0xe90b0110]    ; modification of segment register
 *  138cfb12   9a 00000001 c4ff call far ffc4:01000000                   ; far call
 *  138cfb19   8508             test dword ptr ds:[eax],ecx
 *  138cfb1b  -e9 0305a9f3      jmp 07360023
 *  138cfb20   90               nop
 *  138cfb21   cc               int3
 *  138cfb22   cc               int3
 *
 *  138cfb22   cc               int3
 *  138cfb23   cc               int3
 *  138cfb24   77 0f            ja short 138cfb35
 *  138cfb26   c705 a8aa1001 b0>mov dword ptr ds:[0x110aaa8],0x885ffb0
 *  138cfb30  -e9 cf04a9f3      jmp 07360004
 *  138cfb35   8b05 b4a71001    mov eax,dword ptr ds:[0x110a7b4]
 *  138cfb3b   81e0 ffffff3f    and eax,0x3fffffff
 *  138cfb41   8bb0 14de7f07    mov esi,dword ptr ds:[eax+0x77fde14]
 *  138cfb47   8bc6             mov eax,esi
 *  138cfb49   81e0 ffffff3f    and eax,0x3fffffff
 *  138cfb4f   0fb6b8 00008007  movzx edi,byte ptr ds:[eax+0x7800000] ; jichi: hook here
 *  138cfb56   8d76 01          lea esi,dword ptr ds:[esi+0x1]
 *  138cfb59   8b05 b4a71001    mov eax,dword ptr ds:[0x110a7b4]
 *  138cfb5f   81e0 ffffff3f    and eax,0x3fffffff
 *  138cfb65   89b0 14de7f07    mov dword ptr ds:[eax+0x77fde14],esi
 *  138cfb6b   81ff 00000000    cmp edi,0x0
 *  138cfb71   8935 70a71001    mov dword ptr ds:[0x110a770],esi
 *  138cfb77   893d 74a71001    mov dword ptr ds:[0x110a774],edi
 *  138cfb7d   0f84 16000000    je 138cfb99
 *  138cfb83   832d c4aa1001 05 sub dword ptr ds:[0x110aac4],0x5
 *  138cfb8a  ^e9 95ffffff      jmp 138cfb24
 *  138cfb8f   01b0 ff8508e9    add dword ptr ds:[eax+0xe90885ff],esi
 *  138cfb95   8a04a9           mov al,byte ptr ds:[ecx+ebp*4]
 *  138cfb98   f3:              prefix rep:                              ; superfluous prefix
 *  138cfb99   832d c4aa1001 05 sub dword ptr ds:[0x110aac4],0x5
 *  138cfba0   e9 0b000000      jmp 138cfbb0
 *  138cfba5   01c4             add esp,eax
 *  138cfba7   ff85 08e97404    inc dword ptr ss:[ebp+0x474e908]
 *  138cfbad   a9 f390770f      test eax,0xf7790f3
 *  138cfbb2   c705 a8aa1001 c4>mov dword ptr ds:[0x110aaa8],0x885ffc4
 *  138cfbbc  -e9 4304a9f3      jmp 07360004
 *  138cfbc1   f3:0f1015 6c1609>movss xmm2,dword ptr ds:[0x1009166c]
 *  138cfbc9   8b05 b0a71001    mov eax,dword ptr ds:[0x110a7b0]
 *  138cfbcf   81e0 ffffff3f    and eax,0x3fffffff
 *  138cfbd5   8bb0 00008007    mov esi,dword ptr ds:[eax+0x7800000]
 *  138cfbdb   f3:0f101d 641609>movss xmm3,dword ptr ds:[0x10091664]
 *  138cfbe3   c7c7 00000000    mov edi,0x0
 *  138cfbe9   893d f4b12b11    mov dword ptr ds:[0x112bb1f4],edi
 *  138cfbef   8bc6             mov eax,esi
 *  138cfbf1   81e0 ffffff3f    and eax,0x3fffffff
 *  138cfbf7   0fb6a8 00008007  movzx ebp,byte ptr ds:[eax+0x7800000]  ; jichi: hook here
 *  138cfbfe   81fd 00000000    cmp ebp,0x0
 *  138cfc04   c705 70a71001 00>mov dword ptr ds:[0x110a770],0x9ac0000
 *  138cfc0e   c705 74a71001 00>mov dword ptr ds:[0x110a774],0x8890000
 *  138cfc18   892d a8a71001    mov dword ptr ds:[0x110a7a8],ebp
 *  138cfc1e   8935 aca71001    mov dword ptr ds:[0x110a7ac],esi
 *  138cfc24   c705 b4a71001 00>mov dword ptr ds:[0x110a7b4],0x8890000
 *  138cfc2e   c705 b8a71001 80>mov dword ptr ds:[0x110a7b8],0x80
 *  138cfc38   c705 bca71001 00>mov dword ptr ds:[0x110a7bc],0x0
 *  138cfc42   c705 e0a71001 00>mov dword ptr ds:[0x110a7e0],0x0
 *  138cfc4c   f3:0f111d 3ca810>movss dword ptr ds:[0x110a83c],xmm3
 *  138cfc54   f3:0f1115 40a810>movss dword ptr ds:[0x110a840],xmm2
 *  138cfc5c   0f85 16000000    jnz 138cfc78
 *  138cfc62   832d c4aa1001 0d sub dword ptr ds:[0x110aac4],0xd
 *  138cfc69   e9 32270000      jmp 138d23a0
 *  138cfc6e   0158 00          add dword ptr ds:[eax],ebx
 *  138cfc71   8608             xchg byte ptr ds:[eax],cl
 *  138cfc73  -e9 ab03a9f3      jmp 07360023
 *  138cfc78   832d c4aa1001 0d sub dword ptr ds:[0x110aac4],0xd
 *  138cfc7f   e9 0c000000      jmp 138cfc90
 *  138cfc84   01f8             add eax,edi
 *  138cfc86   ff85 08e99503    inc dword ptr ss:[ebp+0x395e908]
 *  138cfc8c   a9 f390cc77      test eax,0x77cc90f3
 *  138cfc91   0fc7             ???                                      ; unknown command
 *  138cfc93   05 a8aa1001      add eax,0x110aaa8
 *  138cfc98   f8               clc
 *  138cfc99   ff85 08e96303    inc dword ptr ss:[ebp+0x363e908]
 *  138cfc9f   a9 f38b35ac      test eax,0xac358bf3
 *  138cfca4   a7               cmps dword ptr ds:[esi],dword ptr es:[ed>
 *  138cfca5   1001             adc byte ptr ds:[ecx],al
 *  138cfca7   8b3d b4a71001    mov edi,dword ptr ds:[0x110a7b4]
 *  138cfcad   81c7 48d6ffff    add edi,-0x29b8
 *  138cfcb3   8935 78a71001    mov dword ptr ds:[0x110a778],esi
 *  138cfcb9   893d 7ca71001    mov dword ptr ds:[0x110a77c],edi
 *  138cfcbf   c705 80a71001 02>mov dword ptr ds:[0x110a780],0x2
 *  138cfcc9   c705 e4a71001 08>mov dword ptr ds:[0x110a7e4],0x8860008
 *  138cfcd3   832d c4aa1001 04 sub dword ptr ds:[0x110aac4],0x4
 *  138cfcda  ^e9 4914f4ff      jmp 13811128
 *  138cfcdf   90               nop
 *  138cfce0   77 0f            ja short 138cfcf1
 *  138cfce2   c705 a8aa1001 74>mov dword ptr ds:[0x110aaa8],0x8844574
 *  138cfcec  -e9 1303a9f3      jmp 07360004
 *  138cfcf1   8b35 84a71001    mov esi,dword ptr ds:[0x110a784]
 *  138cfcf7   81c6 ffffffff    add esi,-0x1
 *  138cfcfd   813d 84a71001 00>cmp dword ptr ds:[0x110a784],0x0
 *  138cfd07   8935 8ca71001    mov dword ptr ds:[0x110a78c],esi
 *  138cfd0d   0f85 16000000    jnz 138cfd29
 *  138cfd13   832d c4aa1001 02 sub dword ptr ds:[0x110aac4],0x2
 *  138cfd1a   c705 a8aa1001 e0>mov dword ptr ds:[0x110aaa8],0x88445e0
 *  138cfd24  -e9 fa02a9f3      jmp 07360023
 *  138cfd29   832d c4aa1001 02 sub dword ptr ds:[0x110aac4],0x2
 *  138cfd30  ^e9 ab15f4ff      jmp 138112e0
 *  138cfd35   90               nop
 *  138cfd36   cc               int3
 *  138cfd37   cc               int3
 *
 *  13811266   cc               int3
 *  13811267   cc               int3
 *  13811268   77 0f            ja short 13811279
 *  1381126a   c705 a8aa1001 b0>mov dword ptr ds:[0x110aaa8],0x88445b0
 *  13811274  -e9 8bedb4f3      jmp 07360004
 *  13811279   8b35 8ca71001    mov esi,dword ptr ds:[0x110a78c]
 *  1381127f   8b3d 88a71001    mov edi,dword ptr ds:[0x110a788]
 *  13811285   8b2d 84a71001    mov ebp,dword ptr ds:[0x110a784]
 *  1381128b   81c5 ffffffff    add ebp,-0x1
 *  13811291   813d 84a71001 00>cmp dword ptr ds:[0x110a784],0x0
 *  1381129b   8935 78a71001    mov dword ptr ds:[0x110a778],esi
 *  138112a1   893d 7ca71001    mov dword ptr ds:[0x110a77c],edi
 *  138112a7   892d 8ca71001    mov dword ptr ds:[0x110a78c],ebp
 *  138112ad   0f84 16000000    je 138112c9
 *  138112b3   832d c4aa1001 04 sub dword ptr ds:[0x110aac4],0x4
 *  138112ba   e9 21000000      jmp 138112e0
 *  138112bf   017c45 84        add dword ptr ss:[ebp+eax*2-0x7c],edi
 *  138112c3   08e9             or cl,ch
 *  138112c5   5a               pop edx
 *  138112c6   ed               in eax,dx                                ; i/o command
 *  138112c7   b4 f3            mov ah,0xf3
 *  138112c9   832d c4aa1001 04 sub dword ptr ds:[0x110aac4],0x4
 *  138112d0   c705 a8aa1001 c0>mov dword ptr ds:[0x110aaa8],0x88445c0
 *  138112da  -e9 44edb4f3      jmp 07360023
 *  138112df   90               nop
 *  138112e0   77 0f            ja short 138112f1
 *  138112e2   c705 a8aa1001 7c>mov dword ptr ds:[0x110aaa8],0x884457c
 *  138112ec  -e9 13edb4f3      jmp 07360004
 *  138112f1   8b05 7ca71001    mov eax,dword ptr ds:[0x110a77c]
 *  138112f7   81e0 ffffff3f    and eax,0x3fffffff
 *  138112fd   0fb6b0 00008007  movzx esi,byte ptr ds:[eax+0x7800000] ; jichi: hook here
 *  13811304   8b05 78a71001    mov eax,dword ptr ds:[0x110a778]
 *  1381130a   81e0 ffffff3f    and eax,0x3fffffff
 *  13811310   0fbeb8 00008007  movsx edi,byte ptr ds:[eax+0x7800000] ; jichi: hook here
 *  13811317   8bc6             mov eax,esi
 *  13811319   0fbee8           movsx ebp,al
 *  1381131c   3bef             cmp ebp,edi
 *  1381131e   893d 70a71001    mov dword ptr ds:[0x110a770],edi
 *  13811324   892d 74a71001    mov dword ptr ds:[0x110a774],ebp
 *  1381132a   8935 80a71001    mov dword ptr ds:[0x110a780],esi
 *  13811330   0f85 16000000    jnz 1381134c
 *  13811336   832d c4aa1001 05 sub dword ptr ds:[0x110aac4],0x5
 *  1381133d   e9 56110000      jmp 13812498
 *  13811342   01c8             add eax,ecx
 *  13811344   45               inc ebp
 *  13811345   8408             test byte ptr ds:[eax],cl
 *  13811347  -e9 d7ecb4f3      jmp 07360023
 *  1381134c   832d c4aa1001 05 sub dword ptr ds:[0x110aac4],0x5
 *  13811353   e9 0c000000      jmp 13811364
 *  13811358   0190 458408e9    add dword ptr ds:[eax+0xe9088445],edx
 *  1381135e   c1ec b4          shr esp,0xb4                             ; shift constant out of range 1..31
 *  13811361   f3:              prefix rep:                              ; superfluous prefix
 *  13811362   90               nop
 *  13811363   cc               int3
 *
 *  13811362   90               nop
 *  13811363   cc               int3
 *  13811364   77 0f            ja short 13811375
 *  13811366   c705 a8aa1001 90>mov dword ptr ds:[0x110aaa8],0x8844590
 *  13811370  -e9 8fecb4f3      jmp 07360004
 *  13811375   8b05 78a71001    mov eax,dword ptr ds:[0x110a778]
 *  1381137b   81e0 ffffff3f    and eax,0x3fffffff
 *  13811381   0fb6b0 00008007  movzx esi,byte ptr ds:[eax+0x7800000] ; jichi: hook here
 *  13811388   81e6 ff000000    and esi,0xff
 *  1381138e   8b3d 80a71001    mov edi,dword ptr ds:[0x110a780]
 *  13811394   81e7 ff000000    and edi,0xff
 *  1381139a   8bc7             mov eax,edi
 *  1381139c   8bfe             mov edi,esi
 *  1381139e   2bf8             sub edi,eax
 *  138113a0   8b05 e4a71001    mov eax,dword ptr ds:[0x110a7e4]
 *  138113a6   893d 70a71001    mov dword ptr ds:[0x110a770],edi
 *  138113ac   8935 74a71001    mov dword ptr ds:[0x110a774],esi
 *  138113b2   8905 a8aa1001    mov dword ptr ds:[0x110aaa8],eax
 *  138113b8   832d c4aa1001 05 sub dword ptr ds:[0x110aac4],0x5
 *  138113bf  -e9 5fecb4f3      jmp 07360023
 *  138113c4   90               nop
 *  138113c5   cc               int3
 *  138113c6   cc               int3
 *  138113c7   cc               int3
 *
 *  138124f2   cc               int3
 *  138124f3   cc               int3
 *  138124f4   77 0f            ja short 13812505
 *  138124f6   c705 a8aa1001 d0>mov dword ptr ds:[0x110aaa8],0x88445d0
 *  13812500  -e9 ffdab4f3      jmp 07360004
 *  13812505   813d 74a71001 00>cmp dword ptr ds:[0x110a774],0x0
 *  1381250f   c705 90a71001 00>mov dword ptr ds:[0x110a790],0x0
 *  13812519   0f84 16000000    je 13812535
 *  1381251f   832d c4aa1001 02 sub dword ptr ds:[0x110aac4],0x2
 *  13812526   e9 21000000      jmp 1381254c
 *  1381252b   018446 8408e9ee  add dword ptr ds:[esi+eax*2+0xeee90884],>
 *  13812532   dab4f3 832dc4aa  fidiv dword ptr ds:[ebx+esi*8+0xaac42d83>
 *  13812539   1001             adc byte ptr ds:[ecx],al
 *  1381253b   02e9             add ch,cl
 *  1381253d   3302             xor eax,dword ptr ds:[edx]
 *  1381253f   0000             add byte ptr ds:[eax],al
 *  13812541   01d8             add eax,ebx
 *  13812543   45               inc ebp
 *  13812544   8408             test byte ptr ds:[eax],cl
 *  13812546  -e9 d8dab4f3      jmp 07360023
 *  1381254b   90               nop
 *  1381254c   77 0f            ja short 1381255d
 *  1381254e   c705 a8aa1001 84>mov dword ptr ds:[0x110aaa8],0x8844684
 *  13812558  -e9 a7dab4f3      jmp 07360004
 *  1381255d   8b35 78a71001    mov esi,dword ptr ds:[0x110a778]
 *  13812563   0335 8ca71001    add esi,dword ptr ds:[0x110a78c]
 *  13812569   8b3d 88a71001    mov edi,dword ptr ds:[0x110a788]
 *  1381256f   8d7f 01          lea edi,dword ptr ds:[edi+0x1]
 *  13812572   8b2d 7ca71001    mov ebp,dword ptr ds:[0x110a77c]
 *  13812578   8d6d 01          lea ebp,dword ptr ss:[ebp+0x1]
 *  1381257b   8b15 90a71001    mov edx,dword ptr ds:[0x110a790]
 *  13812581   3b15 8ca71001    cmp edx,dword ptr ds:[0x110a78c]
 *  13812587   892d 7ca71001    mov dword ptr ds:[0x110a77c],ebp
 *  1381258d   893d 88a71001    mov dword ptr ds:[0x110a788],edi
 *  13812593   8935 94a71001    mov dword ptr ds:[0x110a794],esi
 *  13812599   0f85 16000000    jnz 138125b5
 *  1381259f   832d c4aa1001 04 sub dword ptr ds:[0x110aac4],0x4
 *  138125a6   c705 a8aa1001 c4>mov dword ptr ds:[0x110aaa8],0x88446c4
 *  138125b0  -e9 6edab4f3      jmp 07360023
 *  138125b5   832d c4aa1001 04 sub dword ptr ds:[0x110aac4],0x4
 *  138125bc   e9 0b000000      jmp 138125cc
 *  138125c1   019446 8408e958  add dword ptr ds:[esi+eax*2+0x58e90884],>
 *  138125c8   dab4f3 90770fc7  fidiv dword ptr ds:[ebx+esi*8+0xc70f7790>
 *  138125cf   05 a8aa1001      add eax,0x110aaa8
 *  138125d4   94               xchg eax,esp
 *  138125d5   46               inc esi
 *  138125d6   8408             test byte ptr ds:[eax],cl
 *  138125d8  -e9 27dab4f3      jmp 07360004
 *  138125dd   8b05 88a71001    mov eax,dword ptr ds:[0x110a788]
 *  138125e3   81e0 ffffff3f    and eax,0x3fffffff
 *  138125e9   0fb6b0 00008007  movzx esi,byte ptr ds:[eax+0x7800000] ; jichi: hook here
 *  138125f0   8b05 7ca71001    mov eax,dword ptr ds:[0x110a77c]
 *  138125f6   81e0 ffffff3f    and eax,0x3fffffff
 *  138125fc   0fb6b8 00008007  movzx edi,byte ptr ds:[eax+0x7800000]
 *  13812603   8bc6             mov eax,esi
 *  13812605   0fbee8           movsx ebp,al
 *  13812608   8bc7             mov eax,edi
 *  1381260a   0fbed0           movsx edx,al
 *  1381260d   8b0d 90a71001    mov ecx,dword ptr ds:[0x110a790]
 *  13812613   8d49 01          lea ecx,dword ptr ds:[ecx+0x1]
 *  13812616   3bd5             cmp edx,ebp
 *  13812618   892d 70a71001    mov dword ptr ds:[0x110a770],ebp
 *  1381261e   8935 74a71001    mov dword ptr ds:[0x110a774],esi
 *  13812624   893d 80a71001    mov dword ptr ds:[0x110a780],edi
 *  1381262a   8915 84a71001    mov dword ptr ds:[0x110a784],edx
 *  13812630   890d 90a71001    mov dword ptr ds:[0x110a790],ecx
 *  13812636   0f84 16000000    je 13812652
 *  1381263c   832d c4aa1001 06 sub dword ptr ds:[0x110aac4],0x6
 *  13812643   e9 98d70b00      jmp 138cfde0
 *  13812648   019445 8408e9d1  add dword ptr ss:[ebp+eax*2+0xd1e90884],>
 *  1381264f   d9b4f3 832dc4aa  fstenv (28-byte) ptr ds:[ebx+esi*8+0xaac>
 *  13812656   1001             adc byte ptr ds:[ecx],al
 *  13812658   06               push es
 *  13812659   e9 0e000000      jmp 1381266c
 *  1381265e   01ac46 8408e9bb  add dword ptr ds:[esi+eax*2+0xbbe90884],>
 *  13812665   d9b4f3 90cccccc  fstenv (28-byte) ptr ds:[ebx+esi*8+0xccc>
 *  1381266c   77 0f            ja short 1381267d
 *  1381266e   c705 a8aa1001 ac>mov dword ptr ds:[0x110aaa8],0x88446ac
 *  13812678  -e9 87d9b4f3      jmp 07360004
 *  1381267d   8b35 88a71001    mov esi,dword ptr ds:[0x110a788]
 *  13812683   3b35 94a71001    cmp esi,dword ptr ds:[0x110a794]
 *  13812689   0f85 16000000    jnz 138126a5
 *  1381268f   832d c4aa1001 02 sub dword ptr ds:[0x110aac4],0x2
 *  13812696   e9 d9000000      jmp 13812774
 *  1381269b   01d8             add eax,ebx
 *  1381269d   45               inc ebp
 *  1381269e   8408             test byte ptr ds:[eax],cl
 *  138126a0  -e9 7ed9b4f3      jmp 07360023
 *  138126a5   832d c4aa1001 02 sub dword ptr ds:[0x110aac4],0x2
 *  138126ac   e9 0b000000      jmp 138126bc
 *  138126b1   01b446 8408e968  add dword ptr ds:[esi+eax*2+0x68e90884],>
 *  138126b8   d9b4f3 90770fc7  fstenv (28-byte) ptr ds:[ebx+esi*8+0xc70>
 *  138126bf   05 a8aa1001      add eax,0x110aaa8
 *  138126c4   b4 46            mov ah,0x46
 *  138126c6   8408             test byte ptr ds:[eax],cl
 *  138126c8  -e9 37d9b4f3      jmp 07360004
 *  138126cd   8b35 88a71001    mov esi,dword ptr ds:[0x110a788]
 *  138126d3   8d76 01          lea esi,dword ptr ds:[esi+0x1]
 *  138126d6   813d 84a71001 00>cmp dword ptr ds:[0x110a784],0x0
 *  138126e0   8935 88a71001    mov dword ptr ds:[0x110a788],esi
 *  138126e6   0f84 16000000    je 13812702
 *  138126ec   832d c4aa1001 02 sub dword ptr ds:[0x110aac4],0x2
 *  138126f3   e9 24000000      jmp 1381271c
 *  138126f8   018c46 8408e921  add dword ptr ds:[esi+eax*2+0x21e90884],>
 *  138126ff   d9b4f3 832dc4aa  fstenv (28-byte) ptr ds:[ebx+esi*8+0xaac>
 *  13812706   1001             adc byte ptr ds:[ecx],al
 *  13812708   02c7             add al,bh
 *  1381270a   05 a8aa1001      add eax,0x110aaa8
 *  1381270f   bc 468408e9      mov esp,0xe9088446
 *  13812714   0bd9             or ebx,ecx
 *  13812716   b4 f3            mov ah,0xf3
 *  13812718   90               nop
 *  13812719   cc               int3
 *  1381271a   cc               int3
 *  1381271b   cc               int3
 *
 *  This function is very similar to Imageepoch, and can have duplicate text
 *  138d1486   cc               int3
 *  138d1487   cc               int3
 *  138d1488   77 0f            ja short 138d1499
 *  138d148a   c705 a8aa1001 2c>mov dword ptr ds:[0x110aaa8],0x884452c
 *  138d1494  -e9 6beba8f3      jmp 07360004
 *  138d1499   8b05 7ca71001    mov eax,dword ptr ds:[0x110a77c]
 *  138d149f   81e0 ffffff3f    and eax,0x3fffffff
 *  138d14a5   0fbeb0 00008007  movsx esi,byte ptr ds:[eax+0x7800000] ; jichi: hook here
 *  138d14ac   8b3d 7ca71001    mov edi,dword ptr ds:[0x110a77c]
 *  138d14b2   8d7f 01          lea edi,dword ptr ds:[edi+0x1]
 *  138d14b5   8b05 74a71001    mov eax,dword ptr ds:[0x110a774]
 *  138d14bb   81e0 ffffff3f    and eax,0x3fffffff
 *  138d14c1   8bd6             mov edx,esi
 *  138d14c3   8890 00008007    mov byte ptr ds:[eax+0x7800000],dl
 *  138d14c9   8b2d 74a71001    mov ebp,dword ptr ds:[0x110a774]
 *  138d14cf   8d6d 01          lea ebp,dword ptr ss:[ebp+0x1]
 *  138d14d2   81fe 00000000    cmp esi,0x0
 *  138d14d8   8935 70a71001    mov dword ptr ds:[0x110a770],esi
 *  138d14de   892d 74a71001    mov dword ptr ds:[0x110a774],ebp
 *  138d14e4   893d 7ca71001    mov dword ptr ds:[0x110a77c],edi
 *  138d14ea   0f85 16000000    jnz 138d1506
 *  138d14f0   832d c4aa1001 05 sub dword ptr ds:[0x110aac4],0x5
 *  138d14f7   e9 e8000000      jmp 138d15e4
 *  138d14fc   015445 84        add dword ptr ss:[ebp+eax*2-0x7c],edx
 *  138d1500   08e9             or cl,ch
 *  138d1502   1d eba8f383      sbb eax,0x83f3a8eb
 *  138d1507   2d c4aa1001      sub eax,0x110aac4
 *  138d150c   05 e90e0000      add eax,0xee9
 *  138d1511   0001             add byte ptr ds:[ecx],al
 *  138d1513   40               inc eax
 *  138d1514   45               inc ebp
 *  138d1515   8408             test byte ptr ds:[eax],cl
 *  138d1517  -e9 07eba8f3      jmp 07360023
 *  138d151c   90               nop
 *  138d151d   cc               int3
 *  138d151e   cc               int3
 *  138d151f   cc               int3
 */
//static void SpecialPSPHookYeti(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
//{
//  //enum { base = 0x7400000 };
//  DWORD eax = regof(eax, esp_base);
//  LPCSTR text = LPCSTR(eax + hp->user_value);
//  if (*text) {
//    *data = (DWORD)text;
//    *len = ::strlen(text); // SHIFT-JIS
//    //*split = regof(ecx, esp_base); // ecx is bad that will split text threads
//    //*split = FIXED_SPLIT_VALUE; // Similar to 5pb, it only has one thread?
//    //*split = regof(ebx, esp_base); // value of ebx is splitting
//    *split = FIXED_SPLIT_VALUE << 1; // * 2 to make it unique
//  }
//}

bool InsertYetiPSPHook()
{
  ConsoleOutput("vnreng: Yeti PSP: enter");
  const BYTE bytes[] =  {
    //0xcc,                         // 14e49edb   cc               int3
    0x77, 0x0f,                     // 14e49edc   77 0f            ja short 14e49eed
    0xc7,0x05, XX8,                 // 14e49ede   c705 a8aa1001 98>mov dword ptr ds:[0x110aaa8],0x885ff98
    0xe9, XX4,                      // 14e49ee8  -e9 17619eee      jmp 03830004
    0x8b,0x35, XX4,                 // 14e49eed   8b35 70a71001    mov esi,dword ptr ds:[0x110a770]
    0xc1,0xee, 0x1f,                // 14e49ef3   c1ee 1f          shr esi,0x1f
    0x8b,0x05, XX4,                 // 14e49ef6   8b05 b4a71001    mov eax,dword ptr ds:[0x110a7b4]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 14e49efc   81e0 ffffff3f    and eax,0x3fffffff
    0x8b,0xb8, XX4,                 // 14e49f02   8bb8 14deff07    mov edi,dword ptr ds:[eax+0x7ffde14]
    0x03,0x35, XX4,                 // 14e49f08   0335 70a71001    add esi,dword ptr ds:[0x110a770]
    0xd1,0xfe,                      // 14e49f0e   d1fe             sar esi,1
    0x8b,0x05, XX4,                 // 14e49f10   8b05 b0a71001    mov eax,dword ptr ds:[0x110a7b0]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 14e49f16   81e0 ffffff3f    and eax,0x3fffffff
    0x89,0xb8, XX4,                 // 14e49f1c   89b8 00000008    mov dword ptr ds:[eax+0x8000000],edi
    0x8b,0x05, XX4,                 // 14e49f22   8b05 dca71001    mov eax,dword ptr ds:[0x110a7dc]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 14e49f28   81e0 ffffff3f    and eax,0x3fffffff
    0x89,0xb0, XX4,                 // 14e49f2e   89b0 30000008    mov dword ptr ds:[eax+0x8000030],esi
    0x8b,0x05, XX4,                 // 14e49f34   8b05 b4a71001    mov eax,dword ptr ds:[0x110a7b4]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 14e49f3a   81e0 ffffff3f    and eax,0x3fffffff
    0x8b,0xa8, XX4,                 // 14e49f40   8ba8 14deff07    mov ebp,dword ptr ds:[eax+0x7ffde14]
    0x8b,0xc5,                      // 14e49f46   8bc5             mov eax,ebp
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 14e49f48   81e0 ffffff3f    and eax,0x3fffffff
    0x0f,0xb6,0xb0 //, XX4,         // 14e49f4e   0fb6b0 00000008  movzx esi,byte ptr ds:[eax+0x8000000] ; jichi: hook here
  };
  enum { memory_offset = 3 }; // 14e49f4e   0fb6b0 00000008  movzx esi,byte ptr ds:[eax+0x8000000]
  enum { addr_offset = sizeof(bytes) - memory_offset };

  DWORD addr = SafeMatchBytesInPSPMemory(bytes, sizeof(bytes));
  if (!addr)
    ConsoleOutput("vnreng: Yeti PSP: pattern not found");
  else {
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.user_value = *(DWORD *)(hp.address + memory_offset);
    hp.type = USING_STRING|USING_SPLIT|FIXING_SPLIT|NO_CONTEXT; // Fix the split value to merge all threads
    hp.text_fun = SpecialPSPHook;
    hp.offset = pusha_eax_off - 4;
    ConsoleOutput("vnreng: Yeti PSP: INSERT");
    NewHook(hp, "Yeti PSP");
  }

  ConsoleOutput("vnreng: Yeti PSP: leave");
  return addr;
}

/** 7/19/2014 jichi kid-game.co.jp PSP engine, 0,9.8, 0.9.9
 *  Sample game: Monochrome
 *
 *  Note: sceFontGetCharInfo, sceFontGetCharGlyphImage_Clip also works
 *
 *  Debug method: breakpoint the memory address
 *  There are two matched memory address to the current text
 *
 *  == Second run ==
 *  13973a7b   90               nop
 *  13973a7c   77 0f            ja short 13973a8d
 *  13973a7e   c705 a8aa1001 90>mov dword ptr ds:[0x110aaa8],0x885c290
 *  13973a88  -e9 77c5ecef      jmp 03840004
 *  13973a8d   8b05 90a71001    mov eax,dword ptr ds:[0x110a790]
 *  13973a93   81e0 ffffff3f    and eax,0x3fffffff
 *  13973a99   0fb6b0 00008007  movzx esi,byte ptr ds:[eax+0x7800000]
 *  13973aa0   8b05 78a71001    mov eax,dword ptr ds:[0x110a778]
 *  13973aa6   81e0 ffffff3f    and eax,0x3fffffff
 *  13973aac   0fb6b8 00008007  movzx edi,byte ptr ds:[eax+0x7800000] ; jichi: hook here
 *  13973ab3   81fe 00000000    cmp esi,0x0
 *  13973ab9   c705 8ca71001 00>mov dword ptr ds:[0x110a78c],0x0
 *  13973ac3   893d 9ca71001    mov dword ptr ds:[0x110a79c],edi
 *  13973ac9   8935 a0a71001    mov dword ptr ds:[0x110a7a0],esi
 *  13973acf   0f85 16000000    jnz 13973aeb
 *  13973ad5   832d c4aa1001 04 sub dword ptr ds:[0x110aac4],0x4
 *  13973adc   c705 a8aa1001 d0>mov dword ptr ds:[0x110aaa8],0x885c2d0
 *  13973ae6  -e9 38c5ecef      jmp 03840023
 *  13973aeb   832d c4aa1001 04 sub dword ptr ds:[0x110aac4],0x4
 *  13973af2   e9 0d000000      jmp 13973b04
 *  13973af7   01a0 c28508e9    add dword ptr ds:[eax+0xe90885c2],esp
 *  13973afd   22c5             and al,ch
 *  13973aff   ec               in al,dx                                 ; i/o command
 *  13973b00   ef               out dx,eax                               ; i/o command
 *  13973b01   90               nop
 *  13973b02   cc               int3
 *  13973b03   cc               int3
 *
 *  == First run ==
 *  1087394a   cc               int3
 *  1087394b   cc               int3
 *  1087394c   77 0f            ja short 1087395d
 *  1087394e   c705 a8aa1001 78>mov dword ptr ds:[0x110aaa8],0x885c278
 *  10873958  -e9 a7c6bff2      jmp 03470004
 *  1087395d   8b35 80d0da12    mov esi,dword ptr ds:[0x12dad080]
 *  10873963   8bc6             mov eax,esi
 *  10873965   81e0 ffffff3f    and eax,0x3fffffff
 *  1087396b   8bb8 0000000a    mov edi,dword ptr ds:[eax+0xa000000]
 *  10873971   81ff 00000000    cmp edi,0x0
 *  10873977   c705 70a71001 00>mov dword ptr ds:[0x110a770],0x8db0000
 *  10873981   c705 74a71001 00>mov dword ptr ds:[0x110a774],0x0
 *  1087398b   893d 90a71001    mov dword ptr ds:[0x110a790],edi
 *  10873991   8935 94a71001    mov dword ptr ds:[0x110a794],esi
 *  10873997   c705 98a71001 00>mov dword ptr ds:[0x110a798],0x0
 *  108739a1   0f85 16000000    jnz 108739bd
 *  108739a7   832d c4aa1001 06 sub dword ptr ds:[0x110aac4],0x6
 *  108739ae   e9 75c20100      jmp 1088fc28
 *  108739b3   0148 c3          add dword ptr ds:[eax-0x3d],ecx
 *  108739b6   8508             test dword ptr ds:[eax],ecx
 *  108739b8  -e9 66c6bff2      jmp 03470023
 *  108739bd   832d c4aa1001 06 sub dword ptr ds:[0x110aac4],0x6
 *  108739c4   e9 0b000000      jmp 108739d4
 *  108739c9   0190 c28508e9    add dword ptr ds:[eax+0xe90885c2],edx
 *  108739cf   50               push eax
 *  108739d0   c6               ???                                      ; unknown command
 *  108739d1   bf f290770f      mov edi,0xf7790f2
 *  108739d6   c705 a8aa1001 90>mov dword ptr ds:[0x110aaa8],0x885c290
 *  108739e0  -e9 1fc6bff2      jmp 03470004
 *  108739e5   8b05 90a71001    mov eax,dword ptr ds:[0x110a790]
 *  108739eb   81e0 ffffff3f    and eax,0x3fffffff
 *  108739f1   0fb6b0 0000000a  movzx esi,byte ptr ds:[eax+0xa000000] ; jichi: hook here
 *  108739f8   8b05 78a71001    mov eax,dword ptr ds:[0x110a778]
 *  108739fe   81e0 ffffff3f    and eax,0x3fffffff
 *  10873a04   0fb6b8 0000000a  movzx edi,byte ptr ds:[eax+0xa000000] ; jichi: hook here
 *  10873a0b   81fe 00000000    cmp esi,0x0
 *  10873a11   c705 8ca71001 00>mov dword ptr ds:[0x110a78c],0x0
 *  10873a1b   893d 9ca71001    mov dword ptr ds:[0x110a79c],edi
 *  10873a21   8935 a0a71001    mov dword ptr ds:[0x110a7a0],esi
 *  10873a27   0f85 16000000    jnz 10873a43
 *  10873a2d   832d c4aa1001 04 sub dword ptr ds:[0x110aac4],0x4
 *  10873a34   c705 a8aa1001 d0>mov dword ptr ds:[0x110aaa8],0x885c2d0
 *  10873a3e  -e9 e0c5bff2      jmp 03470023
 *  10873a43   832d c4aa1001 04 sub dword ptr ds:[0x110aac4],0x4
 *  10873a4a   e9 0d000000      jmp 10873a5c
 *  10873a4f   01a0 c28508e9    add dword ptr ds:[eax+0xe90885c2],esp
 *  10873a55   ca c5bf          retf 0xbfc5                              ; far return
 *  10873a58   f2:              prefix repne:                            ; superfluous prefix
 *  10873a59   90               nop
 *  10873a5a   cc               int3
 *  10873a5b   cc               int3
 */
static void SpecialPSPHookKid(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  DWORD eax = regof(eax, esp_base);
  LPCSTR text = LPCSTR(eax + hp->user_value);
  static LPCSTR lasttext; // Prevent reading the same address multiple times
  if (text != lasttext && *text) {
    lasttext = text;
    text = _5pbltrim(text);
    *data = (DWORD)text;
    *len = _5pbstrlen(text);
    *split = regof(ecx, esp_base);
  }
}

bool InsertKidPSPHook()
{
  ConsoleOutput("vnreng: KID PSP: enter");

  const BYTE bytes[] =  {
    //0x90,                           // 13973a7b   90               nop
    0x77, 0x0f,                     // 13973a7c   77 0f            ja short 13973a8d
    0xc7,0x05, XX8,                 // 13973a7e   c705 a8aa1001 90>mov dword ptr ds:[0x110aaa8],0x885c290
    0xe9, XX4,                      // 13973a88  -e9 77c5ecef      jmp 03840004
    0x8b,0x05, XX4,                 // 13973a8d   8b05 90a71001    mov eax,dword ptr ds:[0x110a790]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 13973a93   81e0 ffffff3f    and eax,0x3fffffff
    0x0f,0xb6,0xb0, XX4,            // 13973a99   0fb6b0 00008007  movzx esi,byte ptr ds:[eax+0x7800000]
    0x8b,0x05, XX4,                 // 13973aa0   8b05 78a71001    mov eax,dword ptr ds:[0x110a778]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 13973aa6   81e0 ffffff3f    and eax,0x3fffffff
    0x0f,0xb6,0xb8, XX4,            // 13973aac   0fb6b8 00008007  movzx edi,byte ptr ds:[eax+0x7800000] ; jichi: hook here
    0x81,0xfe, 0x00,0x00,0x00,0x00  // 13973ab3   81fe 00000000    cmp esi,0x0
  };
  enum { memory_offset = 3 }; // 13973aac   0fb6b8 00008007  movzx edi,byte ptr ds:[eax+0x7800000]
  enum { addr_offset = 0x13973aac - 0x13973a7c };

  DWORD addr = SafeMatchBytesInPSPMemory(bytes, sizeof(bytes));
  if (!addr)
    ConsoleOutput("vnreng: KID PSP: pattern not found");
  else {
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.user_value = *(DWORD *)(hp.address + memory_offset);
    hp.text_fun = SpecialPSPHookKid;
    hp.type = USING_STRING|NO_CONTEXT; // no context is needed to get rid of variant retaddr

    //HookParam hp = {};
    //hp.address = addr + addr_offset;
    //hp.user_value = *(DWORD *)(hp.address + memory_offset);
    //hp.type = USING_STRING|USING_SPLIT|NO_CONTEXT; // Fix the split value to merge all threads
    //hp.offset = pusha_eax_off - 4;
    //hp.split = pusha_ecx_off - 4;
    //hp.text_fun = SpecialPSPHook;

    ConsoleOutput("vnreng: KID PSP: INSERT");
    NewHook(hp, "KID PSP");
  }

  ConsoleOutput("vnreng: KID PSP: leave");
  return addr;
}

/** 7/19/2014 jichi CYBERFRONT PSP engine, 0,9.8, 0.9.9
 *  Sample game: 想�かけ�クローストゥ (0.9.9)
 *
 *  Debug method: breakpoint the memory address
 *  There are two matched memory address to the current text
 *
 *  The second is used.
 *  The #1 is missing text.
 *
 *  #1 The text is written word by word
 *
 *  0ed8be86   90               nop
 *  0ed8be87   cc               int3
 *  0ed8be88   77 0f            ja short 0ed8be99
 *  0ed8be8a   c705 c84c1301 dc>mov dword ptr ds:[0x1134cc8],0x88151dc
 *  0ed8be94  -e9 6b41b4f4      jmp 038d0004
 *  0ed8be99   8b35 cc491301    mov esi,dword ptr ds:[0x11349cc]
 *  0ed8be9f   8d76 02          lea esi,dword ptr ds:[esi+0x2]
 *  0ed8bea2   8b3d 94491301    mov edi,dword ptr ds:[0x1134994]
 *  0ed8bea8   8b05 d0491301    mov eax,dword ptr ds:[0x11349d0]
 *  0ed8beae   81e0 ffffff3f    and eax,0x3fffffff
 *  0ed8beb4   8bd7             mov edx,edi
 *  0ed8beb6   8890 00008009    mov byte ptr ds:[eax+0x9800000],dl ; jichi: hook here, write text here
 *  0ed8bebc   8b05 c8491301    mov eax,dword ptr ds:[0x11349c8]
 *  0ed8bec2   81e0 ffffff3f    and eax,0x3fffffff
 *  0ed8bec8   0fb6a8 00008009  movzx ebp,byte ptr ds:[eax+0x9800000]
 *  0ed8becf   8b05 d0491301    mov eax,dword ptr ds:[0x11349d0]
 *  0ed8bed5   81e0 ffffff3f    and eax,0x3fffffff
 *  0ed8bedb   8bd5             mov edx,ebp
 *  0ed8bedd   8890 01008009    mov byte ptr ds:[eax+0x9800001],dl
 *  0ed8bee3   8b15 d0491301    mov edx,dword ptr ds:[0x11349d0]
 *  0ed8bee9   8d52 02          lea edx,dword ptr ds:[edx+0x2]
 *  0ed8beec   892d 90491301    mov dword ptr ds:[0x1134990],ebp
 *  0ed8bef2   8935 cc491301    mov dword ptr ds:[0x11349cc],esi
 *  0ed8bef8   8915 d0491301    mov dword ptr ds:[0x11349d0],edx
 *  0ed8befe   832d e44c1301 06 sub dword ptr ds:[0x1134ce4],0x6
 *  0ed8bf05   e9 0e000000      jmp 0ed8bf18
 *  0ed8bf0a   013451           add dword ptr ds:[ecx+edx*2],esi
 *  0ed8bf0d   8108 e90f41b4    or dword ptr ds:[eax],0xb4410fe9
 *  0ed8bf13   f4               hlt                                      ; privileged command
 *  0ed8bf14   90               nop
 *  0ed8bf15   cc               int3
 *
 *  #2 The text is read
 *
 *  Issue: the text is read multiple times.
 *  Only esp > 0xfff is kept.
 *
 *  0ed8cf13   90               nop
 *  0ed8cf14   77 0f            ja short 0ed8cf25
 *  0ed8cf16   c705 c84c1301 b8>mov dword ptr ds:[0x1134cc8],0x888d1b8
 *  0ed8cf20  -e9 df30b4f4      jmp 038d0004
 *  0ed8cf25   8b05 98491301    mov eax,dword ptr ds:[0x1134998]
 *  0ed8cf2b   81e0 ffffff3f    and eax,0x3fffffff
 *  0ed8cf31   0fb6b0 00008009  movzx esi,byte ptr ds:[eax+0x9800000] ; jichi: hook here
 *  0ed8cf38   81fe 00000000    cmp esi,0x0
 *  0ed8cf3e   8935 90491301    mov dword ptr ds:[0x1134990],esi
 *  0ed8cf44   0f85 2f000000    jnz 0ed8cf79
 *  0ed8cf4a   8b05 9c491301    mov eax,dword ptr ds:[0x113499c]
 *  0ed8cf50   81e0 ffffff3f    and eax,0x3fffffff
 *  0ed8cf56   0fbeb0 00008009  movsx esi,byte ptr ds:[eax+0x9800000]
 *  0ed8cf5d   8935 90491301    mov dword ptr ds:[0x1134990],esi
 *  0ed8cf63   832d e44c1301 03 sub dword ptr ds:[0x1134ce4],0x3
 *  0ed8cf6a   c705 c84c1301 18>mov dword ptr ds:[0x1134cc8],0x888d218
 *  0ed8cf74  -e9 aa30b4f4      jmp 038d0023
 *  0ed8cf79   832d e44c1301 03 sub dword ptr ds:[0x1134ce4],0x3
 *  0ed8cf80   e9 0b000000      jmp 0ed8cf90
 *  0ed8cf85   01c4             add esp,eax
 *  0ed8cf87   d188 08e99430    ror dword ptr ds:[eax+0x3094e908],1
 *  0ed8cf8d   b4 f4            mov ah,0xf4
 *  0ed8cf8f   90               nop
 */

static void SpecialPSPHookCyberfront(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  DWORD splitvalue = regof(edi, esp_base);
  if (splitvalue < 0x0fff)
    return;
  DWORD eax = regof(eax, esp_base);
  LPCSTR text = LPCSTR(eax + hp->user_value);
  if (*text) {
    *data = (DWORD)text;
    *len = ::strlen(text);
    *split = splitvalue;
  }
}
bool InsertCyberfrontPSPHook()
{
  ConsoleOutput("vnreng: CYBERFRONT PSP: enter");

  const BYTE bytes[] =  {
                                    // 0ed8cf13   90               nop
    0x77, 0x0f,                     // 0ed8cf14   77 0f            ja short 0ed8cf25
    0xc7,0x05, XX8,                 // 0ed8cf16   c705 c84c1301 b8>mov dword ptr ds:[0x1134cc8],0x888d1b8
    0xe9, XX4,                      // 0ed8cf20  -e9 df30b4f4      jmp 038d0004
    0x8b,0x05, XX4,                 // 0ed8cf25   8b05 98491301    mov eax,dword ptr ds:[0x1134998]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 0ed8cf2b   81e0 ffffff3f    and eax,0x3fffffff
    0x0f,0xb6,0xb0, XX4,            // 0ed8cf31   0fb6b0 00008009  movzx esi,byte ptr ds:[eax+0x9800000] ; jichi: hook here
    0x81,0xfe, 0x00,0x00,0x00,0x00, // 0ed8cf38   81fe 00000000    cmp esi,0x0
    0x89,0x35, XX4,                 // 0ed8cf3e   8935 90491301    mov dword ptr ds:[0x1134990],esi
    0x0f,0x85, 0x2f,0x00,0x00,0x00, // 0ed8cf44   0f85 2f000000    jnz 0ed8cf79
    0x8b,0x05, XX4,                 // 0ed8cf4a   8b05 9c491301    mov eax,dword ptr ds:[0x113499c]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 0ed8cf50   81e0 ffffff3f    and eax,0x3fffffff
    0x0f,0xbe,0xb0, XX4,            // 0ed8cf56   0fbeb0 00008009  movsx esi,byte ptr ds:[eax+0x9800000]
    0x89,0x35, XX4,                 // 0ed8cf5d   8935 90491301    mov dword ptr ds:[0x1134990],esi
    0x83,0x2d, XX4, 0x03,           // 0ed8cf63   832d e44c1301 03 sub dword ptr ds:[0x1134ce4],0x3
    0xc7,0x05 //, XX8               // 0ed8cf6a   c705 c84c1301 18>mov dword ptr ds:[0x1134cc8],0x888d218
  };
  enum { memory_offset = 3 }; // 13909a51   8890 00008007    mov byte ptr ds:[eax+0x7800000],dl
  enum { addr_offset = 0x0ed8cf31 - 0x0ed8cf14 };

  DWORD addr = SafeMatchBytesInPSPMemory(bytes, sizeof(bytes));
  //GROWL_DWORD(addr);
  if (!addr)
    ConsoleOutput("vnreng: CYBERFRONT PSP: pattern not found");
  else {
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.user_value = *(DWORD *)(hp.address + memory_offset);
    hp.type = USING_STRING|USING_SPLIT|NO_CONTEXT;
    //hp.offset = pusha_eax_off - 4;
    //hp.split = pusha_edi_off - 4;
    hp.text_fun = SpecialPSPHookCyberfront;
    ConsoleOutput("vnreng: CYBERFRONT PSP: INSERT");
    NewHook(hp, "CYBERFRONT PSP");
  }

  ConsoleOutput("vnreng: CYBERFRONT PSP: leave");
  return addr;
}

/** 7/19/2014 jichi Alternative Yeti PSP engine, 0.9.8, 0.9.9
 *  Sample game: Never 7, 0.9.8 & 0.9.9
 *  Sample game: ひまわり
 *
 *  Do not work on 0.9.9 Ever17 (7/27/2014)
 *
 *
 *  This hook does not work for 12River.
 *  However, sceFont functions work.
 *
 *  Memory address is FIXED.
 *  Debug method: breakpoint the memory address
 *  There are two matched memory address to the current text
 *
 *  There are several functions. The first one is used.
 *
 *  The text also has 5pb-like garbage, but it is difficult to trim.
 *
 *  PPSSPP 0.9.8:
 *
 *  14289802   cc               int3
 *  14289803   cc               int3
 *  14289804   77 0f            ja short 14289815
 *  14289806   c705 a8aa1001 58>mov dword ptr ds:[0x110aaa8],0x881ab58
 *  14289810  -e9 ef6767ef      jmp 03900004
 *  14289815   8b35 74a71001    mov esi,dword ptr ds:[0x110a774]
 *  1428981b   0335 78a71001    add esi,dword ptr ds:[0x110a778]
 *  14289821   8b05 dca71001    mov eax,dword ptr ds:[0x110a7dc]
 *  14289827   81e0 ffffff3f    and eax,0x3fffffff
 *  1428982d   8bb8 28004007    mov edi,dword ptr ds:[eax+0x7400028]
 *  14289833   8bc6             mov eax,esi
 *  14289835   81e0 ffffff3f    and eax,0x3fffffff
 *  1428983b   8bd7             mov edx,edi
 *  1428983d   8890 10044007    mov byte ptr ds:[eax+0x7400410],dl
 *  14289843   8b05 b0a71001    mov eax,dword ptr ds:[0x110a7b0]
 *  14289849   81e0 ffffff3f    and eax,0x3fffffff
 *  1428984f   8bb8 84004007    mov edi,dword ptr ds:[eax+0x7400084]
 *  14289855   8b05 aca71001    mov eax,dword ptr ds:[0x110a7ac]
 *  1428985b   81e0 ffffff3f    and eax,0x3fffffff
 *  14289861   0fb6a8 00004007  movzx ebp,byte ptr ds:[eax+0x7400000] ; jichi: hook here
 *  14289868   81ff 00000000    cmp edi,0x0
 *  1428986e   8935 70a71001    mov dword ptr ds:[0x110a770],esi
 *  14289874   893d 74a71001    mov dword ptr ds:[0x110a774],edi
 *  1428987a   892d 78a71001    mov dword ptr ds:[0x110a778],ebp
 *  14289880   0f85 16000000    jnz 1428989c
 *  14289886   832d c4aa1001 06 sub dword ptr ds:[0x110aac4],0x6
 *  1428988d   c705 a8aa1001 ac>mov dword ptr ds:[0x110aaa8],0x881aeac
 *  14289897  -e9 876767ef      jmp 03900023
 *  1428989c   832d c4aa1001 06 sub dword ptr ds:[0x110aac4],0x6
 *  142898a3   e9 0c000000      jmp 142898b4
 *  142898a8   0170 ab          add dword ptr ds:[eax-0x55],esi
 *  142898ab   8108 e9716767    or dword ptr ds:[eax],0x676771e9
 *  142898b1   ef               out dx,eax                               ; i/o command
 *  142898b2   90               nop
 *
 *  142878ed   cc               int3
 *  142878ee   cc               int3
 *  142878ef   cc               int3
 *  142878f0   77 0f            ja short 14287901
 *  142878f2   c705 a8aa1001 44>mov dword ptr ds:[0x110aaa8],0x8811e44
 *  142878fc  -e9 038767ef      jmp 03900004
 *  14287901   8b35 70a71001    mov esi,dword ptr ds:[0x110a770]
 *  14287907   8b05 b0a71001    mov eax,dword ptr ds:[0x110a7b0]
 *  1428790d   81e0 ffffff3f    and eax,0x3fffffff
 *  14287913   8bd6             mov edx,esi
 *  14287915   8890 00004007    mov byte ptr ds:[eax+0x7400000],dl ; jichi: hook here
 *  1428791b   8b05 a8a71001    mov eax,dword ptr ds:[0x110a7a8]
 *  14287921   81e0 ffffff3f    and eax,0x3fffffff
 *  14287927   0fb6b8 00004007  movzx edi,byte ptr ds:[eax+0x7400000]
 *  1428792e   8b2d aca71001    mov ebp,dword ptr ds:[0x110a7ac]
 *  14287934   81c5 02000000    add ebp,0x2
 *  1428793a   8bd5             mov edx,ebp
 *  1428793c   8915 aca71001    mov dword ptr ds:[0x110a7ac],edx
 *  14287942   8b05 b0a71001    mov eax,dword ptr ds:[0x110a7b0]
 *  14287948   81e0 ffffff3f    and eax,0x3fffffff
 *  1428794e   8bd7             mov edx,edi
 *  14287950   8890 01004007    mov byte ptr ds:[eax+0x7400001],dl
 *  14287956   8b15 b0a71001    mov edx,dword ptr ds:[0x110a7b0]
 *  1428795c   8d52 02          lea edx,dword ptr ds:[edx+0x2]
 *  1428795f   893d 74a71001    mov dword ptr ds:[0x110a774],edi
 *  14287965   892d a8a71001    mov dword ptr ds:[0x110a7a8],ebp
 *  1428796b   8915 b0a71001    mov dword ptr ds:[0x110a7b0],edx
 *  14287971   832d c4aa1001 07 sub dword ptr ds:[0x110aac4],0x7
 *  14287978   e9 0b000000      jmp 14287988
 *  1428797d   01a8 1d8108e9    add dword ptr ds:[eax+0xe908811d],ebp
 *  14287983   9c               pushfd
 *  14287984   8667 ef          xchg byte ptr ds:[edi-0x11],ah
 *  14287987   90               nop
 *
 *  14289a2a   90               nop
 *  14289a2b   cc               int3
 *  14289a2c   77 0f            ja short 14289a3d
 *  14289a2e   c705 a8aa1001 b4>mov dword ptr ds:[0x110aaa8],0x881abb4
 *  14289a38  -e9 c76567ef      jmp 03900004
 *  14289a3d   8b05 dca71001    mov eax,dword ptr ds:[0x110a7dc]
 *  14289a43   81e0 ffffff3f    and eax,0x3fffffff
 *  14289a49   8bb0 18004007    mov esi,dword ptr ds:[eax+0x7400018]
 *  14289a4f   8b05 dca71001    mov eax,dword ptr ds:[0x110a7dc]
 *  14289a55   81e0 ffffff3f    and eax,0x3fffffff
 *  14289a5b   8bb8 24004007    mov edi,dword ptr ds:[eax+0x7400024]
 *  14289a61   8b2d 70a71001    mov ebp,dword ptr ds:[0x110a770]
 *  14289a67   03ee             add ebp,esi
 *  14289a69   8b05 dca71001    mov eax,dword ptr ds:[0x110a7dc]
 *  14289a6f   81e0 ffffff3f    and eax,0x3fffffff
 *  14289a75   8bb0 20004007    mov esi,dword ptr ds:[eax+0x7400020]
 *  14289a7b   8bc5             mov eax,ebp
 *  14289a7d   81e0 ffffff3f    and eax,0x3fffffff
 *  14289a83   66:89b8 c2034007 mov word ptr ds:[eax+0x74003c2],di
 *  14289a8a   8bc5             mov eax,ebp
 *  14289a8c   81e0 ffffff3f    and eax,0x3fffffff
 *  14289a92   66:89b0 c0034007 mov word ptr ds:[eax+0x74003c0],si
 *  14289a99   8b05 aca71001    mov eax,dword ptr ds:[0x110a7ac]
 *  14289a9f   81e0 ffffff3f    and eax,0x3fffffff
 *  14289aa5   0fb6b0 00004007  movzx esi,byte ptr ds:[eax+0x7400000] ; jichi: hook here
 *  14289aac   81e6 ff000000    and esi,0xff
 *  14289ab2   892d 70a71001    mov dword ptr ds:[0x110a770],ebp
 *  14289ab8   893d 74a71001    mov dword ptr ds:[0x110a774],edi
 *  14289abe   8935 78a71001    mov dword ptr ds:[0x110a778],esi
 *  14289ac4   c705 e4a71001 d8>mov dword ptr ds:[0x110a7e4],0x881abd8
 *  14289ace   832d c4aa1001 09 sub dword ptr ds:[0x110aac4],0x9
 *  14289ad5  ^e9 d6c6f8ff      jmp 142161b0
 *  14289ada   90               nop
 *
 *  14289adb   cc               int3
 *  14289adc   77 0f            ja short 14289aed
 *  14289ade   c705 a8aa1001 d8>mov dword ptr ds:[0x110aaa8],0x881abd8
 *  14289ae8  -e9 176567ef      jmp 03900004
 *  14289aed   813d 70a71001 00>cmp dword ptr ds:[0x110a770],0x0
 *  14289af7   0f85 2f000000    jnz 14289b2c
 *  14289afd   8b05 aca71001    mov eax,dword ptr ds:[0x110a7ac]
 *  14289b03   81e0 ffffff3f    and eax,0x3fffffff
 *  14289b09   0fb6b0 00004007  movzx esi,byte ptr ds:[eax+0x7400000] ; jichi: hook here
 *  14289b10   8935 70a71001    mov dword ptr ds:[0x110a770],esi
 *  14289b16   832d c4aa1001 02 sub dword ptr ds:[0x110aac4],0x2
 *  14289b1d   e9 22000000      jmp 14289b44
 *  14289b22   0110             add dword ptr ds:[eax],edx
 *  14289b24   af               scas dword ptr es:[edi]
 *  14289b25   8108 e9f76467    or dword ptr ds:[eax],0x6764f7e9
 *  14289b2b   ef               out dx,eax                               ; i/o command
 *  14289b2c   832d c4aa1001 02 sub dword ptr ds:[0x110aac4],0x2
 *  14289b33   c705 a8aa1001 e0>mov dword ptr ds:[0x110aaa8],0x881abe0
 *  14289b3d  -e9 e16467ef      jmp 03900023
 *
 *  PPSSPP 0.9.9 (7/27/2014)
 *
 *  0ed85942   cc               int3
 *  0ed85943   cc               int3
 *  0ed85944   77 0f            ja short 0ed85955
 *  0ed85946   c705 c84c1301 58>mov dword ptr ds:[0x1134cc8],0x881ab58
 *  0ed85950  -e9 afa6aef4      jmp 03870004
 *  0ed85955   8b35 94491301    mov esi,dword ptr ds:[0x1134994]
 *  0ed8595b   0335 98491301    add esi,dword ptr ds:[0x1134998]
 *  0ed85961   8b05 fc491301    mov eax,dword ptr ds:[0x11349fc]
 *  0ed85967   81e0 ffffff3f    and eax,0x3fffffff
 *  0ed8596d   8bb8 28008009    mov edi,dword ptr ds:[eax+0x9800028]
 *  0ed85973   8bc6             mov eax,esi
 *  0ed85975   81e0 ffffff3f    and eax,0x3fffffff
 *  0ed8597b   8bd7             mov edx,edi
 *  0ed8597d   8890 10048009    mov byte ptr ds:[eax+0x9800410],dl
 *  0ed85983   8b05 d0491301    mov eax,dword ptr ds:[0x11349d0]
 *  0ed85989   81e0 ffffff3f    and eax,0x3fffffff
 *  0ed8598f   8bb8 84008009    mov edi,dword ptr ds:[eax+0x9800084]
 *  0ed85995   8b05 cc491301    mov eax,dword ptr ds:[0x11349cc]
 *  0ed8599b   81e0 ffffff3f    and eax,0x3fffffff
 *  0ed859a1   0fb6a8 00008009  movzx ebp,byte ptr ds:[eax+0x9800000] ; jichi: hook here
 *  0ed859a8   81ff 00000000    cmp edi,0x0
 *  0ed859ae   8935 90491301    mov dword ptr ds:[0x1134990],esi
 *  0ed859b4   893d 94491301    mov dword ptr ds:[0x1134994],edi
 *  0ed859ba   892d 98491301    mov dword ptr ds:[0x1134998],ebp
 *  0ed859c0   0f85 16000000    jnz 0ed859dc
 *  0ed859c6   832d e44c1301 06 sub dword ptr ds:[0x1134ce4],0x6
 *  0ed859cd   c705 c84c1301 ac>mov dword ptr ds:[0x1134cc8],0x881aeac
 *  0ed859d7  -e9 47a6aef4      jmp 03870023
 *  0ed859dc   832d e44c1301 06 sub dword ptr ds:[0x1134ce4],0x6
 *  0ed859e3   e9 0c000000      jmp 0ed859f4
 *  0ed859e8   0170 ab          add dword ptr ds:[eax-0x55],esi
 *  0ed859eb   8108 e931a6ae    or dword ptr ds:[eax],0xaea631e9
 *  0ed859f1   f4               hlt                                      ; privileged command
 *  0ed859f2   90               nop
 */
// TODO: Is reverse_strlen a better choice?
static void SpecialPSPHookYeti2(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  DWORD eax = regof(eax, esp_base);
  LPCSTR text = LPCSTR(eax + hp->user_value);
  if (BYTE c = *(BYTE *)text) {
    *data = (DWORD)text;
    //*len = text[1] ? 2 : 1;
    *len = ::LeadByteTable[c];

    *split = regof(edx, esp_base);
    //DWORD ecx = regof(ecx, esp_base);
    //*split = ecx ? (FIXED_SPLIT_VALUE << 1) : 0; // << 1 to be unique, non-zero ecx is what I want
  }
}

bool InsertYeti2PSPHook()
{
  ConsoleOutput("vnreng: Yeti2 PSP: enter");

  const BYTE bytes[] =  {
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 14289827   81e0 ffffff3f    and eax,0x3fffffff
    0x8b,0xb8, XX4,                 // 1428982d   8bb8 28004007    mov edi,dword ptr ds:[eax+0x7400028]
    0x8b,0xc6,                      // 14289833   8bc6             mov eax,esi
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 14289835   81e0 ffffff3f    and eax,0x3fffffff
    0x8b,0xd7,                      // 1428983b   8bd7             mov edx,edi
    0x88,0x90, XX4,                 // 1428983d   8890 10044007    mov byte ptr ds:[eax+0x7400410],dl
    0x8b,0x05, XX4,                 // 14289843   8b05 b0a71001    mov eax,dword ptr ds:[0x110a7b0]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 14289849   81e0 ffffff3f    and eax,0x3fffffff
    0x8b,0xb8, XX4,                 // 1428984f   8bb8 84004007    mov edi,dword ptr ds:[eax+0x7400084]
    0x8b,0x05, XX4,                 // 14289855   8b05 aca71001    mov eax,dword ptr ds:[0x110a7ac]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 1428985b   81e0 ffffff3f    and eax,0x3fffffff
    0x0f,0xb6,0xa8 //, XX4          // 14289861   0fb6a8 00004007  movzx ebp,byte ptr ds:[eax+0x7400000] ; jichi: hook here
                                    // 14289b10   8935 70a71001    mov dword ptr ds:[0x110a770],esi
                                    // 14289b16   832d c4aa1001 02 sub dword ptr ds:[0x110aac4],0x2
  };
  enum { memory_offset = 3 };
  enum { addr_offset = sizeof(bytes) - memory_offset };
  //enum { addr_offset = sizeof(bytes) + 4 }; // point to next statement after ebp is assigned

  DWORD addr = SafeMatchBytesInPSPMemory(bytes, sizeof(bytes));
  if (!addr)
    ConsoleOutput("vnreng: Yeti2 PSP: pattern not found");
  else {
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.user_value = *(DWORD *)(hp.address + memory_offset);
    hp.type = USING_STRING|NO_CONTEXT;
    //hp.offset = pusha_eax_off - 4;
    //hp.split = pusha_ecx_off - 4; // this would split scenario thread
    //hp.split = hp.offset; // directly use text address to split
    hp.text_fun = SpecialPSPHookYeti2;
    ConsoleOutput("vnreng: Yeti2 PSP: INSERT");
    NewHook(hp, "Yeti2 PSP");
  }

  ConsoleOutput("vnreng: Yeti2 PSP: leave");
  return addr;
}

/** 7/22/2014 jichi BANDAI PSP engine, 0.9.8 only
 *  Replaced by Otomate PPSSPP on 0.9.9.
 *  Sample game: School Rumble PSP 姉さん事件で�(SHIFT-JIS)
 *  See: http://sakuradite.com/topic/333
 *
 *  Sample game: 寮�のサクリファイス work on 0.9.8, not 0.9.9
 *
 *
 *  Sample game: Shining Hearts (UTF-8)
 *  See: http://sakuradite.com/topic/346
 *
 *  The encoding could be either UTF-8 or SHIFT-JIS
 *
 *  Debug method: breakpoint the memory address
 *  There are two matched memory address to the current text
 *
 *  Only one function is accessing the text address.
 *
 *  Character name:
 *
 *  1346c122   cc               int3
 *  1346c123   cc               int3
 *  1346c124   77 0f            ja short 1346c135
 *  1346c126   c705 a8aa1001 a4>mov dword ptr ds:[0x110aaa8],0x882f2a4
 *  1346c130  -e9 cf3e2cf0      jmp 03730004
 *  1346c135   8b05 a8a71001    mov eax,dword ptr ds:[0x110a7a8]
 *  1346c13b   81e0 ffffff3f    and eax,0x3fffffff
 *  1346c141   8bb0 14004007    mov esi,dword ptr ds:[eax+0x7400014]
 *  1346c147   8b3d 70a71001    mov edi,dword ptr ds:[0x110a770]
 *  1346c14d   c1e7 02          shl edi,0x2
 *  1346c150   8b05 a8a71001    mov eax,dword ptr ds:[0x110a7a8]
 *  1346c156   81e0 ffffff3f    and eax,0x3fffffff
 *  1346c15c   8ba8 18004007    mov ebp,dword ptr ds:[eax+0x7400018]
 *  1346c162   03fe             add edi,esi
 *  1346c164   8bc7             mov eax,edi
 *  1346c166   81e0 ffffff3f    and eax,0x3fffffff
 *  1346c16c   0fb790 02004007  movzx edx,word ptr ds:[eax+0x7400002]
 *  1346c173   8bc2             mov eax,edx
 *  1346c175   8bd5             mov edx,ebp
 *  1346c177   03d0             add edx,eax
 *  1346c179   8bc2             mov eax,edx
 *  1346c17b   81e0 ffffff3f    and eax,0x3fffffff
 *  1346c181   0fb6b8 00004007  movzx edi,byte ptr ds:[eax+0x7400000] ; jichi: hook here
 *  1346c188   8bcf             mov ecx,edi
 *  1346c18a   81e7 ff000000    and edi,0xff
 *  1346c190   8935 74a71001    mov dword ptr ds:[0x110a774],esi
 *  1346c196   8b35 b8a71001    mov esi,dword ptr ds:[0x110a7b8]
 *  1346c19c   81c6 bc82ffff    add esi,0xffff82bc
 *  1346c1a2   81ff 00000000    cmp edi,0x0
 *  1346c1a8   893d 70a71001    mov dword ptr ds:[0x110a770],edi
 *  1346c1ae   8915 78a71001    mov dword ptr ds:[0x110a778],edx
 *  1346c1b4   892d 7ca71001    mov dword ptr ds:[0x110a77c],ebp
 *  1346c1ba   890d 80a71001    mov dword ptr ds:[0x110a780],ecx
 *  1346c1c0   8935 84a71001    mov dword ptr ds:[0x110a784],esi
 *  1346c1c6   0f85 16000000    jnz 1346c1e2
 *  1346c1cc   832d c4aa1001 0b sub dword ptr ds:[0x110aac4],0xb
 *  1346c1d3   e9 3c050000      jmp 1346c714
 *  1346c1d8   014cf3 82        add dword ptr ds:[ebx+esi*8-0x7e],ecx
 *  1346c1dc   08e9             or cl,ch
 *  1346c1de   41               inc ecx
 *  1346c1df   3e:2c f0         sub al,0xf0                              ; superfluous prefix
 *  1346c1e2   832d c4aa1001 0b sub dword ptr ds:[0x110aac4],0xb
 *  1346c1e9   e9 0e000000      jmp 1346c1fc
 *  1346c1ee   01d0             add eax,edx
 *  1346c1f0   f2:              prefix repne:                            ; superfluous prefix
 *  1346c1f1   8208 e9          or byte ptr ds:[eax],0xffffffe9
 *  1346c1f4   2b3e             sub edi,dword ptr ds:[esi]
 *  1346c1f6   2c f0            sub al,0xf0
 *  1346c1f8   90               nop
 *  1346c1f9   cc               int3
 *  1346c1fa   cc               int3
 *  1346c1fb   cc               int3
 *
 *  Scenario:
 *
 *  1340055d   cc               int3
 *  1340055e   cc               int3
 *  1340055f   cc               int3
 *  13400560   77 0f            ja short 13400571
 *  13400562   c705 a8aa1001 cc>mov dword ptr ds:[0x110aaa8],0x883decc
 *  1340056c  -e9 93fa54f0      jmp 03950004
 *  13400571   8b35 78a71001    mov esi,dword ptr ds:[0x110a778]
 *  13400577   81c6 01000000    add esi,0x1
 *  1340057d   8b05 78a71001    mov eax,dword ptr ds:[0x110a778]
 *  13400583   81e0 ffffff3f    and eax,0x3fffffff
 *  13400589   0fb6b8 00004007  movzx edi,byte ptr ds:[eax+0x7400000] ; jichi: hook here
 *  13400590   8b2d 78a71001    mov ebp,dword ptr ds:[0x110a778]
 *  13400596   8d6d 01          lea ebp,dword ptr ss:[ebp+0x1]
 *  13400599   81ff 00000000    cmp edi,0x0
 *  1340059f   8935 70a71001    mov dword ptr ds:[0x110a770],esi
 *  134005a5   893d 74a71001    mov dword ptr ds:[0x110a774],edi
 *  134005ab   892d 78a71001    mov dword ptr ds:[0x110a778],ebp
 *  134005b1   0f84 16000000    je 134005cd
 *  134005b7   832d c4aa1001 04 sub dword ptr ds:[0x110aac4],0x4
 *  134005be   e9 21000000      jmp 134005e4
 *  134005c3   01d0             add eax,edx
 *  134005c5   de83 08e956fa    fiadd word ptr ds:[ebx+0xfa56e908]
 *  134005cb   54               push esp
 *  134005cc   f0:832d c4aa1001>lock sub dword ptr ds:[0x110aac4],0x4    ; lock prefix
 *  134005d4   e9 7f000000      jmp 13400658
 *  134005d9   01dc             add esp,ebx
 *  134005db   de83 08e940fa    fiadd word ptr ds:[ebx+0xfa40e908]
 *  134005e1   54               push esp
 *  134005e2   f0:90            lock nop                                 ; lock prefix is not allowed
 *  134005e4   77 0f            ja short 134005f5
 *  134005e6   c705 a8aa1001 d0>mov dword ptr ds:[0x110aaa8],0x883ded0
 *  134005f0  -e9 0ffa54f0      jmp 03950004
 *  134005f5   8b05 78a71001    mov eax,dword ptr ds:[0x110a778]
 *  134005fb   81e0 ffffff3f    and eax,0x3fffffff
 *  13400601   0fb6b0 00004007  movzx esi,byte ptr ds:[eax+0x7400000]
 *  13400608   8b3d 78a71001    mov edi,dword ptr ds:[0x110a778]
 *  1340060e   8d7f 01          lea edi,dword ptr ds:[edi+0x1]
 *  13400611   81fe 00000000    cmp esi,0x0
 *  13400617   8935 74a71001    mov dword ptr ds:[0x110a774],esi
 *  1340061d   893d 78a71001    mov dword ptr ds:[0x110a778],edi
 *  13400623   0f84 16000000    je 1340063f
 *  13400629   832d c4aa1001 03 sub dword ptr ds:[0x110aac4],0x3
 *  13400630  ^e9 afffffff      jmp 134005e4
 *  13400635   01d0             add eax,edx
 *  13400637   de83 08e9e4f9    fiadd word ptr ds:[ebx+0xf9e4e908]
 *  1340063d   54               push esp
 *  1340063e   f0:832d c4aa1001>lock sub dword ptr ds:[0x110aac4],0x3    ; lock prefix
 *  13400646   e9 0d000000      jmp 13400658
 *  1340064b   01dc             add esp,ebx
 *  1340064d   de83 08e9cef9    fiadd word ptr ds:[ebx+0xf9cee908]
 *  13400653   54               push esp
 *  13400654   f0:90            lock nop                                 ; lock prefix is not allowed
 *  13400656   cc               int3
 *  13400657   cc               int3
 */
bool InsertBandaiNamePSPHook()
{
  ConsoleOutput("vnreng: BANDAI Name PSP: enter");

  const BYTE bytes[] =  {
    //0xcc,                         // 1346c122   cc               int3
    //0xcc,                         // 1346c123   cc               int3
    0x77, 0x0f,                     // 1346c124   77 0f            ja short 1346c135
    0xc7,0x05, XX8,                 // 1346c126   c705 a8aa1001 a4>mov dword ptr ds:[0x110aaa8],0x882f2a4
    0xe9, XX4,                      // 1346c130  -e9 cf3e2cf0      jmp 03730004
    0x8b,0x05, XX4,                 // 1346c135   8b05 a8a71001    mov eax,dword ptr ds:[0x110a7a8]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 1346c13b   81e0 ffffff3f    and eax,0x3fffffff
    0x8b,0xb0, XX4,                 // 1346c141   8bb0 14004007    mov esi,dword ptr ds:[eax+0x7400014]
    0x8b,0x3d, XX4,                 // 1346c147   8b3d 70a71001    mov edi,dword ptr ds:[0x110a770]
    0xc1,0xe7, 0x02,                // 1346c14d   c1e7 02          shl edi,0x2
    0x8b,0x05, XX4,                 // 1346c150   8b05 a8a71001    mov eax,dword ptr ds:[0x110a7a8]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 1346c156   81e0 ffffff3f    and eax,0x3fffffff
    0x8b,0xa8, XX4,                 // 1346c15c   8ba8 18004007    mov ebp,dword ptr ds:[eax+0x7400018]
    0x03,0xfe,                      // 1346c162   03fe             add edi,esi
    0x8b,0xc7,                      // 1346c164   8bc7             mov eax,edi
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 1346c166   81e0 ffffff3f    and eax,0x3fffffff
    0x0f,0xb7,0x90, XX4,            // 1346c16c   0fb790 02004007  movzx edx,word ptr ds:[eax+0x7400002]
    0x8b,0xc2,                      // 1346c173   8bc2             mov eax,edx
    0x8b,0xd5,                      // 1346c175   8bd5             mov edx,ebp
    0x03,0xd0,                      // 1346c177   03d0             add edx,eax
    0x8b,0xc2,                      // 1346c179   8bc2             mov eax,edx
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 1346c17b   81e0 ffffff3f    and eax,0x3fffffff
    0x0f,0xb6,0xb8 //, XX4          // 1346c181   0fb6b8 00004007  movzx edi,byte ptr ds:[eax+0x7400000] ; jichi: hook here
  };
  enum { memory_offset = 3 };  // 1346c181   0fb6b8 00004007  movzx edi,byte ptr ds:[eax+0x7400000]
  enum { addr_offset = sizeof(bytes) - memory_offset };

  DWORD addr = SafeMatchBytesInPSPMemory(bytes, sizeof(bytes));
  if (!addr)
    ConsoleOutput("vnreng: BANDAI Name PSP: pattern not found");
  else {
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.user_value = *(DWORD *)(hp.address + memory_offset);
    hp.type = USING_STRING|USING_SPLIT|NO_CONTEXT;
    hp.offset = pusha_eax_off - 4;
    hp.split = pusha_ebx_off - 4;
    hp.text_fun = SpecialPSPHook;
    ConsoleOutput("vnreng: BANDAI Name PSP: INSERT");
    NewHook(hp, "BANDAI Name PSP");
  }

  ConsoleOutput("vnreng: BANDAI Name PSP: leave");
  return addr;
}

namespace { // unnamed

inline bool _bandaigarbage_ch(char c)
{
  return c == ' ' || c == '/' || c == '#' || c == '.' || c == ':'
      || c >= '0' && c <= '9'
      || c >= 'A' && c <= 'z'; // also ignore ASCII 91-96: [ \ ] ^ _ `
}

// Remove trailing /L/P or #n garbage
size_t _bandaistrlen(LPCSTR text)
{
  size_t len = ::strlen(text);
  size_t ret = len;
  while (len && _bandaigarbage_ch(text[len - 1])) {
    len--;
    if (text[len] == '/' || text[len] == '#') // in case trim UTF-8 trailing bytes
      ret = len;
  }
  return ret;
}

// Trim leading garbage
LPCSTR _bandailtrim(LPCSTR p)
{
  enum { MAX_LENGTH = VNR_TEXT_CAPACITY };
  if (p)
    for (int count = 0; *p && count < MAX_LENGTH; count++, p++)
      if (!_bandaigarbage_ch(*p))
        return p;
  return nullptr;
}
} // unnamed namespae

static void SpecialPSPHookBandai(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  DWORD eax = regof(eax, esp_base);
  LPCSTR text = LPCSTR(eax + hp->user_value);

  if (*text) {
    //lasttext = text;
    text = _bandailtrim(text);
    *data = (DWORD)text;
    *len = _bandaistrlen(text);

    // Issue: The split value will create lots of threads for Shining Hearts
    //*split = regof(ecx, esp_base); // works for Shool Rumble, but mix character name for Shining Hearts
    *split = regof(edi, esp_base); // works for Shining Hearts to split character name
  }
}

// 7/22/2014 jichi: This engine works for multiple game?
// It is also observed in Broccoli game ぁ�の�リンスさまっ.
bool InsertBandaiPSPHook()
{
  ConsoleOutput("vnreng: BANDAI PSP: enter");

  const BYTE bytes[] =  {
    0x77, 0x0f,                     // 13400560   77 0f            ja short 13400571
    0xc7,0x05, XX8,                 // 13400562   c705 a8aa1001 cc>mov dword ptr ds:[0x110aaa8],0x883decc
    0xe9, XX4,                      // 1340056c  -e9 93fa54f0      jmp 03950004
    0x8b,0x35, XX4,                 // 13400571   8b35 78a71001    mov esi,dword ptr ds:[0x110a778]
    0x81,0xc6, 0x01,0x00,0x00,0x00, // 13400577   81c6 01000000    add esi,0x1
    0x8b,0x05, XX4,                 // 1340057d   8b05 78a71001    mov eax,dword ptr ds:[0x110a778]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 13400583   81e0 ffffff3f    and eax,0x3fffffff
    0x0f,0xb6,0xb8, XX4,            // 13400589   0fb6b8 00004007  movzx edi,byte ptr ds:[eax+0x7400000] ; jichi: hook here
    0x8b,0x2d, XX4,                 // 13400590   8b2d 78a71001    mov ebp,dword ptr ds:[0x110a778]
    0x8d,0x6d, 0x01,                // 13400596   8d6d 01          lea ebp,dword ptr ss:[ebp+0x1]
    0x81,0xff, 0x00,0x00,0x00,0x00  // 13400599   81ff 00000000    cmp edi,0x0
  };
  enum { memory_offset = 3 };  // 13400589   0fb6b8 00004007  movzx edi,byte ptr ds:[eax+0x7400000]
  enum { addr_offset = 0x13400589 - 0x13400560 };

  DWORD addr = SafeMatchBytesInPSPMemory(bytes, sizeof(bytes));
  if (!addr)
    ConsoleOutput("vnreng: BANDAI PSP: pattern not found");
  else {
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.user_value = *(DWORD *)(hp.address + memory_offset);
    hp.type = USING_STRING|USING_SPLIT|NO_CONTEXT;
    //hp.offset = pusha_eax_off - 4;
    //hp.split = pusha_ecx_off - 4;
    hp.text_fun = SpecialPSPHookBandai;
    ConsoleOutput("vnreng: BANDAI PSP: INSERT");
    NewHook(hp, "BANDAI PSP");
  }

  ConsoleOutput("vnreng: BANDAI PSP: leave");
  return addr;
}

/** 7/22/2014 jichi: Nippon1 PSP engine, 0.9.8 only
 *  Sample game: ぁ�の�リンスさまっ♪  (0.9.8 only)
 *
 *  Memory address is FIXED.
 *  Debug method: breakpoint the precomputed address
 *
 *  The data is in (WORD)bp instead of eax.
 *  bp contains SHIFT-JIS BIG_ENDIAN data.
 *
 *  There is only one text thread.
 *
 *  134e0553   cc               int3
 *  134e0554   77 0f            ja short 134e0565
 *  134e0556   c705 a8aa1001 34>mov dword ptr ds:[0x110aaa8],0x8853a34
 *  134e0560  -e9 9ffa03f0      jmp 03520004
 *  134e0565   8b35 74a71001    mov esi,dword ptr ds:[0x110a774]
 *  134e056b   d1e6             shl esi,1
 *  134e056d   c7c7 987db708    mov edi,0x8b77d98
 *  134e0573   03fe             add edi,esi
 *  134e0575   8b2d 78a71001    mov ebp,dword ptr ds:[0x110a778]
 *  134e057b   8bc7             mov eax,edi
 *  134e057d   81e0 ffffff3f    and eax,0x3fffffff
 *  134e0583   66:89a8 00004007 mov word ptr ds:[eax+0x7400000],bp ; jichi: hook here
 *  134e058a   8b2d 8c7df70f    mov ebp,dword ptr ds:[0xff77d8c]
 *  134e0590   8d6d 01          lea ebp,dword ptr ss:[ebp+0x1]
 *  134e0593   892d 8c7df70f    mov dword ptr ds:[0xff77d8c],ebp
 *  134e0599   8b05 e4a71001    mov eax,dword ptr ds:[0x110a7e4]
 *  134e059f   c705 74a71001 00>mov dword ptr ds:[0x110a774],0x8b70000
 *  134e05a9   892d 78a71001    mov dword ptr ds:[0x110a778],ebp
 *  134e05af   8935 7ca71001    mov dword ptr ds:[0x110a77c],esi
 *  134e05b5   8905 a8aa1001    mov dword ptr ds:[0x110aaa8],eax
 *  134e05bb   832d c4aa1001 0c sub dword ptr ds:[0x110aac4],0xc
 *  134e05c2  -e9 5cfa03f0      jmp 03520023
 */
// Read text from bp
// TODO: This should be expressed as general hook without extern fun
static void SpecialPSPHookNippon1(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  //LPCSTR text = LPCSTR(esp_base + pusha_ebp_off - 4); // ebp address
  LPCSTR text = LPCSTR(esp_base + hp->offset); // dynamic offset, ebp or esi
  if (*text) {
    *data = (DWORD)text;
    *len = !text[0] ? 0 : !text[1] ? 1 : 2; // bp or si has at most two bytes
    //*len = ::LeadByteTable[*(BYTE *)text] // TODO: Test leadbytetable
    *split = regof(ecx, esp_base);
  }
}

bool InsertNippon1PSPHook()
{
  ConsoleOutput("vnreng: Nippon1 PSP: enter");

  const BYTE bytes[] =  {
    //0xcc,                         // 134e0553   cc               int3
    0x77, 0x0f,                     // 134e0554   77 0f            ja short 134e0565
    0xc7,0x05, XX8,                 // 134e0556   c705 a8aa1001 34>mov dword ptr ds:[0x110aaa8],0x8853a34
    0xe9, XX4,                      // 134e0560  -e9 9ffa03f0      jmp 03520004
    0x8b,0x35, XX4,                 // 134e0565   8b35 74a71001    mov esi,dword ptr ds:[0x110a774]
    0xd1,0xe6,                      // 134e056b   d1e6             shl esi,1
    0xc7,0xc7, XX4,                 // 134e056d   c7c7 987db708    mov edi,0x8b77d98
    0x03,0xfe,                      // 134e0573   03fe             add edi,esi
    0x8b,0x2d, XX4,                 // 134e0575   8b2d 78a71001    mov ebp,dword ptr ds:[0x110a778]
    0x8b,0xc7,                      // 134e057b   8bc7             mov eax,edi
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 134e057d   81e0 ffffff3f    and eax,0x3fffffff
    0x66,0x89,0xa8, XX4,            // 134e0583   66:89a8 00004007 mov word ptr ds:[eax+0x7400000],bp ; jichi: hook here
    0x8b,0x2d, XX4,                 // 134e058a   8b2d 8c7df70f    mov ebp,dword ptr ds:[0xff77d8c]
    0x8d,0x6d, 0x01                 // 134e0590   8d6d 01          lea ebp,dword ptr ss:[ebp+0x1]
  };
  enum { memory_offset = 3 };
  enum { addr_offset = 0x134e0583 - 0x134e0554 };

  DWORD addr = SafeMatchBytesInPSPMemory(bytes, sizeof(bytes));
  if (!addr)
    ConsoleOutput("vnreng: Nippon1 PSP: pattern not found");
  else {
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.offset = pusha_ebp_off - 4; // ebp
    hp.type = USING_STRING|NO_CONTEXT;
    hp.text_fun = SpecialPSPHookNippon1;
    ConsoleOutput("vnreng: Nippon1 PSP: INSERT");
    NewHook(hp, "Nippon1 PSP");
  }

  ConsoleOutput("vnreng: Nippon1 PSP: leave");
  return addr;
}

/** 7/26/2014 jichi: Alternative Nippon1 PSP engine, 0.9.8 only
 *  Sample game: 神�悪戯 (0.9.8 only)
 *  Issue: character name cannot be extracted
 *
 *  Memory address is FIXED.
 *  Debug method: breakpoint the precomputed address
 *
 *  This function is the one that write the text into the memory.
 *
 *  13d13e8b   0f92c0           setb al
 *  13d13e8e   8bf8             mov edi,eax
 *  13d13e90   81ff 00000000    cmp edi,0x0
 *  13d13e96   893d 78a71001    mov dword ptr ds:[0x110a778],edi
 *  13d13e9c   8935 dca71001    mov dword ptr ds:[0x110a7dc],esi
 *  13d13ea2   0f85 16000000    jnz 13d13ebe
 *  13d13ea8   832d c4aa1001 0a sub dword ptr ds:[0x110aac4],0xa
 *  13d13eaf   c705 a8aa1001 cc>mov dword ptr ds:[0x110aaa8],0x887c2cc
 *  13d13eb9  -e9 65c1a3ef      jmp 03750023
 *  13d13ebe   832d c4aa1001 0a sub dword ptr ds:[0x110aac4],0xa
 *  13d13ec5   e9 0e000000      jmp 13d13ed8
 *  13d13eca   01a8 c28708e9    add dword ptr ds:[eax+0xe90887c2],ebp
 *  13d13ed0   4f               dec edi
 *  13d13ed1   c1a3 ef90cccc cc shl dword ptr ds:[ebx+0xcccc90ef],0xcc   ; shift constant out of range 1..31
 *  13d13ed8   77 0f            ja short 13d13ee9
 *  13d13eda   c705 a8aa1001 a8>mov dword ptr ds:[0x110aaa8],0x887c2a8
 *  13d13ee4  -e9 1bc1a3ef      jmp 03750004
 *  13d13ee9   8b05 dca71001    mov eax,dword ptr ds:[0x110a7dc]
 *  13d13eef   81e0 ffffff3f    and eax,0x3fffffff
 *  13d13ef5   0fb7b0 0000c007  movzx esi,word ptr ds:[eax+0x7c00000]
 *  13d13efc   8b3d fccd5a10    mov edi,dword ptr ds:[0x105acdfc]
 *  13d13f02   8bef             mov ebp,edi
 *  13d13f04   d1e5             shl ebp,1
 *  13d13f06   81c5 e8cd9a08    add ebp,0x89acde8
 *  13d13f0c   8bc5             mov eax,ebp
 *  13d13f0e   81e0 ffffff3f    and eax,0x3fffffff
 *  13d13f14   66:89b0 2000c007 mov word ptr ds:[eax+0x7c00020],si ; jichi: hook here
 *  13d13f1b   8d7f 01          lea edi,dword ptr ds:[edi+0x1]
 *  13d13f1e   893d fccd5a10    mov dword ptr ds:[0x105acdfc],edi
 *  13d13f24   8b15 dca71001    mov edx,dword ptr ds:[0x110a7dc]
 *  13d13f2a   8d52 10          lea edx,dword ptr ds:[edx+0x10]
 *  13d13f2d   8b05 e4a71001    mov eax,dword ptr ds:[0x110a7e4]
 *  13d13f33   893d 78a71001    mov dword ptr ds:[0x110a778],edi
 *  13d13f39   c705 7ca71001 e8>mov dword ptr ds:[0x110a77c],0x89acde8
 *  13d13f43   8935 80a71001    mov dword ptr ds:[0x110a780],esi
 *  13d13f49   892d 84a71001    mov dword ptr ds:[0x110a784],ebp
 *  13d13f4f   8915 dca71001    mov dword ptr ds:[0x110a7dc],edx
 *  13d13f55   8905 a8aa1001    mov dword ptr ds:[0x110aaa8],eax
 *  13d13f5b   832d c4aa1001 0b sub dword ptr ds:[0x110aac4],0xb
 *  13d13f62  -e9 bcc0a3ef      jmp 03750023
 *  13d13f67   90               nop
 */
//static void SpecialPSPHookNippon2(DWORD esp_base, HookParam *, BYTE, DWORD *data, DWORD *split, DWORD *len)
//{
//  LPCSTR text = LPCSTR(esp_base + pusha_esi_off - 4); // esi address
//  if (*text) {
//    *data = (DWORD)text;
//    *len = !text[0] ? 0 : !text[1] ? 1 : 2; // bp has at most two bytes
//    //*len = ::LeadByteTable[*(BYTE *)text] // TODO: Test leadbytetable
//    *split = regof(ecx, esp_base);
//  }
//}

// 8/13/2014: 5pb might crash on 0.9.9.
bool InsertNippon2PSPHook()
{
  ConsoleOutput("vnreng: Nippon2 PSP: enter");

  const BYTE bytes[] =  {
    0xe9, XX4,                      // 13d13ee4  -e9 1bc1a3ef      jmp 03750004
    0x8b,0x05, XX4,                 // 13d13ee9   8b05 dca71001    mov eax,dword ptr ds:[0x110a7dc]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 13d13eef   81e0 ffffff3f    and eax,0x3fffffff
    0x0f,0xb7,0xb0, XX4,            // 13d13ef5   0fb7b0 0000c007  movzx esi,word ptr ds:[eax+0x7c00000]
    0x8b,0x3d, XX4,                 // 13d13efc   8b3d fccd5a10    mov edi,dword ptr ds:[0x105acdfc]
    0x8b,0xef,                      // 13d13f02   8bef             mov ebp,edi
    0xd1,0xe5,                      // 13d13f04   d1e5             shl ebp,1
    0x81,0xc5, XX4,                 // 13d13f06   81c5 e8cd9a08    add ebp,0x89acde8
    0x8b,0xc5,                      // 13d13f0c   8bc5             mov eax,ebp
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 13d13f0e   81e0 ffffff3f    and eax,0x3fffffff
    0x66,0x89,0xb0 //, XX4          // 13d13f14   66:89b0 2000c007 mov word ptr ds:[eax+0x7c00020],si ; jichi: hook here
  };
  enum { memory_offset = 3 };
  enum { addr_offset = sizeof(bytes) - memory_offset };

  DWORD addr = SafeMatchBytesInPSPMemory(bytes, sizeof(bytes));
  if (!addr)
    ConsoleOutput("vnreng: Nippon2 PSP: pattern not found");
  else {
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.offset = pusha_esi_off - 4; // esi
    hp.type = USING_STRING|NO_CONTEXT;
    hp.text_fun = SpecialPSPHookNippon1;
    ConsoleOutput("vnreng: Nippon2 PSP: INSERT");
    NewHook(hp, "Nippon2 PSP");
  }

  ConsoleOutput("vnreng: Nippon2 PSP: leave");
  return addr;
}

/** 7/26/2014 jichi Broccoli PSP engine, 0.9.8, 0.9.9
 *  Sample game: 明治東亰恋伽 (works on both 0.9.8, 0.9.9)
 *
 *  Memory address is FIXED.
 *  Debug method: breakpoint the memory address
 *
 *  The data is in (WORD)dl in bytes.
 *
 *  There are two text threads.
 *  Only one is correct.
 *
 *  13d26cab   cc               int3
 *  13d26cac   77 0f            ja short 13d26cbd
 *  13d26cae   c705 a8aa1001 24>mov dword ptr ds:[0x110aaa8],0x886a724
 *  13d26cb8  -e9 4793ccef      jmp 039f0004
 *  13d26cbd   8b35 dca71001    mov esi,dword ptr ds:[0x110a7dc]
 *  13d26cc3   8db6 60feffff    lea esi,dword ptr ds:[esi-0x1a0]
 *  13d26cc9   8b3d e4a71001    mov edi,dword ptr ds:[0x110a7e4]
 *  13d26ccf   8bc6             mov eax,esi
 *  13d26cd1   81e0 ffffff3f    and eax,0x3fffffff
 *  13d26cd7   89b8 9001c007    mov dword ptr ds:[eax+0x7c00190],edi
 *  13d26cdd   8b2d 80a71001    mov ebp,dword ptr ds:[0x110a780]
 *  13d26ce3   0fbfed           movsx ebp,bp
 *  13d26ce6   8bd6             mov edx,esi
 *  13d26ce8   8bce             mov ecx,esi
 *  13d26cea   03cd             add ecx,ebp
 *  13d26cec   8935 dca71001    mov dword ptr ds:[0x110a7dc],esi
 *  13d26cf2   33c0             xor eax,eax
 *  13d26cf4   3bd1             cmp edx,ecx
 *  13d26cf6   0f92c0           setb al
 *  13d26cf9   8bf0             mov esi,eax
 *  13d26cfb   81fe 00000000    cmp esi,0x0
 *  13d26d01   8935 70a71001    mov dword ptr ds:[0x110a770],esi
 *  13d26d07   890d 74a71001    mov dword ptr ds:[0x110a774],ecx
 *  13d26d0d   892d 80a71001    mov dword ptr ds:[0x110a780],ebp
 *  13d26d13   8915 8ca71001    mov dword ptr ds:[0x110a78c],edx
 *  13d26d19   0f85 16000000    jnz 13d26d35
 *  13d26d1f   832d c4aa1001 08 sub dword ptr ds:[0x110aac4],0x8
 *  13d26d26   e9 b9000000      jmp 13d26de4
 *  13d26d2b   0158 a7          add dword ptr ds:[eax-0x59],ebx
 *  13d26d2e   8608             xchg byte ptr ds:[eax],cl
 *  13d26d30  -e9 ee92ccef      jmp 039f0023
 *  13d26d35   832d c4aa1001 08 sub dword ptr ds:[0x110aac4],0x8
 *  13d26d3c   e9 0b000000      jmp 13d26d4c
 *  13d26d41   0144a7 86        add dword ptr ds:[edi-0x7a],eax
 *  13d26d45   08e9             or cl,ch
 *  13d26d47   d892 ccef9077    fcom dword ptr ds:[edx+0x7790efcc]
 *  13d26d4d   0fc7             ???                                      ; unknown command
 *  13d26d4f   05 a8aa1001      add eax,0x110aaa8
 *  13d26d54   44               inc esp
 *  13d26d55   a7               cmps dword ptr ds:[esi],dword ptr es:[ed>
 *  13d26d56   8608             xchg byte ptr ds:[eax],cl
 *  13d26d58  -e9 a792ccef      jmp 039f0004
 *  13d26d5d   8b05 7ca71001    mov eax,dword ptr ds:[0x110a77c]
 *  13d26d63   81e0 ffffff3f    and eax,0x3fffffff
 *  13d26d69   0fb6b0 0000c007  movzx esi,byte ptr ds:[eax+0x7c00000]
 *  13d26d70   8b3d 7ca71001    mov edi,dword ptr ds:[0x110a77c]
 *  13d26d76   8d7f 01          lea edi,dword ptr ds:[edi+0x1]
 *  13d26d79   8b05 8ca71001    mov eax,dword ptr ds:[0x110a78c]
 *  13d26d7f   81e0 ffffff3f    and eax,0x3fffffff
 *  13d26d85   8bd6             mov edx,esi
 *  13d26d87   8890 0000c007    mov byte ptr ds:[eax+0x7c00000],dl ; jichi: hook here, get byte from dl
 *  13d26d8d   8b2d 8ca71001    mov ebp,dword ptr ds:[0x110a78c]
 *  13d26d93   8d6d 01          lea ebp,dword ptr ss:[ebp+0x1]
 *  13d26d96   81fe 00000000    cmp esi,0x0
 *  13d26d9c   893d 7ca71001    mov dword ptr ds:[0x110a77c],edi
 *  13d26da2   8935 88a71001    mov dword ptr ds:[0x110a788],esi
 *  13d26da8   892d 8ca71001    mov dword ptr ds:[0x110a78c],ebp
 *  13d26dae   0f84 16000000    je 13d26dca
 *  13d26db4   832d c4aa1001 05 sub dword ptr ds:[0x110aac4],0x5
 *  13d26dbb   e9 f48b0100      jmp 13d3f9b4
 *  13d26dc0   0138             add dword ptr ds:[eax],edi
 *  13d26dc2   a7               cmps dword ptr ds:[esi],dword ptr es:[ed>
 *  13d26dc3   8608             xchg byte ptr ds:[eax],cl
 *  13d26dc5  -e9 5992ccef      jmp 039f0023
 *  13d26dca   832d c4aa1001 05 sub dword ptr ds:[0x110aac4],0x5
 *  13d26dd1   e9 0e000000      jmp 13d26de4
 *  13d26dd6   0158 a7          add dword ptr ds:[eax-0x59],ebx
 *  13d26dd9   8608             xchg byte ptr ds:[eax],cl
 *  13d26ddb  -e9 4392ccef      jmp 039f0023
 *  13d26de0   90               nop
 *  13d26de1   cc               int3
 */

// New line character for Broccoli games is '^'
static inline bool _broccoligarbage_ch(char c) { return c == '^'; }

// Read text from dl
static void SpecialPSPHookBroccoli(DWORD esp_base, HookParam *, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  DWORD text = esp_base + pusha_edx_off - 4; // edx address
  char c = *(LPCSTR)text;
  if (c && !_broccoligarbage_ch(c)) {
    *data = text;
    *len = 1;
    *split = regof(ecx, esp_base);
  }
}

bool InsertBroccoliPSPHook()
{
  ConsoleOutput("vnreng: Broccoli PSP: enter");

  const BYTE bytes[] =  {
    0x0f,0xc7,                      // 13d26d4d   0fc7             ???                                      ; unknown command
    0x05, XX4,                      // 13d26d4f   05 a8aa1001      add eax,0x110aaa8
    0x44,                           // 13d26d54   44               inc esp
    0xa7,                           // 13d26d55   a7               cmps dword ptr ds:[esi],dword ptr es:[ed>
    0x86,0x08,                      // 13d26d56   8608             xchg byte ptr ds:[eax],cl
    0xe9, XX4,                      // 13d26d58  -e9 a792ccef      jmp 039f0004
    0x8b,0x05, XX4,                 // 13d26d5d   8b05 7ca71001    mov eax,dword ptr ds:[0x110a77c]
    // Following pattern is not sufficient
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 13d26d63   81e0 ffffff3f    and eax,0x3fffffff
    0x0f,0xb6,0xb0, XX4,            // 13d26d69   0fb6b0 0000c007  movzx esi,byte ptr ds:[eax+0x7c00000]
    0x8b,0x3d, XX4,                 // 13d26d70   8b3d 7ca71001    mov edi,dword ptr ds:[0x110a77c]
    0x8d,0x7f, 0x01,                // 13d26d76   8d7f 01          lea edi,dword ptr ds:[edi+0x1]
    0x8b,0x05, XX4,                 // 13d26d79   8b05 8ca71001    mov eax,dword ptr ds:[0x110a78c]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 13d26d7f   81e0 ffffff3f    and eax,0x3fffffff
    0x8b,0xd6,                      // 13d26d85   8bd6             mov edx,esi
    0x88,0x90, XX4,                 // 13d26d87   8890 0000c007    mov byte ptr ds:[eax+0x7c00000],dl ; jichi: hook here, get byte from dl
    0x8b,0x2d, XX4,                 // 13d26d8d   8b2d 8ca71001    mov ebp,dword ptr ds:[0x110a78c]
    0x8d,0x6d, 0x01,                // 13d26d93   8d6d 01          lea ebp,dword ptr ss:[ebp+0x1]
    0x81,0xfe, 0x00,0x00,0x00,0x00  // 13d26d96   81fe 00000000    cmp esi,0x0
  };
  enum { addr_offset = 0x13d26d87 - 0x13d26d4d };

  DWORD addr = SafeMatchBytesInPSPMemory(bytes, sizeof(bytes));
  if (!addr)
    ConsoleOutput("vnreng: Broccoli PSP: pattern not found");
  else {
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.type = USING_STRING|USING_SPLIT|NO_CONTEXT;
    hp.text_fun = SpecialPSPHookBroccoli;
    //GROWL_DWORD(hp.address);
    ConsoleOutput("vnreng: Broccoli PSP: INSERT");
    NewHook(hp, "Broccoli PSP");
  }

  ConsoleOutput("vnreng: Broccoli PSP: leave");
  return addr;
}

/** 7/26/2014 jichi Otomate PSP engine, 0.9.8 only, 0.9.9 not work
 *  Replaced by Otomate PPSSPP on 0.9.9.
 *
 *  Sample game: クロノスタシア
 *  Sample game: フォトカ�(repetition)
 *
 *  Not work on 0.9.9: Amnesia Crowd
 *
 *  The instruction pattern also exist in 0.9.9. But the function is not called.
 *
 *  Memory address is FIXED.
 *  Debug method: breakpoint the memory address
 *
 *  The memory access of the function below is weird that the accessed value is 2 bytes after the real text.
 *
 *  PPSSPP 0.9.8, クロノスタシア
 *  13c00fe1   cc               int3
 *  13c00fe2   cc               int3
 *  13c00fe3   cc               int3
 *  13c00fe4   77 0f            ja short 13c00ff5
 *  13c00fe6   c705 a8aa1001 30>mov dword ptr ds:[0x110aaa8],0x884b330
 *  13c00ff0  -e9 0ff0edf2      jmp 06ae0004
 *  13c00ff5   8b05 78a71001    mov eax,dword ptr ds:[0x110a778]
 *  13c00ffb   81e0 ffffff3f    and eax,0x3fffffff
 *  13c01001   0fbeb0 0000c007  movsx esi,byte ptr ds:[eax+0x7c00000] ; jichi: hook here
 *  13c01008   81fe 00000000    cmp esi,0x0 ; jichi: hook here, get the esi value
 *  13c0100e   8935 80a71001    mov dword ptr ds:[0x110a780],esi
 *  13c01014   0f84 25000000    je 13c0103f
 *  13c0101a   8b35 78a71001    mov esi,dword ptr ds:[0x110a778]
 *  13c01020   8d76 01          lea esi,dword ptr ds:[esi+0x1]
 *  13c01023   8935 78a71001    mov dword ptr ds:[0x110a778],esi
 *  13c01029   832d c4aa1001 03 sub dword ptr ds:[0x110aac4],0x3
 *  13c01030  ^e9 afffffff      jmp 13c00fe4
 *  13c01035   0130             add dword ptr ds:[eax],esi
 *  13c01037   b3 84            mov bl,0x84
 *  13c01039   08e9             or cl,ch
 *  13c0103b   e4 ef            in al,0xef                               ; i/o command
 *  13c0103d   ed               in eax,dx                                ; i/o command
 *  13c0103e   f2:              prefix repne:                            ; superfluous prefix
 *  13c0103f   832d c4aa1001 03 sub dword ptr ds:[0x110aac4],0x3
 *  13c01046   e9 0d000000      jmp 13c01058
 *  13c0104b   013cb3           add dword ptr ds:[ebx+esi*4],edi
 *  13c0104e   8408             test byte ptr ds:[eax],cl
 *  13c01050  -e9 ceefedf2      jmp 06ae0023
 *  13c01055   90               nop
 *  13c01056   cc               int3
 *  13c01057   cc               int3
 */
// TODO: is reverse_strlen a better choice?
// Read text from esi
static void SpecialPSPHookOtomate(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  //static uniquemap uniq;
  DWORD eax = regof(eax, esp_base);
  LPCSTR text = LPCSTR(eax + hp->user_value - 2); // -2 to read 1 word more from previous location
  if (*text) {
    *split = regof(ecx, esp_base); // this would cause lots of texts, but it works for all games
    //*split = regof(ecx, esp_base) & 0xff00; // only use higher bits
    *data = (DWORD)text;
    size_t sz = ::strlen(text);
    *len = sz == 3 ? 3 : 1; // handling the last two bytes
  }
}

bool InsertOtomatePSPHook()
{
  ConsoleOutput("vnreng: Otomate PSP: enter");
  const BYTE bytes[] =  {
    0x77, 0x0f,                     // 13c00fe4   77 0f            ja short 13c00ff5
    0xc7,0x05, XX8,                 // 13c00fe6   c705 a8aa1001 30>mov dword ptr ds:[0x110aaa8],0x884b330
    0xe9, XX4,                      // 13c00ff0  -e9 0ff0edf2      jmp 06ae0004
    0x8b,0x05, XX4,                 // 13c00ff5   8b05 78a71001    mov eax,dword ptr ds:[0x110a778]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 13c00ffb   81e0 ffffff3f    and eax,0x3fffffff
    0x0f,0xbe,0xb0, XX4,            // 13c01001   0fbeb0 0000c007  movsx esi,byte ptr ds:[eax+0x7c00000] ; jichi: hook here
    0x81,0xfe, 0x00,0x00,0x00,0x00, // 13c01008   81fe 00000000    cmp esi,0x0
    0x89,0x35, XX4,                 // 13c0100e   8935 80a71001    mov dword ptr ds:[0x110a780],esi
    0x0f,0x84, 0x25,0x00,0x00,0x00, // 13c01014   0f84 25000000    je 13c0103f
    0x8b,0x35, XX4,                 // 13c0101a   8b35 78a71001    mov esi,dword ptr ds:[0x110a778]
    0x8d,0x76, 0x01,                // 13c01020   8d76 01          lea esi,dword ptr ds:[esi+0x1]
    0x89,0x35, XX4,                 // 13c01023   8935 78a71001    mov dword ptr ds:[0x110a778],esi
    0x83,0x2d, XX4, 0x03            // 13c01029   832d c4aa1001 03 sub dword ptr ds:[0x110aac4],0x3
  };
  enum { memory_offset = 3 };
  //enum { addr_offset = 0x13c01008 - 0x13c00fe4 };
  enum { addr_offset = 0x13c01001- 0x13c00fe4 };

  DWORD addr = SafeMatchBytesInPSPMemory(bytes, sizeof(bytes));
  //GROWL_DWORD(addr);
  if (!addr)
    ConsoleOutput("vnreng: Otomate PSP: pattern not found");
  else {
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.user_value = *(DWORD *)(hp.address + memory_offset);
    hp.type = USING_STRING|NO_CONTEXT;
    hp.text_fun = SpecialPSPHookOtomate;
    ConsoleOutput("vnreng: Otomate PSP: INSERT");
    NewHook(hp, "Otomate PSP");
  }

  ConsoleOutput("vnreng: Otomate PSP: leave");
  return addr;
}

/** 7/29/2014 jichi Otomate PPSSPP 0.9.9
 *  Sample game: Amnesia Crowd
 *  Sample game: Amnesia Later
 *
 *  006db4af   cc               int3
 *  006db4b0   8b15 b8ebaf00    mov edx,dword ptr ds:[0xafebb8]          ; ppssppwi.01134988
 *  006db4b6   56               push esi
 *  006db4b7   8b42 10          mov eax,dword ptr ds:[edx+0x10]
 *  006db4ba   25 ffffff3f      and eax,0x3fffffff
 *  006db4bf   0305 94411301    add eax,dword ptr ds:[0x1134194]
 *  006db4c5   8d70 01          lea esi,dword ptr ds:[eax+0x1]
 *  006db4c8   8a08             mov cl,byte ptr ds:[eax] ; jichi: hook here, get text in [eax]
 *  006db4ca   40               inc eax
 *  006db4cb   84c9             test cl,cl
 *  006db4cd  ^75 f9            jnz short ppssppwi.006db4c8
 *  006db4cf   2bc6             sub eax,esi
 *  006db4d1   8942 08          mov dword ptr ds:[edx+0x8],eax
 *  006db4d4   5e               pop esi
 *  006db4d5   8d0485 07000000  lea eax,dword ptr ds:[eax*4+0x7]
 *  006db4dc   c3               retn
 *  006db4dd   cc               int3
 */
static void SpecialPPSSPPHookOtomate(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  // 006db4b7   8b42 10          mov eax,dword ptr ds:[edx+0x10] ; jichi: hook here
  // 006db4ba   25 ffffff3f      and eax,0x3fffffff
  // 006db4bf   0305 94411301    add eax,dword ptr ds:[0x1134194]; jichi: ds offset
  // 006db4c5   8d70 01          lea esi,dword ptr ds:[eax+0x1]
  DWORD edx = regof(edx, esp_base);
  DWORD eax = *(DWORD *)(edx + 0x10);
  eax &= 0x3fffffff;
  eax += *(DWORD *)hp->user_value;

  //DWORD eax = regof(eax, esp_base);
  LPCSTR text = LPCSTR(eax);
  if (*text) {
    text = _bandailtrim(text); // the same as bandai PSP
    *data = (DWORD)text;
    *len = _bandaistrlen(text);

    *split = regof(ecx, esp_base); // the same as Otomate PSP hook
    //DWORD ecx = regof(ecx, esp_base); // the same as Otomate PSP hook
    //*split = ecx ? ecx : (FIXED_SPLIT_VALUE << 2);
    //*split = ecx & 0xffffff00; // skip cl which is used
  }
}
bool InsertOtomatePPSSPPHook()
{
  ULONG startAddress, stopAddress;
  if (!NtInspect::getProcessMemoryRange(&startAddress, &stopAddress)) { // need accurate stopAddress
    ConsoleOutput("vnreng: Otomate PPSSPP: failed to get memory range");
    return false;
  }
  ConsoleOutput("vnreng: Otomate PPSSPP: enter");
  const BYTE bytes[] =  {
    0x8b,0x15, XX4,             // 006db4b0   8b15 b8ebaf00    mov edx,dword ptr ds:[0xafebb8]          ; ppssppwi.01134988
    0x56,                       // 006db4b6   56               push esi
    0x8b,0x42, 0x10,            // 006db4b7   8b42 10          mov eax,dword ptr ds:[edx+0x10] ; jichi: hook here
    0x25, 0xff,0xff,0xff,0x3f,  // 006db4ba   25 ffffff3f      and eax,0x3fffffff
    0x03,0x05, XX4,             // 006db4bf   0305 94411301    add eax,dword ptr ds:[0x1134194]; jichi: ds offset
    0x8d,0x70, 0x01,            // 006db4c5   8d70 01          lea esi,dword ptr ds:[eax+0x1]
    0x8a,0x08,                  // 006db4c8   8a08             mov cl,byte ptr ds:[eax] ; jichi: hook here
    0x40,                       // 006db4ca   40               inc eax
    0x84,0xc9,                  // 006db4cb   84c9             test cl,cl
    0x75, 0xf9,                 // 006db4cd  ^75 f9            jnz short ppssppwi.006db4c8
    0x2b,0xc6,                  // 006db4cf   2bc6             sub eax,esi
    0x89,0x42, 0x08,            // 006db4d1   8942 08          mov dword ptr ds:[edx+0x8],eax
    0x5e,                       // 006db4d4   5e               pop esi
    0x8d,0x04,0x85, 0x07,0x00,0x00,0x00  // 006db4d5   8d0485 07000000  lea eax,dword ptr ds:[eax*4+0x7]
  };
  //enum { addr_offset = 0x006db4c8 - 0x006db4b0 };
  enum { addr_offset = 0x006db4b7 - 0x006db4b0 };
  enum { ds_offset = 0x006db4bf - 0x006db4b0 + 2 };

  DWORD addr = SafeMatchBytes(bytes, sizeof(bytes), startAddress, stopAddress);
  //GROWL_DWORD(addr);
  if (!addr)
    ConsoleOutput("vnreng: Otomate PPSSPP: pattern not found");
  else {
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.user_value = *(DWORD *)(addr + ds_offset); // this is the address after ds:[]
    hp.type = USING_STRING|NO_CONTEXT;
    hp.text_fun = SpecialPPSSPPHookOtomate;
    ConsoleOutput("vnreng: Otomate PPSSPP: INSERT");
    NewHook(hp, "Otomate PPSSPP");
  }

  ConsoleOutput("vnreng: Otomate PPSSPP: leave");
  return addr;
}

#if 0 // FIXME: I am not able to find stable pattern in PSP 0.9.9.1

/** 9/21/2014 jichi Otomate PPSSPP 0.9.9.1
 *  Sample game: Amnesia Later
 *
 *  There are four fixed memory addresses.
 *  The two out of four can be used.
 *  (The other twos have loops or cannot be debugged).
 *
 *  This function is the same as PPSSPP 0.9.9.1 (?).
 *
 *  14039126   cc               int3
 *  14039127   cc               int3
 *  14039128   77 0f            ja short 14039139
 *  1403912a   c705 988e1301 3c>mov dword ptr ds:[0x1138e98],0x8922c3c
 *  14039134  -e9 cb6e83ef      jmp 03870004
 *  14039139   8b05 688b1301    mov eax,dword ptr ds:[0x1138b68]
 *  1403913f   81e0 ffffff3f    and eax,0x3fffffff
 *  14039145   0fbeb0 00000008  movsx esi,byte ptr ds:[eax+0x8000000]   ; jichi: text accessed, but looped
 *  1403914c   8b05 6c8b1301    mov eax,dword ptr ds:[0x1138b6c]
 *  14039152   81e0 ffffff3f    and eax,0x3fffffff
 *  14039158   0fbeb8 00000008  movsx edi,byte ptr ds:[eax+0x8000000]
 *  1403915f   3bf7             cmp esi,edi
 *  14039161   8935 748b1301    mov dword ptr ds:[0x1138b74],esi
 *  14039167   893d 7c8b1301    mov dword ptr ds:[0x1138b7c],edi
 *  1403916d   0f84 2f000000    je 140391a2
 *  14039173   8b05 688b1301    mov eax,dword ptr ds:[0x1138b68]
 *  14039179   81e0 ffffff3f    and eax,0x3fffffff
 *  1403917f   0fb6b0 00000008  movzx esi,byte ptr ds:[eax+0x8000000]   ; jichi: hook here
 *  14039186   8935 608b1301    mov dword ptr ds:[0x1138b60],esi
 *  1403918c   832d b48e1301 04 sub dword ptr ds:[0x1138eb4],0x4
 *  14039193   e9 24000000      jmp 140391bc
 *  14039198   0170 2c          add dword ptr ds:[eax+0x2c],esi
 *  1403919b   92               xchg eax,edx
 *  1403919c   08e9             or cl,ch
 *  1403919e   816e 83 ef832db4 sub dword ptr ds:[esi-0x7d],0xb42d83ef
 *  140391a5   8e13             mov ss,word ptr ds:[ebx]                 ; modification of segment register
 *  140391a7   0104e9           add dword ptr ds:[ecx+ebp*8],eax
 *  140391aa   b2 59            mov dl,0x59
 *  140391ac   0000             add byte ptr ds:[eax],al
 *  140391ae   014c2c 92        add dword ptr ss:[esp+ebp-0x6e],ecx
 *  140391b2   08e9             or cl,ch
 *  140391b4   6b6e 83 ef       imul ebp,dword ptr ds:[esi-0x7d],-0x11
 *  140391b8   90               nop
 *  140391b9   cc               int3
 *  140391ba   cc               int3
 */
// Get bytes in esi
static void SpecialPSPHookOtomate2(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  //static uniquemap uniq;
  DWORD text = esp_base + pusha_esi_off - 4;
  if (*(LPCSTR *)text) {
    *split = regof(ecx, esp_base); // this would cause lots of texts, but it works for all games
    *data = text;
    *len = 1;
  }
}

bool InsertOtomate2PSPHook()
{
  ConsoleOutput("vnreng: Otomate2 PSP: enter");
  const BYTE bytes[] =  {
    0x77, 0x0f,                     // 14039128   77 0f            ja short 14039139
    0xc7,0x05, XX8,                 // 1403912a   c705 988e1301 3c>mov dword ptr ds:[0x1138e98],0x8922c3c
    0xe9, XX4,                      // 14039134  -e9 cb6e83ef      jmp 03870004
    0x8b,0x05, XX4,                 // 14039139   8b05 688b1301    mov eax,dword ptr ds:[0x1138b68]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 1403913f   81e0 ffffff3f    and eax,0x3fffffff
    0x0f,0xbe,0xb0, XX4,            // 14039145   0fbeb0 00000008  movsx esi,byte ptr ds:[eax+0x8000000]   ; jichi: text accessed, but looped
    0x8b,0x05, XX4,                 // 1403914c   8b05 6c8b1301    mov eax,dword ptr ds:[0x1138b6c]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 14039152   81e0 ffffff3f    and eax,0x3fffffff
    0x0f,0xbe,0xb8, XX4,            // 14039158   0fbeb8 00000008  movsx edi,byte ptr ds:[eax+0x8000000]
    0x3b,0xf7,                      // 1403915f   3bf7             cmp esi,edi
    0x89,0x35, XX4,                 // 14039161   8935 748b1301    mov dword ptr ds:[0x1138b74],esi
    0x89,0x3d, XX4,                 // 14039167   893d 7c8b1301    mov dword ptr ds:[0x1138b7c],edi
    0x0f,0x84, 0x2f,0x00,0x00,0x00, // 1403916d   0f84 2f000000    je 140391a2

    //0x8b,0x05, XX4,                 // 14039173   8b05 688b1301    mov eax,dword ptr ds:[0x1138b68]
    //0x81,0xe0, 0xff,0xff,0xff,0x3f, // 14039179   81e0 ffffff3f    and eax,0x3fffffff
    //0x0f,0xb6,0xb0, XX4,            // 1403917f   0fb6b0 00000008  movzx esi,byte ptr ds:[eax+0x8000000]   ; jichi: text accessed
    //0x89,0x35, XX4,                 // 14039186   8935 608b1301    mov dword ptr ds:[0x1138b60],esi   ; jichi: hook here, get lower bytes in esi
    //0x83,0x2d, XX4, 0x04            // 1403918c   832d b48e1301 04 sub dword ptr ds:[0x1138eb4],0x4
  };
  enum { addr_offset = 0x14039186 - 0x14039128 };

  DWORD addr = SafeMatchBytesInPSPMemory(bytes, sizeof(bytes));
  if (!addr) {
    ConsoleOutput("vnreng: Otomate2 PSP: leave: first pattern not found");
    return false;
  }
  addr += addr_offset;

  //0x89,0x35, XX4,                 // 14039186   8935 608b1301    mov dword ptr ds:[0x1138b60],esi   ; jichi: hook here, get lower bytes in esi
  enum : WORD { mov_esi = 0x3589 };
  if (*(WORD *)addr != mov_esi) {
    ConsoleOutput("vnreng: Otomate2 PSP: leave: second pattern not found");
    return false;
  }

  HookParam hp = {};
  hp.address = addr;
  hp.type = USING_STRING|NO_CONTEXT;
  hp.text_fun = SpecialPSPHookOtomate2;
  ConsoleOutput("vnreng: Otomate2 PSP: INSERT");
  NewHook(hp, "Otomate PSP");

  ConsoleOutput("vnreng: Otomate2 PSP: leave");
  return addr;
}

#endif // 0

/** 7/27/2014 jichi Intense.jp PSP engine, 0.9.8, 0.9.9,
 *  Though Otomate can work, it cannot work line by line.
 *
 *  Sample game: 寮�のサクリファイス work on 0.9.8 & 0.9.9
 *  This hook is only for intro graphic painting
 *
 *  Memory address is FIXED.
 *  Debug method: predict and breakpoint the memory address
 *
 *  There are two matches in the memory, and only one function accessing them.
 *  The memory is accessed by words.
 *
 *  The memory and hooked function is as follows.
 *
 *  09dfee77  88 c3 82 a2 95 a3 82 cc 89 9c 92 ea 82 c5 81 41  暗い淵の奥底で� *  09dfee87  92 e1 82 ad 81 41 8f ac 82 b3 82 ad 81 41 8b bf  低く、小さく〟�
 *  09dfee97  82 ad 81 42 2a 70 0a 82 b1 82 ea 82 cd 81 41 8c  く�p.これは、�
 *  09dfeea7  db 93 ae 81 63 81 48 2a 70 0a 82 c6 82 e0 82 b7  �動…p.ともす
 *  09dfeeb7  82 ea 82 ce 95 b7 82 ab 93 a6 82 b5 82 c4 82 b5  れ�聞き送�て� *  09dfeec7  82 dc 82 a2 82 bb 82 a4 82 c8 81 41 2a 70 0a 8f  まぁ�ぁ��p.・
 *  09dfeed7  ac 82 b3 82 ad 81 41 8e e3 81 58 82 b5 82 ad 81  �さく、弱、�く�
 *  09dfeee7  41 95 73 8a 6d 82 a9 82 c8 89 b9 81 42 00 00 00  a不確かな音�..
 *  09dfeef7  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 *  09dfee07  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 *
 *  13472227   90               nop
 *  13472228   77 0f            ja short 13472239
 *  1347222a   c705 a8aa1001 20>mov dword ptr ds:[0x110aaa8],0x884ce20
 *  13472234  -e9 cbdd16f0      jmp 035e0004
 *  13472239   8b05 a8a71001    mov eax,dword ptr ds:[0x110a7a8]
 *  1347223f   81e0 ffffff3f    and eax,0x3fffffff
 *  13472245   8bb0 30004007    mov esi,dword ptr ds:[eax+0x7400030]
 *  1347224b   8b3d 84a71001    mov edi,dword ptr ds:[0x110a784]
 *  13472251   81c7 01000000    add edi,0x1
 *  13472257   8bee             mov ebp,esi
 *  13472259   032d 84a71001    add ebp,dword ptr ds:[0x110a784]
 *  1347225f   8bc5             mov eax,ebp
 *  13472261   81e0 ffffff3f    and eax,0x3fffffff
 *  13472267   0fbe90 00004007  movsx edx,byte ptr ds:[eax+0x7400000]   ; jichi: hook here
 *  1347226e   8b05 a8a71001    mov eax,dword ptr ds:[0x110a7a8]
 *  13472274   81e0 ffffff3f    and eax,0x3fffffff
 *  1347227a   89b8 38004007    mov dword ptr ds:[eax+0x7400038],edi
 *  13472280   8bea             mov ebp,edx
 *  13472282   81e5 ff000000    and ebp,0xff
 *  13472288   81fa 0a000000    cmp edx,0xa
 *  1347228e   c705 70a71001 0a>mov dword ptr ds:[0x110a770],0xa
 *  13472298   8915 74a71001    mov dword ptr ds:[0x110a774],edx
 *  1347229e   893d 78a71001    mov dword ptr ds:[0x110a778],edi
 *  134722a4   892d 7ca71001    mov dword ptr ds:[0x110a77c],ebp
 *  134722aa   8935 80a71001    mov dword ptr ds:[0x110a780],esi
 *  134722b0   0f85 16000000    jnz 134722cc
 *  134722b6   832d c4aa1001 08 sub dword ptr ds:[0x110aac4],0x8
 *  134722bd   e9 02680000      jmp 13478ac4
 *  134722c2   01ec             add esp,ebp
 *  134722c4   ce               into
 *  134722c5   8408             test byte ptr ds:[eax],cl
 *  134722c7  -e9 57dd16f0      jmp 035e0023
 *  134722cc   832d c4aa1001 08 sub dword ptr ds:[0x110aac4],0x8
 *  134722d3   e9 0c000000      jmp 134722e4
 *  134722d8   0140 ce          add dword ptr ds:[eax-0x32],eax
 *  134722db   8408             test byte ptr ds:[eax],cl
 *  134722dd  -e9 41dd16f0      jmp 035e0023
 *  134722e2   90               nop
 *  134722e3   cc               int3
 */
// Read text from esi
static void SpecialPSPHookIntense(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  DWORD eax = regof(eax, esp_base);
  DWORD text = eax + hp->user_value;
  if (BYTE c = *(BYTE *)text) { // unsigned char
    *data = text;
    *len = ::LeadByteTable[c]; // 1 or 2
    //*split = regof(ecx, esp_base); // cause scenario text to split
    //*split = regof(edx, esp_base); // cause scenario text to split

    //*split = regof(ebx, esp_base); // works, but floating value
    *split = FIXED_SPLIT_VALUE * 3;
  }
}
bool InsertIntensePSPHook()
{
  ConsoleOutput("vnreng: Intense PSP: enter");
  const BYTE bytes[] =  {
    0x77, 0x0f,                     // 13472228   77 0f            ja short 13472239
    0xc7,0x05, XX8,                 // 1347222a   c705 a8aa1001 20>mov dword ptr ds:[0x110aaa8],0x884ce20
    0xe9, XX4,                      // 13472234  -e9 cbdd16f0      jmp 035e0004
    0x8b,0x05, XX4,                 // 13472239   8b05 a8a71001    mov eax,dword ptr ds:[0x110a7a8]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 1347223f   81e0 ffffff3f    and eax,0x3fffffff
    0x8b,0xb0, XX4,                 // 13472245   8bb0 30004007    mov esi,dword ptr ds:[eax+0x7400030]
    0x8b,0x3d, XX4,                 // 1347224b   8b3d 84a71001    mov edi,dword ptr ds:[0x110a784]
    0x81,0xc7, 0x01,0x00,0x00,0x00, // 13472251   81c7 01000000    add edi,0x1
    0x8b,0xee,                      // 13472257   8bee             mov ebp,esi
    0x03,0x2d, XX4,                 // 13472259   032d 84a71001    add ebp,dword ptr ds:[0x110a784]
    0x8b,0xc5,                      // 1347225f   8bc5             mov eax,ebp
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 13472261   81e0 ffffff3f    and eax,0x3fffffff
    0x0f,0xbe,0x90, XX4,            // 13472267   0fbe90 00004007  movsx edx,byte ptr ds:[eax+0x7400000]   ; jichi: hook here
    0x8b,0x05, XX4,                 // 1347226e   8b05 a8a71001    mov eax,dword ptr ds:[0x110a7a8]
    0x81,0xe0, 0xff,0xff,0xff,0x3f  // 13472274   81e0 ffffff3f    and eax,0x3fffffff
  };
  enum { memory_offset = 3 };
  enum { addr_offset = 0x13472267 - 0x13472228 };

  DWORD addr = SafeMatchBytesInPSPMemory(bytes, sizeof(bytes));
  if (!addr)
    ConsoleOutput("vnreng: Intense PSP: pattern not found");
  else {
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.user_value = *(DWORD *)(hp.address + memory_offset);
    hp.type = USING_STRING|NO_CONTEXT;
    hp.text_fun = SpecialPSPHookIntense;
    ConsoleOutput("vnreng: Intense PSP: INSERT");
    NewHook(hp, "Intense PSP");
  }

  ConsoleOutput("vnreng: Intense PSP: leave");
  return addr;
}

/** 8/12/2014 jichi Konami.jp PSP engine, 0.9.8, 0.9.9,
 *  Though Alchemist/Otomate can work, it has bad split that creates too many threads.
 *
 *  Sample game: 幻想水滸�紡がれし百年の�on 0.9.8, 0.9.9
 *
 *  Memory address is FIXED.
 *  But hardware accesses are looped.
 *  Debug method: predict and breakpoint the memory address
 *
 *  There are two matches in the memory.
 *  Three looped functions are as follows.
 *  I randomply picked the first one.
 *
 *  It cannot extract character names.
 *
 *  14178f73   cc               int3
 *  14178f74   77 0f            ja short 14178f85
 *  14178f76   c705 c84c1301 a4>mov dword ptr ds:[0x1134cc8],0x88129a4
 *  14178f80  -e9 7f7071ef      jmp 03890004
 *  14178f85   8b05 c8491301    mov eax,dword ptr ds:[0x11349c8]
 *  14178f8b   81e0 ffffff3f    and eax,0x3fffffff
 *  14178f91   0fbeb0 00000008  movsx esi,byte ptr ds:[eax+0x8000000] ; jichi: hook here, loop
 *  14178f98   81fe 40000000    cmp esi,0x40
 *  14178f9e   8935 98491301    mov dword ptr ds:[0x1134998],esi
 *  14178fa4   c705 9c491301 40>mov dword ptr ds:[0x113499c],0x40
 *  14178fae   0f85 2f000000    jnz 14178fe3
 *  14178fb4   8b05 c8491301    mov eax,dword ptr ds:[0x11349c8]
 *  14178fba   81e0 ffffff3f    and eax,0x3fffffff
 *  14178fc0   0fbeb0 01000008  movsx esi,byte ptr ds:[eax+0x8000001]
 *  14178fc7   8935 98491301    mov dword ptr ds:[0x1134998],esi
 *  14178fcd   832d e44c1301 04 sub dword ptr ds:[0x1134ce4],0x4
 *  14178fd4   c705 c84c1301 d0>mov dword ptr ds:[0x1134cc8],0x88129d0
 *  14178fde  -e9 407071ef      jmp 03890023
 *  14178fe3   832d e44c1301 04 sub dword ptr ds:[0x1134ce4],0x4
 *  14178fea   e9 0d000000      jmp 14178ffc
 *  14178fef   01b429 8108e92a  add dword ptr ds:[ecx+ebp+0x2ae90881],es>
 *  14178ff6   70 71            jo short 14179069
 *  14178ff8   ef               out dx,eax                               ; i/o command
 *  14178ff9   90               nop
 *  14178ffa   cc               int3
 *
 *  1417a18c   77 0f            ja short 1417a19d
 *  1417a18e   c705 c84c1301 78>mov dword ptr ds:[0x1134cc8],0x8818378
 *  1417a198  -e9 675e71ef      jmp 03890004
 *  1417a19d   8b05 c8491301    mov eax,dword ptr ds:[0x11349c8]
 *  1417a1a3   81e0 ffffff3f    and eax,0x3fffffff
 *  1417a1a9   0fbeb0 00000008  movsx esi,byte ptr ds:[eax+0x8000000] ; jichi: hook here, loop
 *  1417a1b0   81fe 0a000000    cmp esi,0xa
 *  1417a1b6   8935 98491301    mov dword ptr ds:[0x1134998],esi
 *  1417a1bc   c705 9c491301 0a>mov dword ptr ds:[0x113499c],0xa
 *  1417a1c6   0f84 2e000000    je 1417a1fa
 *  1417a1cc   8b05 fc491301    mov eax,dword ptr ds:[0x11349fc]
 *  1417a1d2   81e0 ffffff3f    and eax,0x3fffffff
 *  1417a1d8   8bb0 18000008    mov esi,dword ptr ds:[eax+0x8000018]
 *  1417a1de   8935 98491301    mov dword ptr ds:[0x1134998],esi
 *  1417a1e4   832d e44c1301 04 sub dword ptr ds:[0x1134ce4],0x4
 *  1417a1eb   e9 24000000      jmp 1417a214
 *  1417a1f0   01b0 838108e9    add dword ptr ds:[eax+0xe9088183],esi
 *  1417a1f6   295e 71          sub dword ptr ds:[esi+0x71],ebx
 *  1417a1f9   ef               out dx,eax                               ; i/o command
 *  1417a1fa   832d e44c1301 04 sub dword ptr ds:[0x1134ce4],0x4
 *  1417a201   e9 1e660000      jmp 14180824
 *  1417a206   0188 838108e9    add dword ptr ds:[eax+0xe9088183],ecx
 *  1417a20c   135e 71          adc ebx,dword ptr ds:[esi+0x71]
 *  1417a20f   ef               out dx,eax                               ; i/o command
 *  1417a210   90               nop
 *  1417a211   cc               int3
 *  1417a212   cc               int3
 *
 *  1417a303   90               nop
 *  1417a304   77 0f            ja short 1417a315
 *  1417a306   c705 c84c1301 48>mov dword ptr ds:[0x1134cc8],0x8818448
 *  1417a310  -e9 ef5c71ef      jmp 03890004
 *  1417a315   8b35 dc491301    mov esi,dword ptr ds:[0x11349dc]
 *  1417a31b   8b3d 98491301    mov edi,dword ptr ds:[0x1134998]
 *  1417a321   33c0             xor eax,eax
 *  1417a323   3bf7             cmp esi,edi
 *  1417a325   0f9cc0           setl al
 *  1417a328   8bf8             mov edi,eax
 *  1417a32a   81ff 00000000    cmp edi,0x0
 *  1417a330   893d 98491301    mov dword ptr ds:[0x1134998],edi
 *  1417a336   0f84 2f000000    je 1417a36b
 *  1417a33c   8b05 c8491301    mov eax,dword ptr ds:[0x11349c8]
 *  1417a342   81e0 ffffff3f    and eax,0x3fffffff
 *  1417a348   0fbeb0 00000008  movsx esi,byte ptr ds:[eax+0x8000000] ; jichi: hook here, loop
 *  1417a34f   8935 98491301    mov dword ptr ds:[0x1134998],esi
 *  1417a355   832d e44c1301 03 sub dword ptr ds:[0x1134ce4],0x3
 *  1417a35c   e9 23000000      jmp 1417a384
 *  1417a361   018484 8108e9b8  add dword ptr ss:[esp+eax*4+0xb8e90881],>
 *  1417a368   5c               pop esp
 *  1417a369  ^71 ef            jno short 1417a35a
 *  1417a36b   832d e44c1301 03 sub dword ptr ds:[0x1134ce4],0x3
 *  1417a372   c705 c84c1301 54>mov dword ptr ds:[0x1134cc8],0x8818454
 *  1417a37c  -e9 a25c71ef      jmp 03890023
 *  1417a381   90               nop
 *  1417a382   cc               int3
 */
// Read text from looped address word by word
// Use reverse search to avoid looping issue assume the text is at fixed address.
static void SpecialPSPHookKonami(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  //static LPCSTR lasttext; // this value should be the same for the same game
  static size_t lastsize;

  DWORD eax = regof(eax, esp_base);
  LPCSTR cur = LPCSTR(eax + hp->user_value);
  if (!*cur)
    return;

  LPCSTR text = reverse_search_begin(cur);
  if (!text)
    return;
  //if (lasttext != text) {
  //  lasttext = text;
  //  lastsize = 0; // reset last size
  //}

  size_t size = ::strlen(text);
  if (size == lastsize)
    return;

  *len = lastsize = size;
  *data = (DWORD)text;

  *split = regof(ebx, esp_base); // ecx changes for each character, ebx is an address, edx is stable, but very large
}
bool InsertKonamiPSPHook()
{
  ConsoleOutput("vnreng: KONAMI PSP: enter");
  const BYTE bytes[] =  {
                                         // 14178f73   cc               int3
    0x77, 0x0f,                          // 14178f74   77 0f            ja short 14178f85
    0xc7,0x05, XX8,                      // 14178f76   c705 c84c1301 a4>mov dword ptr ds:[0x1134cc8],0x88129a4
    0xe9, XX4,                           // 14178f80  -e9 7f7071ef      jmp 03890004
    0x8b,0x05, XX4,                      // 14178f85   8b05 c8491301    mov eax,dword ptr ds:[0x11349c8]
    0x81,0xe0, 0xff,0xff,0xff,0x3f,      // 14178f8b   81e0 ffffff3f    and eax,0x3fffffff
    0x0f,0xbe,0xb0, XX4,                 // 14178f91   0fbeb0 00000008  movsx esi,byte ptr ds:[eax+0x8000000] ; jichi: hook here, loop
    0x81,0xfe, 0x40,0x00,0x00,0x00,      // 14178f98   81fe 40000000    cmp esi,0x40
    0x89,0x35 //, XX4,                      // 14178f9e   8935 98491301    mov dword ptr ds:[0x1134998],esi
    //0xc7,0x05, XX4, 0x40,0x00,0x00,0x00,  // 14178fa4   c705 9c491301 40>mov dword ptr ds:[0x113499c],0x40
    //0x0f,0x85, 0x2f,0x00,0x00,0x00,0x00,  // 14178fae   0f85 2f000000    jnz 14178fe3
    //0x8b,0x05, XX4                        // 14178fb4   8b05 c8491301    mov eax,dword ptr ds:[0x11349c8]
  };
  enum { memory_offset = 3 };
  enum { addr_offset = 0x14178f91 - 0x14178f74 };

  DWORD addr = SafeMatchBytesInPSPMemory(bytes, sizeof(bytes));
  if (!addr)
    ConsoleOutput("vnreng: KONAMI PSP: pattern not found");
  else {
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.user_value = *(DWORD *)(hp.address + memory_offset);
    hp.type = USING_STRING|NO_CONTEXT;
    hp.text_fun = SpecialPSPHookKonami;
    ConsoleOutput("vnreng: KONAMI PSP: INSERT");
    NewHook(hp, "KONAMI PSP");
  }

  ConsoleOutput("vnreng: KONAMI PSP: leave");
  return addr;
}

/** 8/9/2014 jichi Kadokawa.co.jp PSP engine, 0.9.8, ?,
 *
 *  Sample game: 未来日�work on 0.9.8, not tested on 0.9.9
 *
 *  FIXME: Currently, only the character name works
 *
 *  Memory address is FIXED.
 *  Debug method: predict and breakpoint the memory address
 *
 *  There are two matches in the memory, and only one function accessing them.
 *
 *  Character name function is as follows.
 *  The scenario is the text after the name.
 *
 *  1348d79f   cc               int3
 *  1348d7a0   77 0f            ja short 1348d7b1
 *  1348d7a2   c705 a8aa1001 fc>mov dword ptr ds:[0x110aaa8],0x884c6fc
 *  1348d7ac  -e9 532844f0      jmp 038d0004
 *  1348d7b1   8b05 78a71001    mov eax,dword ptr ds:[0x110a778]
 *  1348d7b7   81e0 ffffff3f    and eax,0x3fffffff
 *  1348d7bd   0fb6b0 00004007  movzx esi,byte ptr ds:[eax+0x7400000] ; jichi: hook here
 *  1348d7c4   81fe 00000000    cmp esi,0x0
 *  1348d7ca   8935 70a71001    mov dword ptr ds:[0x110a770],esi
 *  1348d7d0   0f85 2f000000    jnz 1348d805
 *  1348d7d6   8b05 7ca71001    mov eax,dword ptr ds:[0x110a77c]
 *  1348d7dc   81e0 ffffff3f    and eax,0x3fffffff
 *  1348d7e2   0fbeb0 00004007  movsx esi,byte ptr ds:[eax+0x7400000]
 *  1348d7e9   8935 70a71001    mov dword ptr ds:[0x110a770],esi
 *  1348d7ef   832d c4aa1001 03 sub dword ptr ds:[0x110aac4],0x3
 *  1348d7f6   c705 a8aa1001 5c>mov dword ptr ds:[0x110aaa8],0x884c75c
 *  1348d800  -e9 1e2844f0      jmp 038d0023
 *  1348d805   832d c4aa1001 03 sub dword ptr ds:[0x110aac4],0x3
 *  1348d80c   e9 0b000000      jmp 1348d81c
 *  1348d811   0108             add dword ptr ds:[eax],ecx
 *  1348d813   c78408 e9082844 >mov dword ptr ds:[eax+ecx+0x442808e9],0x>
 *  1348d81e   c705 a8aa1001 08>mov dword ptr ds:[0x110aaa8],0x884c708
 *  1348d828  -e9 d72744f0      jmp 038d0004
 *  1348d82d   8b05 7ca71001    mov eax,dword ptr ds:[0x110a77c]
 *  1348d833   81e0 ffffff3f    and eax,0x3fffffff
 *  1348d839   0fbeb0 00004007  movsx esi,byte ptr ds:[eax+0x7400000]
 *  1348d840   81fe 00000000    cmp esi,0x0
 *  1348d846   8935 88a71001    mov dword ptr ds:[0x110a788],esi
 *  1348d84c   0f85 16000000    jnz 1348d868
 *  1348d852   832d c4aa1001 03 sub dword ptr ds:[0x110aac4],0x3
 *  1348d859   e9 aa030000      jmp 1348dc08
 *  1348d85e   0154c7 84        add dword ptr ds:[edi+eax*8-0x7c],edx
 *  1348d862   08e9             or cl,ch
 *  1348d864   bb 2744f083      mov ebx,0x83f04427
 *  1348d869   2d c4aa1001      sub eax,0x110aac4
 *  1348d86e   03e9             add ebp,ecx
 *  1348d870   0c 00            or al,0x0
 *  1348d872   0000             add byte ptr ds:[eax],al
 *  1348d874   0114c7           add dword ptr ds:[edi+eax*8],edx
 *  1348d877   8408             test byte ptr ds:[eax],cl
 *  1348d879  -e9 a52744f0      jmp 038d0023
 *  1348d87e   90               nop
 *  1348d87f   cc               int3
 *
 *  Scenario function is as follows.
 *  But I am not able to find it at runtime.
 *
 *  13484483   90               nop
 *  13484484   77 0f            ja short 13484495
 *  13484486   c705 a8aa1001 30>mov dword ptr ds:[0x110aaa8],0x884b030
 *  13484490  -e9 6fbb59f3      jmp 06a20004
 *  13484495   8b35 74a71001    mov esi,dword ptr ds:[0x110a774]
 *  1348449b   81fe 00000000    cmp esi,0x0
 *  134844a1   9c               pushfd
 *  134844a2   8bc6             mov eax,esi
 *  134844a4   8b35 84a71001    mov esi,dword ptr ds:[0x110a784]
 *  134844aa   03f0             add esi,eax
 *  134844ac   8935 74a71001    mov dword ptr ds:[0x110a774],esi
 *  134844b2   9d               popfd
 *  134844b3   0f8f 0c000000    jg 134844c5
 *  134844b9   832d c4aa1001 02 sub dword ptr ds:[0x110aac4],0x2
 *  134844c0  ^e9 23b0f9ff      jmp 1341f4e8
 *  134844c5   832d c4aa1001 02 sub dword ptr ds:[0x110aac4],0x2
 *  134844cc   e9 0b000000      jmp 134844dc
 *  134844d1   0138             add dword ptr ds:[eax],edi
 *  134844d3   b0 84            mov al,0x84
 *  134844d5   08e9             or cl,ch
 *  134844d7   48               dec eax
 *  134844d8   bb 59f39077      mov ebx,0x7790f359
 *  134844dd   0fc7             ???                                      ; unknown command
 *  134844df   05 a8aa1001      add eax,0x110aaa8
 *  134844e4   38b0 8408e917    cmp byte ptr ds:[eax+0x17e90884],dh
 *  134844ea   bb 59f38b05      mov ebx,0x58bf359
 *  134844ef  ^7c a7            jl short 13484498
 *  134844f1   1001             adc byte ptr ds:[ecx],al
 *  134844f3   81e0 ffffff3f    and eax,0x3fffffff
 *  134844f9   0fb6b0 00004007  movzx esi,byte ptr ds:[eax+0x7400000] ; jichi: hook here, byte by byte
 *  13484500   8b05 84a71001    mov eax,dword ptr ds:[0x110a784]
 *  13484506   81e0 ffffff3f    and eax,0x3fffffff
 *  1348450c   8bd6             mov edx,esi
 *  1348450e   8890 00004007    mov byte ptr ds:[eax+0x7400000],dl
 *  13484514   8b3d 84a71001    mov edi,dword ptr ds:[0x110a784]
 *  1348451a   8d7f 01          lea edi,dword ptr ds:[edi+0x1]
 *  1348451d   8b2d 7ca71001    mov ebp,dword ptr ds:[0x110a77c]
 *  13484523   8d6d 01          lea ebp,dword ptr ss:[ebp+0x1]
 *  13484526   3b3d 74a71001    cmp edi,dword ptr ds:[0x110a774]
 *  1348452c   8935 70a71001    mov dword ptr ds:[0x110a770],esi
 *  13484532   892d 7ca71001    mov dword ptr ds:[0x110a77c],ebp
 *  13484538   893d 84a71001    mov dword ptr ds:[0x110a784],edi
 *  1348453e   0f84 16000000    je 1348455a
 *  13484544   832d c4aa1001 05 sub dword ptr ds:[0x110aac4],0x5
 *  1348454b  ^e9 8cffffff      jmp 134844dc
 *  13484550   0138             add dword ptr ds:[eax],edi
 *  13484552   b0 84            mov al,0x84
 *  13484554   08e9             or cl,ch
 *  13484556   c9               leave
 *  13484557   ba 59f3832d      mov edx,0x2d83f359
 *  1348455c   c4aa 100105e9    les ebp,fword ptr ds:[edx+0xe9050110]    ; modification of segment register
 *  13484562   0e               push cs
 *  13484563   0000             add byte ptr ds:[eax],al
 *  13484565   0001             add byte ptr ds:[ecx],al
 *  13484567   4c               dec esp
 *  13484568   b0 84            mov al,0x84
 *  1348456a   08e9             or cl,ch
 *  1348456c   b3 ba            mov bl,0xba
 *  1348456e   59               pop ecx
 *  1348456f   f3:              prefix rep:                              ; superfluous prefix
 *  13484570   90               nop
 *  13484571   cc               int3
 *  13484572   cc               int3
 *  13484573   cc               int3
 */
bool InsertKadokawaNamePSPHook()
{
  ConsoleOutput("vnreng: Kadokawa Name PSP: enter");
  const BYTE bytes[] =  {
    0x77, 0x0f,                     // 1348d7a0   77 0f            ja short 1348d7b1
    0xc7,0x05, XX8,                 // 1348d7a2   c705 a8aa1001 fc>mov dword ptr ds:[0x110aaa8],0x884c6fc
    0xe9, XX4,                      // 1348d7ac  -e9 532844f0      jmp 038d0004
    0x8b,0x05, XX4,                 // 1348d7b1   8b05 78a71001    mov eax,dword ptr ds:[0x110a778]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 1348d7b7   81e0 ffffff3f    and eax,0x3fffffff
    0x0f,0xb6,0xb0, XX4,            // 1348d7bd   0fb6b0 00004007  movzx esi,byte ptr ds:[eax+0x7400000] ; jichi: hook here
    0x81,0xfe, 0x00,0x00,0x00,0x00, // 1348d7c4   81fe 00000000    cmp esi,0x0
    0x89,0x35, XX4,                 // 1348d7ca   8935 70a71001    mov dword ptr ds:[0x110a770],esi
    0x0f,0x85, 0x2f,0x00,0x00,0x00, // 1348d7d0   0f85 2f000000    jnz 1348d805
    0x8b,0x05, XX4,                 // 1348d7d6   8b05 7ca71001    mov eax,dword ptr ds:[0x110a77c]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 1348d7dc   81e0 ffffff3f    and eax,0x3fffffff
    0x0f,0xbe,0xb0, XX4,            // 1348d7e2   0fbeb0 00004007  movsx esi,byte ptr ds:[eax+0x7400000]
    0x89,0x35 //, XX4,              // 1348d7e9   8935 70a71001    mov dword ptr ds:[0x110a770],esi
  };
  enum { memory_offset = 3 };
  enum { addr_offset = 0x1348d7bd - 0x1348d7a0 };

  DWORD addr = SafeMatchBytesInPSPMemory(bytes, sizeof(bytes));
  if (!addr)
    ConsoleOutput("vnreng: Kadokawa Name PSP: pattern not found");
  else {
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.user_value = *(DWORD *)(hp.address + memory_offset);
    hp.type = USING_STRING|USING_SPLIT|NO_CONTEXT;
    hp.offset = pusha_eax_off - 4;
    hp.split = pusha_edx_off - 4; // use edx to split repetition
    hp.text_fun = SpecialPSPHook;

    //GROWL_DWORD2(hp.address, hp.user_value);
    ConsoleOutput("vnreng: Kadokawa Name PSP: INSERT");
    NewHook(hp, "Kadokawa Name PSP");
  }

  ConsoleOutput("vnreng: Kadokawa Name PSP: leave");
  return addr;
}

/** 9/5/2014 jichi felistella.co.jp PSP engine, 0.9.8, 0.9.9
 *  Sample game: Summon Night 5 0.9.8/0.9.9
 *
 *  Encoding: utf8
 *  Fixed memory addresses: two matches
 *
 *  Debug method: predict the text and add break-points.
 *
 *  There are two good functions
 *  The second is used as it contains fewer garbage
 *
 *  // Not used
 *  14081173   cc               int3
 *  14081174   77 0f            ja short 14081185
 *  14081176   c705 c84c1301 40>mov dword ptr ds:[0x1134cc8],0x8989540
 *  14081180  -e9 7feef5f3      jmp 07fe0004
 *  14081185   8b35 9c491301    mov esi,dword ptr ds:[0x113499c]
 *  1408118b   8bc6             mov eax,esi
 *  1408118d   81e0 ffffff3f    and eax,0x3fffffff
 *  14081193   0fb6b8 00000008  movzx edi,byte ptr ds:[eax+0x8000000] ; jichi: hook here
 *  1408119a   8bef             mov ebp,edi
 *  1408119c   81e5 80000000    and ebp,0x80
 *  140811a2   8d76 01          lea esi,dword ptr ds:[esi+0x1]
 *  140811a5   81fd 00000000    cmp ebp,0x0
 *  140811ab   c705 90491301 00>mov dword ptr ds:[0x1134990],0x0
 *  140811b5   893d 9c491301    mov dword ptr ds:[0x113499c],edi
 *  140811bb   8935 a0491301    mov dword ptr ds:[0x11349a0],esi
 *  140811c1   892d a4491301    mov dword ptr ds:[0x11349a4],ebp
 *  140811c7   0f85 16000000    jnz 140811e3
 *  140811cd   832d e44c1301 06 sub dword ptr ds:[0x1134ce4],0x6
 *  140811d4   e9 fbf71200      jmp 141b09d4
 *  140811d9   01dc             add esp,ebx
 *  140811db   95               xchg eax,ebp
 *  140811dc   98               cwde
 *  140811dd   08e9             or cl,ch
 *  140811df   40               inc eax
 *
 *  // Used
 *  141be92f   cc               int3
 *  141be930   77 0f            ja short 141be941
 *  141be932   c705 c84c1301 0c>mov dword ptr ds:[0x1134cc8],0x8988f0c
 *  141be93c  -e9 c316e2f3      jmp 07fe0004
 *  141be941   8b35 98491301    mov esi,dword ptr ds:[0x1134998]
 *  141be947   8bc6             mov eax,esi
 *  141be949   81e0 ffffff3f    and eax,0x3fffffff
 *  141be94f   0fb6b8 00000008  movzx edi,byte ptr ds:[eax+0x8000000] ; jichi: hook here
 *  141be956   81ff 00000000    cmp edi,0x0
 *  141be95c   c705 90491301 00>mov dword ptr ds:[0x1134990],0x0
 *  141be966   893d 98491301    mov dword ptr ds:[0x1134998],edi
 *  141be96c   8935 9c491301    mov dword ptr ds:[0x113499c],esi
 *  141be972   0f85 16000000    jnz 141be98e
 *  141be978   832d e44c1301 04 sub dword ptr ds:[0x1134ce4],0x4
 *  141be97f   e9 e4020000      jmp 141bec68
 *  141be984   01748f 98        add dword ptr ds:[edi+ecx*4-0x68],esi
 *  141be988   08e9             or cl,ch
 *  141be98a   95               xchg eax,ebp
 *  141be98b   16               push ss
 *  141be98c  ^e2 f3            loopd short 141be981
 *  141be98e   832d e44c1301 04 sub dword ptr ds:[0x1134ce4],0x4
 *  141be995   e9 0e000000      jmp 141be9a8
 *  141be99a   011c8f           add dword ptr ds:[edi+ecx*4],ebx
 *  141be99d   98               cwde
 *  141be99e   08e9             or cl,ch
 *  141be9a0   7f 16            jg short 141be9b8
 *  141be9a2  ^e2 f3            loopd short 141be997
 *  141be9a4   90               nop
 *  141be9a5   cc               int3
 */
// Only split text when edi is eax
// The value of edi is either eax or 0
static void SpecialPSPHookFelistella(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  DWORD eax = regof(eax, esp_base);
  LPCSTR text = LPCSTR(eax + hp->user_value);
  if (text) {
    *len = ::strlen(text); // utf8
    *data = (DWORD)text;

    DWORD edi = regof(edi, esp_base);
    *split = FIXED_SPLIT_VALUE * (edi == eax ? 4 : 5);
  }
}
bool InsertFelistellaPSPHook()
{
  ConsoleOutput("vnreng: FELISTELLA PSP: enter");
  const BYTE bytes[] =  {
    //0xcc,                              // 141be92f   cc               int3
    0x77, 0x0f,                          // 141be930   77 0f            ja short 141be941
    0xc7,0x05, XX8,                      // 141be932   c705 c84c1301 0c>mov dword ptr ds:[0x1134cc8],0x8988f0c
    0xe9, XX4,                           // 141be93c  -e9 c316e2f3      jmp 07fe0004
    0x8b,0x35, XX4,                      // 141be941   8b35 98491301    mov esi,dword ptr ds:[0x1134998]
    0x8b,0xc6,                           // 141be947   8bc6             mov eax,esi
    0x81,0xe0, 0xff,0xff,0xff,0x3f,      // 141be949   81e0 ffffff3f    and eax,0x3fffffff
    0x0f,0xb6,0xb8, XX4,                 // 141be94f   0fb6b8 00000008  movzx edi,byte ptr ds:[eax+0x8000000] ; jichi: hook here
    0x81,0xff, 0x00,0x00,0x00,0x00,      // 141be956   81ff 00000000    cmp edi,0x0
    0xc7,0x05, XX4, 0x00,0x00,0x00,0x00, // 141be95c   c705 90491301 00>mov dword ptr ds:[0x1134990],0x0
    0x89,0x3d, XX4,                      // 141be966   893d 98491301    mov dword ptr ds:[0x1134998],edi
    0x89,0x35, XX4,                      // 141be96c   8935 9c491301    mov dword ptr ds:[0x113499c],esi
    0x0f,0x85, XX4,                      // 141be972   0f85 16000000    jnz 141be98e
    0x83,0x2d, XX4, 0x04,                // 141be978   832d e44c1301 04 sub dword ptr ds:[0x1134ce4],0x4
    // Above is not sufficient
    0xe9, XX4,                           // 141be97f   e9 e4020000      jmp 141bec68
    0x01,0x74,0x8f, 0x98                 // 141be984   01748f 98        add dword ptr ds:[edi+ecx*4-0x68],esi
    //0x08,0xe9,                         // 141be988   08e9             or cl,ch
    // Below could be changed for different run
    //0x95,                              // 141be98a   95               xchg eax,ebp
    //0x16                               // 141be98b   16               push ss
  };
  enum { memory_offset = 3 };
  enum { addr_offset = 0x141be94f - 0x141be930 };

  DWORD addr = SafeMatchBytesInPSPMemory(bytes, sizeof(bytes));
  //GROWL_DWORD(addr);
  if (!addr)
    ConsoleOutput("vnreng: FELISTELLA PSP: pattern not found");
  else {
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.user_value = *(DWORD *)(hp.address + memory_offset);
    hp.type = USING_STRING|USING_UTF8|USING_SPLIT|NO_CONTEXT; // Fix the split value to merge all threads
    //hp.text_fun = SpecialPSPHook;
    hp.text_fun = SpecialPSPHookFelistella;
    hp.offset = pusha_eax_off - 4;
    //hp.split = pusha_ecx_off - 4; // cause main thread to split
    //hp.split = pusha_edx_off - 4; // cause main thread to split for different lines
    ConsoleOutput("vnreng: FELISTELLA PSP: INSERT");
    NewHook(hp, "FELISTELLA PSP");
  }

  ConsoleOutput("vnreng: FELISTELLA PSP: leave");
  return addr;
}


#if 0 // 8/9/2014 jichi: does not work

bool InsertKadokawaPSPHook()
{
  ConsoleOutput("vnreng: Kadokawa PSP: enter");
  const BYTE bytes[] =  {
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 134844f3   81e0 ffffff3f    and eax,0x3fffffff
    0x0f,0xb6,0xb0, XX4,            // 134844f9   0fb6b0 00004007  movzx esi,byte ptr ds:[eax+0x7400000] ; jichi: hook here, byte by byte
    0x8b,0x05, XX4,                 // 13484500   8b05 84a71001    mov eax,dword ptr ds:[0x110a784]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 13484506   81e0 ffffff3f    and eax,0x3fffffff
    0x8b,0xd6,                      // 1348450c   8bd6             mov edx,esi
    0x88,0x90, XX4,                 // 1348450e   8890 00004007    mov byte ptr ds:[eax+0x7400000],dl
    0x8b,0x3d, XX4,                 // 13484514   8b3d 84a71001    mov edi,dword ptr ds:[0x110a784]
    0x8d,0x7f, 0x01,                // 1348451a   8d7f 01          lea edi,dword ptr ds:[edi+0x1]
    0x8b,0x2d, XX4,                 // 1348451d   8b2d 7ca71001    mov ebp,dword ptr ds:[0x110a77c]
    0x8d,0x6d, 0x01,                // 13484523   8d6d 01          lea ebp,dword ptr ss:[ebp+0x1]
    0x3b,0x3d, XX4,                 // 13484526   3b3d 74a71001    cmp edi,dword ptr ds:[0x110a774]
    0x89,0x35, XX4,                 // 1348452c   8935 70a71001    mov dword ptr ds:[0x110a770],esi
    0x89,0x2d, XX4,                 // 13484532   892d 7ca71001    mov dword ptr ds:[0x110a77c],ebp
    0x89,0x3d, XX4,                 // 13484538   893d 84a71001    mov dword ptr ds:[0x110a784],edi
    // Above is not sufficient
    //0x0f,0x84, XX4,                 // 1348453e   0f84 16000000    je 1348455a
    //0x83,0x2d, XX4, 0x05,           // 13484544   832d c4aa1001 05 sub dword ptr ds:[0x110aac4],0x5
    //0xe9, XX4,                      // 1348454b  ^e9 8cffffff      jmp 134844dc
    //0x01,0x38,                      // 13484550   0138             add dword ptr ds:[eax],edi
    //0xb0, 0x84,                     // 13484552   b0 84            mov al,0x84
    //0x08,0xe9                       // 13484554   08e9             or cl,ch
    // Below will change at runtime
  };
  enum { memory_offset = 3 };
  enum { addr_offset = 0x134844f9 - 0x134844f3 };

  DWORD addr = SafeMatchBytesInPSPMemory(bytes, sizeof(bytes));
  if (!addr) {
    ConsoleOutput("vnreng: Kadokawa PSP: pattern not found");
    return false;
  }
  addr = SafeMatchBytesInPSPMemory(bytes, sizeof(bytes), addr);
  addr = SafeMatchBytesInPSPMemory(bytes, sizeof(bytes), addr);

  if (!addr)
    ConsoleOutput("vnreng: Kadokawa PSP: pattern not found");
  else {
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.user_value = *(DWORD *)(hp.address + memory_offset);
    hp.type = USING_STRING|USING_SPLIT|NO_CONTEXT;
    hp.offset = pusha_eax_off - 4;
    hp.split = pusha_ecx_off - 4; // use edx to split repetition
    hp.length_offset = 1; // byte by byte
    hp.text_fun = SpecialPSPHook;

    //GROWL_DWORD2(hp.address, hp.user_value);
    ConsoleOutput("vnreng: Kadokawa PSP: INSERT");
    NewHook(hp, "Kadokawa PSP");
  }

  ConsoleOutput("vnreng: Kadokawa PSP: leave");
  return addr;
}
#endif // 0

#if 0 // 8/9/2014 jichi: cannot find a good function

/** 8/9/2014 jichi Typemoon.com PSP engine, 0.9.8, 0.9.9,
 *
 *  Sample game: Fate CCC
 *  This game is made by both TYPE-MOON and Imageepoch
 *  But the encoding is SHIFT-JIS than UTF-8 like other Imageepoch games.
 *  Otomate hook will produce significant amount of garbage.
 *
 *  Memory address is FIXED.
 *  There are two matches in the memory.
 *
 *  Debug method: breakpoint the memory address
 *  The hooked functions were looping which made it difficult to debug.
 *
 *  Two looped functions are as follows. The first one is used
 *  The second function is tested as bad.
 *
 *  Registers: (all of them are fixed except eax)
 *  EAX 08C91373
 *  ECX 00000016
 *  EDX 00000012
 *  EBX 0027A580
 *  ESP 0353E6D0
 *  EBP 0000000B
 *  ESI 0000001E
 *  EDI 00000001
 *  EIP 1351E14D
 *
 *  1351e12d   f0:90            lock nop                                 ; lock prefix is not allowed
 *  1351e12f   cc               int3
 *  1351e130   77 0f            ja short 1351e141
 *  1351e132   c705 a8aa1001 b8>mov dword ptr ds:[0x110aaa8],0x88ed7b8
 *  1351e13c  -e9 c31e27f0      jmp 03790004
 *  1351e141   8b05 aca71001    mov eax,dword ptr ds:[0x110a7ac]
 *  1351e147   81e0 ffffff3f    and eax,0x3fffffff
 *  1351e14d   0fbeb0 01004007  movsx esi,byte ptr ds:[eax+0x7400001] ; or jichi: hook here
 *  1351e154   8b05 dca71001    mov eax,dword ptr ds:[0x110a7dc]
 *  1351e15a   81e0 ffffff3f    and eax,0x3fffffff
 *  1351e160   8bb8 50004007    mov edi,dword ptr ds:[eax+0x7400050]
 *  1351e166   81e6 ff000000    and esi,0xff
 *  1351e16c   8bc6             mov eax,esi
 *  1351e16e   8b35 a8a71001    mov esi,dword ptr ds:[0x110a7a8]
 *  1351e174   0bf0             or esi,eax
 *  1351e176   c1e6 10          shl esi,0x10
 *  1351e179   c1fe 10          sar esi,0x10
 *  1351e17c   893d 78a71001    mov dword ptr ds:[0x110a778],edi
 *  1351e182   8935 7ca71001    mov dword ptr ds:[0x110a77c],esi
 *  1351e188   c705 e4a71001 d4>mov dword ptr ds:[0x110a7e4],0x88ed7d4
 *  1351e192   832d c4aa1001 07 sub dword ptr ds:[0x110aac4],0x7
 *  1351e199   e9 0e000000      jmp 1351e1ac
 *  1351e19e   01ac3e 8e08e97b  add dword ptr ds:[esi+edi+0x7be9088e],eb>
 *  1351e1a5   1e               push ds
 *  1351e1a6   27               daa
 *  1351e1a7   f0:90            lock nop                                 ; lock prefix is not allowed
 *  1351e1a9   cc               int3
 *
 *  13513f23   cc               int3
 *  13513f24   77 0f            ja short 13513f35
 *  13513f26   c705 a8aa1001 d4>mov dword ptr ds:[0x110aaa8],0x88e7bd4
 *  13513f30  -e9 cfc027f0      jmp 03790004
 *  13513f35   8b05 7ca71001    mov eax,dword ptr ds:[0x110a77c]
 *  13513f3b   81e0 ffffff3f    and eax,0x3fffffff
 *  13513f41   0fbeb0 00004007  movsx esi,byte ptr ds:[eax+0x7400000]
 *  13513f48   8b3d 84a71001    mov edi,dword ptr ds:[0x110a784]
 *  13513f4e   8d7f 01          lea edi,dword ptr ds:[edi+0x1]
 *  13513f51   8b05 78a71001    mov eax,dword ptr ds:[0x110a778]
 *  13513f57   81e0 ffffff3f    and eax,0x3fffffff
 *  13513f5d   8bd6             mov edx,esi
 *  13513f5f   8890 00004007    mov byte ptr ds:[eax+0x7400000],dl ; jichi: bad hook
 *  13513f65   8b2d 78a71001    mov ebp,dword ptr ds:[0x110a778]
 *  13513f6b   8d6d 01          lea ebp,dword ptr ss:[ebp+0x1]
 *  13513f6e   33c0             xor eax,eax
 *  13513f70   3b3d 80a71001    cmp edi,dword ptr ds:[0x110a780]
 *  13513f76   0f9cc0           setl al
 *  13513f79   8bf0             mov esi,eax
 *  13513f7b   8b15 7ca71001    mov edx,dword ptr ds:[0x110a77c]
 *  13513f81   8d52 01          lea edx,dword ptr ds:[edx+0x1]
 *  13513f84   81fe 00000000    cmp esi,0x0
 *  13513f8a   892d 78a71001    mov dword ptr ds:[0x110a778],ebp
 *  13513f90   8915 7ca71001    mov dword ptr ds:[0x110a77c],edx
 *  13513f96   893d 84a71001    mov dword ptr ds:[0x110a784],edi
 *  13513f9c   8935 88a71001    mov dword ptr ds:[0x110a788],esi
 *  13513fa2   0f84 16000000    je 13513fbe
 *  13513fa8   832d c4aa1001 07 sub dword ptr ds:[0x110aac4],0x7
 *  13513faf  ^e9 70ffffff      jmp 13513f24
 *  13513fb4   01d4             add esp,edx
 *  13513fb6   7b 8e            jpo short 13513f46
 *  13513fb8   08e9             or cl,ch
 *  13513fba   65:c027 f0       shl byte ptr gs:[edi],0xf0               ; shift constant out of range 1..31
 *  13513fbe   832d c4aa1001 07 sub dword ptr ds:[0x110aac4],0x7
 *  13513fc5   e9 0e000000      jmp 13513fd8
 *  13513fca   01f0             add eax,esi
 *  13513fcc   7b 8e            jpo short 13513f5c
 *  13513fce   08e9             or cl,ch
 *  13513fd0   4f               dec edi
 *  13513fd1   c027 f0          shl byte ptr ds:[edi],0xf0               ; shift constant out of range 1..31
 *  13513fd4   90               nop
 *  13513fd5   cc               int3
 *  13513fd6   cc               int3
 */
// Read text from dl
static void SpecialPSPHookTypeMoon(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  DWORD eax = regof(eax, esp_base);
  DWORD text = eax + hp->user_value - 1; // the text is in the previous byte
  if (BYTE c = *(BYTE *)text) { // unsigned char
    *data = text;
    *len = ::LeadByteTable[c]; // 1 or 2
    //*split = regof(ecx, esp_base);
    //*split = regof(edx, esp_base);
    *split = regof(ebx, esp_base);
  }
}
bool InsertTypeMoonPSPHook()
{
  ConsoleOutput("vnreng: TypeMoon PSP: enter");
  const BYTE bytes[] =  {
    0x77, 0x0f,                     // 1351e130   77 0f            ja short 1351e141
    0xc7,0x05, XX8,                 // 1351e132   c705 a8aa1001 b8>mov dword ptr ds:[0x110aaa8],0x88ed7b8
    0xe9, XX4,                      // 1351e13c  -e9 c31e27f0      jmp 03790004
    0x8b,0x05, XX4,                 // 1351e141   8b05 aca71001    mov eax,dword ptr ds:[0x110a7ac]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 1351e147   81e0 ffffff3f    and eax,0x3fffffff
    0x0f,0xbe,0xb0, XX4,            // 1351e14d   0fbeb0 01004007  movsx esi,byte ptr ds:[eax+0x7400001] ;  jichi: hook here
    0x8b,0x05, XX4,                 // 1351e154   8b05 dca71001    mov eax,dword ptr ds:[0x110a7dc]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 1351e15a   81e0 ffffff3f    and eax,0x3fffffff
    0x8b,0xb8, XX4,                 // 1351e160   8bb8 50004007    mov edi,dword ptr ds:[eax+0x7400050]
    0x81,0xe6, 0xff,0x00,0x00,0x00, // 1351e166   81e6 ff000000    and esi,0xff
    0x8b,0xc6,                      // 1351e16c   8bc6             mov eax,esi
    0x8b,0x35, XX4,                 // 1351e16e   8b35 a8a71001    mov esi,dword ptr ds:[0x110a7a8]
    0x0b,0xf0,                      // 1351e174   0bf0             or esi,eax
    0xc1,0xe6, 0x10,                // 1351e176   c1e6 10          shl esi,0x10
    0xc1,0xfe, 0x10                 // 1351e179   c1fe 10          sar esi,0x10
  };
  enum { memory_offset = 3 };
  enum { addr_offset = 0x1351e14d - 0x1351e130 };

  DWORD addr = SafeMatchBytesInPSPMemory(bytes, sizeof(bytes));
  if (!addr)
    ConsoleOutput("vnreng: TypeMoon PSP: pattern not found");
  else {
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.user_value = *(DWORD *)(hp.address + memory_offset);
    hp.type = USING_STRING|NO_CONTEXT;
    hp.text_fun = SpecialPSPHookTypeMoon;
    ConsoleOutput("vnreng: TypeMoon PSP: INSERT");
    NewHook(hp, "TypeMoon PSP");
  }

  ConsoleOutput("vnreng: TypeMoon PSP: leave");
  return addr;
}

#endif // 0

#if 0 // 7/25/2014: This function is not invoked? Why?
/** 7/22/2014 jichi: KOEI TECMO PSP, 0.9.8
 *  Sample game: 金色のコルダ3
 *
 *  134598e2   cc               int3
 *  134598e3   cc               int3
 *  134598e4   77 0f            ja short 134598f5
 *  134598e6   c705 a8aa1001 8c>mov dword ptr ds:[0x110aaa8],0x880f08c
 *  134598f0  -e9 0f67fbef      jmp 03410004
 *  134598f5   8b05 7ca71001    mov eax,dword ptr ds:[0x110a77c]
 *  134598fb   81e0 ffffff3f    and eax,0x3fffffff
 *  13459901   8bb0 00004007    mov esi,dword ptr ds:[eax+0x7400000] ; jichi: hook here
 *  13459907   8b3d 7ca71001    mov edi,dword ptr ds:[0x110a77c]
 *  1345990d   8d7f 04          lea edi,dword ptr ds:[edi+0x4]
 *  13459910   8b05 84a71001    mov eax,dword ptr ds:[0x110a784]
 *  13459916   81e0 ffffff3f    and eax,0x3fffffff
 *  1345991c   89b0 00004007    mov dword ptr ds:[eax+0x7400000],esi
 *  13459922   8b2d 84a71001    mov ebp,dword ptr ds:[0x110a784]
 *  13459928   8d6d 04          lea ebp,dword ptr ss:[ebp+0x4]
 *  1345992b   8b15 78a71001    mov edx,dword ptr ds:[0x110a778]
 *  13459931   81fa 01000000    cmp edx,0x1
 *  13459937   8935 70a71001    mov dword ptr ds:[0x110a770],esi
 *  1345993d   893d 7ca71001    mov dword ptr ds:[0x110a77c],edi
 *  13459943   892d 84a71001    mov dword ptr ds:[0x110a784],ebp
 *  13459949   c705 88a71001 01>mov dword ptr ds:[0x110a788],0x1
 *  13459953   0f84 16000000    je 1345996f
 *  13459959   832d c4aa1001 09 sub dword ptr ds:[0x110aac4],0x9
 *  13459960   e9 17000000      jmp 1345997c
 *  13459965   0190 f08008e9    add dword ptr ds:[eax+0xe90880f0],edx
 *  1345996b   b4 66            mov ah,0x66
 *  1345996d   fb               sti
 *  1345996e   ef               out dx,eax                               ; i/o command
 *  1345996f   832d c4aa1001 09 sub dword ptr ds:[0x110aac4],0x9
 *  13459976  ^e9 ddc1ffff      jmp 13455b58
 *  1345997b   90               nop
 */
bool InsertTecmoPSPHook()
{
  ConsoleOutput("vnreng: Tecmo PSP: enter");

  const BYTE bytes[] =  {
    0x77, 0x0f,                     // 134598e4   77 0f            ja short 134598f5
    0xc7,0x05, XX8,                 // 134598e6   c705 a8aa1001 8c>mov dword ptr ds:[0x110aaa8],0x880f08c
    0xe9, XX4,                      // 134598f0  -e9 0f67fbef      jmp 03410004
    0x8b,0x05, XX4,                 // 134598f5   8b05 7ca71001    mov eax,dword ptr ds:[0x110a77c]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 134598fb   81e0 ffffff3f    and eax,0x3fffffff
    0x8b,0xb0, XX4,                 // 13459901   8bb0 00004007    mov esi,dword ptr ds:[eax+0x7400000] ; jichi: hook here
    0x8b,0x3d, XX4,                 // 13459907   8b3d 7ca71001    mov edi,dword ptr ds:[0x110a77c]
    0x8d,0x7f, 0x04,                // 1345990d   8d7f 04          lea edi,dword ptr ds:[edi+0x4]
    0x8b,0x05, XX4,                 // 13459910   8b05 84a71001    mov eax,dword ptr ds:[0x110a784]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 13459916   81e0 ffffff3f    and eax,0x3fffffff
    0x89,0xb0 //, XX4,                 // 1345991c   89b0 00004007    mov dword ptr ds:[eax+0x7400000],esi
    //0x8b,0x2d, XX4,                 // 13459922   8b2d 84a71001    mov ebp,dword ptr ds:[0x110a784]
    //0x8d,0x6d, 0x04,                // 13459928   8d6d 04          lea ebp,dword ptr ss:[ebp+0x4]
    //0x8b,0x15, XX4,                 // 1345992b   8b15 78a71001    mov edx,dword ptr ds:[0x110a778]
    //0x81,0xfa, 0x01,0x00,0x00,0x00  // 13459931   81fa 01000000    cmp edx,0x1
  };
  enum { memory_offset = 2 };
  enum { addr_offset = 0x13459901 - 0x134598e4 };

  DWORD addr = SafeMatchBytesInPSPMemory(bytes, sizeof(bytes));
  if (!addr)
    ConsoleOutput("vnreng: Tecmo PSP: pattern not found");
  else {
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.user_value = *(DWORD *)(hp.address + memory_offset);
    hp.type = USING_STRING|USING_SPLIT|NO_CONTEXT;
    hp.offset = pusha_eax_off - 4;
    hp.split = pusha_ecx_off - 4;
    hp.text_fun = SpecialPSPHook;
    ConsoleOutput("vnreng: Tecmo PSP: INSERT");
    NewHook(hp, "Tecmo PSP");
  }

  ConsoleOutput("vnreng: Tecmo PSP: leave");
  return addr;
}
#endif // 0

/** jichi 7/19/2014 PCSX2
 *  Tested wit  pcsx2-v1.2.1-328-gef0e3fe-windows-x86, built at http://buildbot.orphis.net/pcsx2
 */
bool InsertPCSX2Hooks()
{
  // TODO: Add generic hooks
  return InsertTypeMoonPS2Hook()
      || InsertMarvelousPS2Hook()
      || InsertMarvelous2PS2Hook();
}

/** 7/19/2014 jichi
 *  Tested game: Fate/stay night [Realta Nua]
 *
 *  Fixed memory address.
 *  Text is incrementally increased.
 *
 *  Debug method: Debug next text location at \0.
 *  There are three locations that are OK to hook.
 *  The first one is used.
 *
 *  Runtime stack:
 *  0dc1f7e0   055be7c0
 *  0dc1f7e4   023105b0  pcsx2.023105b0
 *  0dc1f7e8   0dc1f804
 *  0dc1f7ec   023a406b  pcsx2.023a406b
 *  0dc1f7f0   00000000
 *  0dc1f7f4   000027e5
 *
 *  305a5424   2b05 809e9500    sub eax,dword ptr ds:[0x959e80]
 *  305a542a   0f88 05000000    js 305a5435
 *  305a5430  -e9 cbebdfd1      jmp pcsx2.023a4000
 *  305a5435   8b0d 20ac9600    mov ecx,dword ptr ds:[0x96ac20]
 *  305a543b   89c8             mov eax,ecx
 *  305a543d   c1e8 0c          shr eax,0xc
 *  305a5440   8b0485 30009e12  mov eax,dword ptr ds:[eax*4+0x129e0030]
 *  305a5447   bb 57545a30      mov ebx,0x305a5457
 *  305a544c   01c1             add ecx,eax
 *  305a544e  -0f88 ecbcd7d1    js pcsx2.02321140
 *  305a5454   0fbe01           movsx eax,byte ptr ds:[ecx] ; jichi: hook here
 *  305a5457   99               cdq
 *  305a5458   a3 f0ab9600      mov dword ptr ds:[0x96abf0],eax
 *  305a545d   8915 f4ab9600    mov dword ptr ds:[0x96abf4],edx
 *  305a5463   a1 40ac9600      mov eax,dword ptr ds:[0x96ac40]
 *  305a5468   3b05 f0ab9600    cmp eax,dword ptr ds:[0x96abf0]
 *  305a546e   75 11            jnz short 305a5481
 *  305a5470   a1 44ac9600      mov eax,dword ptr ds:[0x96ac44]
 *  305a5475   3b05 f4ab9600    cmp eax,dword ptr ds:[0x96abf4]
 *  305a547b   0f84 3a000000    je 305a54bb
 *  305a5481   8305 00ac9600 24 add dword ptr ds:[0x96ac00],0x24
 *  305a5488   9f               lahf
 *  305a5489   66:c1f8 0f       sar ax,0xf
 *  305a548d   98               cwde
 *  305a548e   a3 04ac9600      mov dword ptr ds:[0x96ac04],eax
 *  305a5493   c705 a8ad9600 6c>mov dword ptr ds:[0x96ada8],0x10e26c
 *  305a549d   a1 c0ae9600      mov eax,dword ptr ds:[0x96aec0]
 *  305a54a2   83c0 04          add eax,0x4
 *
 *  3038c78e  -0f88 ac4af9d1    js pcsx2.02321240
 *  3038c794   8911             mov dword ptr ds:[ecx],edx
 *  3038c796   8b0d 60ab9600    mov ecx,dword ptr ds:[0x96ab60]
 *  3038c79c   89c8             mov eax,ecx
 *  3038c79e   c1e8 0c          shr eax,0xc
 *  3038c7a1   8b0485 30009e12  mov eax,dword ptr ds:[eax*4+0x129e0030]
 *  3038c7a8   bb b8c73830      mov ebx,0x3038c7b8
 *  3038c7ad   01c1             add ecx,eax
 *  3038c7af  -0f88 8b49f9d1    js pcsx2.02321140
 *  3038c7b5   0fbe01           movsx eax,byte ptr ds:[ecx] ; jichi: or hook here
 *  3038c7b8   99               cdq
 *  3038c7b9   a3 e0ab9600      mov dword ptr ds:[0x96abe0],eax
 *  3038c7be   8915 e4ab9600    mov dword ptr ds:[0x96abe4],edx
 *  3038c7c4   c705 20ab9600 00>mov dword ptr ds:[0x96ab20],0x0
 *  3038c7ce   c705 24ab9600 00>mov dword ptr ds:[0x96ab24],0x0
 *  3038c7d8   c705 f0ab9600 25>mov dword ptr ds:[0x96abf0],0x25
 *  3038c7e2   c705 f4ab9600 00>mov dword ptr ds:[0x96abf4],0x0
 *  3038c7ec   833d e0ab9600 25 cmp dword ptr ds:[0x96abe0],0x25
 *  3038c7f3   75 0d            jnz short 3038c802
 *  3038c7f5   833d e4ab9600 00 cmp dword ptr ds:[0x96abe4],0x0
 *  3038c7fc   0f84 34000000    je 3038c836
 *  3038c802   31c0             xor eax,eax
 *
 *  304e1a0a   8b0d 40ab9600    mov ecx,dword ptr ds:[0x96ab40]
 *  304e1a10   89c8             mov eax,ecx
 *  304e1a12   c1e8 0c          shr eax,0xc
 *  304e1a15   8b0485 30009e12  mov eax,dword ptr ds:[eax*4+0x129e0030]
 *  304e1a1c   bb 2c1a4e30      mov ebx,0x304e1a2c
 *  304e1a21   01c1             add ecx,eax
 *  304e1a23  -0f88 17f7e3d1    js pcsx2.02321140
 *  304e1a29   0fbe01           movsx eax,byte ptr ds:[ecx] ; jichi: or hook here
 *  304e1a2c   99               cdq
 *  304e1a2d   a3 f0ab9600      mov dword ptr ds:[0x96abf0],eax
 *  304e1a32   8915 f4ab9600    mov dword ptr ds:[0x96abf4],edx
 *  304e1a38   a1 f0ab9600      mov eax,dword ptr ds:[0x96abf0]
 *  304e1a3d   3b05 d0ab9600    cmp eax,dword ptr ds:[0x96abd0]
 *  304e1a43   75 11            jnz short 304e1a56
 *  304e1a45   a1 f4ab9600      mov eax,dword ptr ds:[0x96abf4]
 *  304e1a4a   3b05 d4ab9600    cmp eax,dword ptr ds:[0x96abd4]
 *  304e1a50   0f84 3c000000    je 304e1a92
 *  304e1a56   a1 f0ab9600      mov eax,dword ptr ds:[0x96abf0]
 *  304e1a5b   83c0 d0          add eax,-0x30
 *  304e1a5e   99               cdq
 */
namespace { // unnamed
bool _typemoongarbage_ch(char c)
{
  return c == '%' || c == '.' || c == ' ' || c == ','
      || c >= '0' && c <= '9'
      || c >= 'A' && c <= 'z'; // also ignore ASCII 91-96: [ \ ] ^ _ `
}

// Trim leading garbage
LPCSTR _typemoonltrim(LPCSTR p)
{
  enum { MAX_LENGTH = VNR_TEXT_CAPACITY };
  if (p && p[0] == '%')
    for (int count = 0; *p && count < MAX_LENGTH; count++, p++)
      if (!_typemoongarbage_ch(*p))
        return p;
  return nullptr;
}

// Remove trailing garbage such as %n
size_t _typemoonstrlen(LPCSTR text)
{
  size_t len = ::strlen(text);
  size_t ret = len;
  while (len && _typemoongarbage_ch(text[len - 1])) {
    len--;
    if (text[len] == '%')
      ret = len;
  }
  return ret;
}

} // unnamed namespace

// Use last text size to determine
static void SpecialPS2HookTypeMoon(DWORD esp_base, HookParam *, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  static LPCSTR lasttext; // this value should be the same for the same game
  static size_t lastsize;

  LPCSTR cur = LPCSTR(regof(ecx, esp_base));
  if (!*cur)
    return;

  LPCSTR text = reverse_search_begin(cur);
  if (!text)
    return;
  //text = _typemoonltrim(text);
  if (lasttext != text) {
    lasttext = text;
    lastsize = 0; // reset last size
  }

  size_t size = ::strlen(text);
  if (size == lastsize)
    return;
  if (size > lastsize) // incremental
    text += lastsize;
  lastsize = size;

  text = _typemoonltrim(text);
  size = _typemoonstrlen(text);
  //size = ::strlen(text);

  *data = (DWORD)text;
  *len = size;

  *split = FIXED_SPLIT_VALUE << 2; // merge all threads
  //*split = *(DWORD *)(esp_base + 4); // use [esp+4] as split
  //*split = regof(eax, esp_base);
  //*split = regof(esi, esp_base);
}

bool InsertTypeMoonPS2Hook()
{
  ConsoleOutput("vnreng: TypeMoon PS2: enter");
  const BYTE bytes[] =  {
    0x2b,0x05, XX4,       // 305a5424   2b05 809e9500    sub eax,dword ptr ds:[0x959e80]
    0x0f,0x88, 0x05,0x00,0x00,0x00, // 305a542a   0f88 05000000    js 305a5435
    0xe9, XX4,            // 305a5430  -e9 cbebdfd1      jmp pcsx2.023a4000
    0x8b,0x0d, XX4,       // 305a5435   8b0d 20ac9600    mov ecx,dword ptr ds:[0x96ac20]
    0x89,0xc8,            // 305a543b   89c8             mov eax,ecx
    0xc1,0xe8, 0x0c,      // 305a543d   c1e8 0c          shr eax,0xc
    0x8b,0x04,0x85, XX4,  // 305a5440   8b0485 30009e12  mov eax,dword ptr ds:[eax*4+0x129e0030]
    0xbb, XX4,            // 305a5447   bb 57545a30      mov ebx,0x305a5457
    0x01,0xc1,            // 305a544c   01c1             add ecx,eax
    // Following pattern is not sufficient
    0x0f,0x88, XX4,       // 305a544e  -0f88 ecbcd7d1    js pcsx2.02321140
    0x0f,0xbe,0x01,       // 305a5454   0fbe01           movsx eax,byte ptr ds:[ecx] ; jichi: hook here
    0x99,                 // 305a5457   99               cdq
    0xa3, XX4,            // 305a5458   a3 f0ab9600      mov dword ptr ds:[0x96abf0],eax
    0x89,0x15, XX4,       // 305a545d   8915 f4ab9600    mov dword ptr ds:[0x96abf4],edx
    0xa1, XX4,            // 305a5463   a1 40ac9600      mov eax,dword ptr ds:[0x96ac40]
    0x3b,0x05, XX4,       // 305a5468   3b05 f0ab9600    cmp eax,dword ptr ds:[0x96abf0]
    0x75, 0x11,           // 305a546e   75 11            jnz short 305a5481
    0xa1, XX4,            // 305a5470   a1 44ac9600      mov eax,dword ptr ds:[0x96ac44]
    0x3b,0x05, XX4,       // 305a5475   3b05 f4ab9600    cmp eax,dword ptr ds:[0x96abf4]
    0x0f,0x84, XX4,       // 305a547b   0f84 3a000000    je 305a54bb
    0x83,0x05, XX4, 0x24, // 305a5481   8305 00ac9600 24 add dword ptr ds:[0x96ac00],0x24
    0x9f,                 // 305a5488   9f               lahf
    0x66,0xc1,0xf8, 0x0f, // 305a5489   66:c1f8 0f       sar ax,0xf
    0x98                  // 305a548d   98               cwde
  };
  enum { addr_offset = 0x305a5454 - 0x305a5424 };

  DWORD addr = SafeMatchBytesInPS2Memory(bytes, sizeof(bytes));
  //addr = 0x30403967;
  if (!addr)
    ConsoleOutput("vnreng: TypeMoon PS2: pattern not found");
  else {
    //GROWL_DWORD(addr + addr_offset);
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.type = USING_STRING|NO_CONTEXT; // no context to get rid of return address
    hp.text_fun = SpecialPS2HookTypeMoon;
    //hp.offset = pusha_ecx_off - 4; // ecx, get text in ds:[ecx]
    //hp.length_offset = 1;
    ConsoleOutput("vnreng: TypeMoon PS2: INSERT");
    //GROWL_DWORD(hp.address);
    NewHook(hp, "TypeMoon PS2");
  }

  ConsoleOutput("vnreng: TypeMoon PS2: leave");
  return addr;
}

/** 8/3/2014 jichi
 *  Tested game: School Rumble ねる娘�育つ
 *
 *  Fixed memory address.
 *  There is only one matched address.
 *
 *  Debug method: Predict text location.
 *  There are a couple of locations that are OK to hook.
 *  The last one is used.
 *
 *  Issue: the order of chara and scenario is reversed: 「scenario」chara
 *
 *  eax 20000000
 *  ecx 202d5ab3
 *  edx 00000000
 *  ebx 3026e299
 *  esp 0c14f910
 *  ebp 0c14f918
 *  esi 0014f470
 *  edi 00000000
 *  eip 3026e296
 *
 *  3026e1d5  -0f88 a530d7d2    js pcsx2.02fe1280
 *  3026e1db   0f1202           movlps xmm0,qword ptr ds:[edx]
 *  3026e1de   0f1301           movlps qword ptr ds:[ecx],xmm0
 *  3026e1e1   ba 10ac6201      mov edx,0x162ac10
 *  3026e1e6   8b0d d0ac6201    mov ecx,dword ptr ds:[0x162acd0]         ; pcsx2.01ffed00
 *  3026e1ec   83c1 10          add ecx,0x10
 *  3026e1ef   83e1 f0          and ecx,0xfffffff0
 *  3026e1f2   89c8             mov eax,ecx
 *  3026e1f4   c1e8 0c          shr eax,0xc
 *  3026e1f7   8b0485 30006d0d  mov eax,dword ptr ds:[eax*4+0xd6d0030]
 *  3026e1fe   bb 11e22630      mov ebx,0x3026e211
 *  3026e203   01c1             add ecx,eax
 *  3026e205  -0f88 b530d7d2    js pcsx2.02fe12c0
 *  3026e20b   0f280a           movaps xmm1,dqword ptr ds:[edx]
 *  3026e20e   0f2909           movaps dqword ptr ds:[ecx],xmm1
 *  3026e211   ba 00ac6201      mov edx,0x162ac00
 *  3026e216   8b0d d0ac6201    mov ecx,dword ptr ds:[0x162acd0]         ; pcsx2.01ffed00
 *  3026e21c   83e1 f0          and ecx,0xfffffff0
 *  3026e21f   89c8             mov eax,ecx
 *  3026e221   c1e8 0c          shr eax,0xc
 *  3026e224   8b0485 30006d0d  mov eax,dword ptr ds:[eax*4+0xd6d0030]
 *  3026e22b   bb 3ee22630      mov ebx,0x3026e23e
 *  3026e230   01c1             add ecx,eax
 *  3026e232  -0f88 8830d7d2    js pcsx2.02fe12c0
 *  3026e238   0f2812           movaps xmm2,dqword ptr ds:[edx]
 *  3026e23b   0f2911           movaps dqword ptr ds:[ecx],xmm2
 *  3026e23e   31c0             xor eax,eax
 *  3026e240   a3 f4ac6201      mov dword ptr ds:[0x162acf4],eax
 *  3026e245   c705 f0ac6201 d4>mov dword ptr ds:[0x162acf0],0x1498d4
 *  3026e24f   c705 a8ad6201 c0>mov dword ptr ds:[0x162ada8],0x1281c0
 *  3026e259   a1 c0ae6201      mov eax,dword ptr ds:[0x162aec0]
 *  3026e25e   83c0 07          add eax,0x7
 *  3026e261   a3 c0ae6201      mov dword ptr ds:[0x162aec0],eax
 *  3026e266   2b05 809e6101    sub eax,dword ptr ds:[0x1619e80]
 *  3026e26c   0f88 05000000    js 3026e277
 *  3026e272  -e9 895ddfd2      jmp pcsx2.03064000
 *  3026e277   8b0d 40ab6201    mov ecx,dword ptr ds:[0x162ab40]
 *  3026e27d   89c8             mov eax,ecx
 *  3026e27f   c1e8 0c          shr eax,0xc
 *  3026e282   8b0485 30006d0d  mov eax,dword ptr ds:[eax*4+0xd6d0030]
 *  3026e289   bb 99e22630      mov ebx,0x3026e299
 *  3026e28e   01c1             add ecx,eax
 *  3026e290  -0f88 6a2dd7d2    js pcsx2.02fe1000
 *  3026e296   0fb601           movzx eax,byte ptr ds:[ecx] ; jichi: hook here
 *  3026e299   a3 60ab6201      mov dword ptr ds:[0x162ab60],eax
 *  3026e29e   c705 64ab6201 00>mov dword ptr ds:[0x162ab64],0x0
 *  3026e2a8   a1 60ab6201      mov eax,dword ptr ds:[0x162ab60]
 *  3026e2ad   05 7fffffff      add eax,-0x81
 *  3026e2b2   99               cdq
 *  3026e2b3   a3 70ab6201      mov dword ptr ds:[0x162ab70],eax
 *  3026e2b8   8915 74ab6201    mov dword ptr ds:[0x162ab74],edx
 *  3026e2be   b8 01000000      mov eax,0x1
 *  3026e2c3   833d 74ab6201 00 cmp dword ptr ds:[0x162ab74],0x0
 *  3026e2ca   72 0d            jb short 3026e2d9
 *  3026e2cc   77 09            ja short 3026e2d7
 *  3026e2ce   833d 70ab6201 18 cmp dword ptr ds:[0x162ab70],0x18
 *  3026e2d5   72 02            jb short 3026e2d9
 *  3026e2d7   31c0             xor eax,eax
 *  3026e2d9   a3 10ab6201      mov dword ptr ds:[0x162ab10],eax
 *  3026e2de   c705 14ab6201 00>mov dword ptr ds:[0x162ab14],0x0
 *  3026e2e8   c705 20ab6201 00>mov dword ptr ds:[0x162ab20],0x0
 *  3026e2f2   c705 24ab6201 00>mov dword ptr ds:[0x162ab24],0x0
 *  3026e2fc   c705 30ab6201 00>mov dword ptr ds:[0x162ab30],0x0
 *  3026e306   c705 34ab6201 00>mov dword ptr ds:[0x162ab34],0x0
 *  3026e310   833d 10ab6201 00 cmp dword ptr ds:[0x162ab10],0x0
 *  3026e317   0f85 41000000    jnz 3026e35e
 *  3026e31d   833d 14ab6201 00 cmp dword ptr ds:[0x162ab14],0x0
 *  3026e324   0f85 34000000    jnz 3026e35e
 *  3026e32a   31c0             xor eax,eax
 *  3026e32c   a3 50ab6201      mov dword ptr ds:[0x162ab50],eax
 *  3026e331   a3 54ab6201      mov dword ptr ds:[0x162ab54],eax
 *  3026e336   c705 a8ad6201 c0>mov dword ptr ds:[0x162ada8],0x1285c0
 *  3026e340   a1 c0ae6201      mov eax,dword ptr ds:[0x162aec0]
 *  3026e345   83c0 08          add eax,0x8
 *  3026e348   a3 c0ae6201      mov dword ptr ds:[0x162aec0],eax
 *  3026e34d   2b05 809e6101    sub eax,dword ptr ds:[0x1619e80]
 *  3026e353   0f88 96280000    js 30270bef
 *  3026e359  -e9 a25cdfd2      jmp pcsx2.03064000
 *  3026e35e   31c0             xor eax,eax
 *  3026e360   a3 50ab6201      mov dword ptr ds:[0x162ab50],eax
 *  3026e365   a3 54ab6201      mov dword ptr ds:[0x162ab54],eax
 *  3026e36a   c705 a8ad6201 dc>mov dword ptr ds:[0x162ada8],0x1281dc
 *  3026e374   a1 c0ae6201      mov eax,dword ptr ds:[0x162aec0]
 *  3026e379   83c0 08          add eax,0x8
 *  3026e37c   a3 c0ae6201      mov dword ptr ds:[0x162aec0],eax
 *  3026e381   2b05 809e6101    sub eax,dword ptr ds:[0x1619e80]
 *  3026e387   0f88 a61f0000    js 30270333
 *  3026e38d  -e9 6e5cdfd2      jmp pcsx2.03064000
 *  3026e392   b8 01000000      mov eax,0x1
 *  3026e397   833d 64ab6201 00 cmp dword ptr ds:[0x162ab64],0x0
 *  3026e39e   7c 10            jl short 3026e3b0
 *  3026e3a0   7f 0c            jg short 3026e3ae
 *  3026e3a2   813d 60ab6201 80>cmp dword ptr ds:[0x162ab60],0x80
 *  3026e3ac   72 02            jb short 3026e3b0
 *  3026e3ae   31c0             xor eax,eax
 *  3026e3b0   a3 10ab6201      mov dword ptr ds:[0x162ab10],eax
 *  3026e3b5   c705 14ab6201 00>mov dword ptr ds:[0x162ab14],0x0
 *  3026e3bf   31c0             xor eax,eax
 *  3026e3c1   a3 54ab6201      mov dword ptr ds:[0x162ab54],eax
 *  3026e3c6   c705 50ab6201 01>mov dword ptr ds:[0x162ab50],0x1
 *  3026e3d0   c705 a8ad6201 e8>mov dword ptr ds:[0x162ada8],0x1285e8
 *  3026e3da   a1 c0ae6201      mov eax,dword ptr ds:[0x162aec0]
 *  3026e3df   83c0 03          add eax,0x3
 *  3026e3e2   a3 c0ae6201      mov dword ptr ds:[0x162aec0],eax
 *  3026e3e7   2b05 809e6101    sub eax,dword ptr ds:[0x1619e80]
 *  3026e3ed   0f88 05000000    js 3026e3f8
 *  3026e3f3  -e9 085cdfd2      jmp pcsx2.03064000
 *  3026e3f8   833d 10ab6201 00 cmp dword ptr ds:[0x162ab10],0x0
 *  3026e3ff   0f85 49000000    jnz 3026e44e
 *  3026e405   833d 14ab6201 00 cmp dword ptr ds:[0x162ab14],0x0
 *  3026e40c   0f85 3c000000    jnz 3026e44e
 *  3026e412   a1 60ab6201      mov eax,dword ptr ds:[0x162ab60]
 *  3026e417   c1e0 03          shl eax,0x3
 *  3026e41a   99               cdq
 *  3026e41b   a3 30ab6201      mov dword ptr ds:[0x162ab30],eax
 *  3026e420   8915 34ab6201    mov dword ptr ds:[0x162ab34],edx
 *  3026e426   c705 a8ad6201 04>mov dword ptr ds:[0x162ada8],0x128604
 *  3026e430   a1 c0ae6201      mov eax,dword ptr ds:[0x162aec0]
 *  3026e435   83c0 02          add eax,0x2
 *  3026e438   a3 c0ae6201      mov dword ptr ds:[0x162aec0],eax
 *  3026e43d   2b05 809e6101    sub eax,dword ptr ds:[0x1619e80]
 *  3026e443   0f88 93220000    js 302706dc
 *  3026e449  -e9 b25bdfd2      jmp pcsx2.03064000
 *  3026e44e   a1 60ab6201      mov eax,dword ptr ds:[0x162ab60]
 *  3026e453   c1e0 03          shl eax,0x3
 *  3026e456   99               cdq
 *  3026e457   a3 30ab6201      mov dword ptr ds:[0x162ab30],eax
 *  3026e45c   8915 34ab6201    mov dword ptr ds:[0x162ab34],edx
 *  3026e462   c705 a8ad6201 f0>mov dword ptr ds:[0x162ada8],0x1285f0
 *  3026e46c   a1 c0ae6201      mov eax,dword ptr ds:[0x162aec0]
 *  3026e471   83c0 02          add eax,0x2
 *  3026e474   a3 c0ae6201      mov dword ptr ds:[0x162aec0],eax
 *  3026e479   2b05 809e6101    sub eax,dword ptr ds:[0x1619e80]
 *  3026e47f   0f88 91270000    js 30270c16
 *  3026e485  -e9 765bdfd2      jmp pcsx2.03064000
 *  3026e48a   a1 30ab6201      mov eax,dword ptr ds:[0x162ab30]
 *  3026e48f   0305 60ab6201    add eax,dword ptr ds:[0x162ab60]
 *  3026e495   99               cdq
 *  3026e496   a3 30ab6201      mov dword ptr ds:[0x162ab30],eax
 *  3026e49b   8915 34ab6201    mov dword ptr ds:[0x162ab34],edx
 *  3026e4a1   a1 30ab6201      mov eax,dword ptr ds:[0x162ab30]
 *  3026e4a6   c1e0 05          shl eax,0x5
 *  3026e4a9   99               cdq
 *  3026e4aa   a3 30ab6201      mov dword ptr ds:[0x162ab30],eax
 *  3026e4af   8915 34ab6201    mov dword ptr ds:[0x162ab34],edx
 *  3026e4b5   a1 30ab6201      mov eax,dword ptr ds:[0x162ab30]
 *  3026e4ba   05 e01f2b00      add eax,0x2b1fe0
 *  3026e4bf   99               cdq
 *  3026e4c0   a3 20ab6201      mov dword ptr ds:[0x162ab20],eax
 *  3026e4c5   8915 24ab6201    mov dword ptr ds:[0x162ab24],edx
 *  3026e4cb   8b35 f0ac6201    mov esi,dword ptr ds:[0x162acf0]
 *  3026e4d1   8935 a8ad6201    mov dword ptr ds:[0x162ada8],esi
 *  3026e4d7   a1 c0ae6201      mov eax,dword ptr ds:[0x162aec0]
 *  3026e4dc   83c0 07          add eax,0x7
 *  3026e4df   a3 c0ae6201      mov dword ptr ds:[0x162aec0],eax
 *  3026e4e4   2b05 809e6101    sub eax,dword ptr ds:[0x1619e80]
 *  3026e4ea  -0f88 155bdfd2    js pcsx2.03064005
 *  3026e4f0  -e9 0b5bdfd2      jmp pcsx2.03064000
 *  3026e4f5   a1 20ab6201      mov eax,dword ptr ds:[0x162ab20]
 *  3026e4fa   8b15 24ab6201    mov edx,dword ptr ds:[0x162ab24]
 *  3026e500   a3 00ac6201      mov dword ptr ds:[0x162ac00],eax
 *  3026e505   8915 04ac6201    mov dword ptr ds:[0x162ac04],edx
 *  3026e50b   833d 00ac6201 00 cmp dword ptr ds:[0x162ac00],0x0
 *  3026e512   75 0d            jnz short 3026e521
 *  3026e514   833d 04ac6201 00 cmp dword ptr ds:[0x162ac04],0x0
 *  3026e51b   0f84 39000000    je 3026e55a
 *  3026e521   31c0             xor eax,eax
 */
// Use fixed split for this hook
static void SpecialPS2HookMarvelous(DWORD esp_base, HookParam *, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  DWORD text = regof(ecx, esp_base);
  if (BYTE c = *(BYTE *)text) { // BYTE is unsigned
    *data = text;
    *len = ::LeadByteTable[c];
    *split = FIXED_SPLIT_VALUE * 3; // merge all threads
    //*split = regof(esi, esp_base);
    //*split = *(DWORD *)(esp_base + 4*5); // esp[5]
  }
}

bool InsertMarvelousPS2Hook()
{
  ConsoleOutput("vnreng: Marvelous PS2: enter");
  const BYTE bytes[] =  {
    0x2b,0x05, XX4,                     // 3026e266   2b05 809e6101    sub eax,dword ptr ds:[0x1619e80]
    0x0f,0x88, 0x05,0x00,0x00,0x00,     // 3026e26c   0f88 05000000    js 3026e277
    0xe9, XX4,                          // 3026e272  -e9 895ddfd2      jmp pcsx2.03064000
    0x8b,0x0d, XX4,                     // 3026e277   8b0d 40ab6201    mov ecx,dword ptr ds:[0x162ab40]
    0x89,0xc8,                          // 3026e27d   89c8             mov eax,ecx
    0xc1,0xe8, 0x0c,                    // 3026e27f   c1e8 0c          shr eax,0xc
    0x8b,0x04,0x85, XX4,                // 3026e282   8b0485 30006d0d  mov eax,dword ptr ds:[eax*4+0xd6d0030]
    0xbb, XX4,                          // 3026e289   bb 99e22630      mov ebx,0x3026e299
    0x01,0xc1,                          // 3026e28e   01c1             add ecx,eax
    0x0f,0x88, XX4,                     // 3026e290  -0f88 6a2dd7d2    js pcsx2.02fe1000
    0x0f,0xb6,0x01,                     // 3026e296   0fb601           movzx eax,byte ptr ds:[ecx] ; jichi: hook here
    0xa3, XX4,                          // 3026e299   a3 60ab6201      mov dword ptr ds:[0x162ab60],eax
    0xc7,0x05, XX4, 0x00,0x00,0x00,0x00,// 3026e29e   c705 64ab6201 00>mov dword ptr ds:[0x162ab64],0x0
    0xa1, XX4,                          // 3026e2a8   a1 60ab6201      mov eax,dword ptr ds:[0x162ab60]
    0x05, 0x7f,0xff,0xff,0xff,          // 3026e2ad   05 7fffffff      add eax,-0x81
    0x99,                               // 3026e2b2   99               cdq
    0xa3 //70ab6201                     // 3026e2b3   a3 70ab6201      mov dword ptr ds:[0x162ab70],eax
  };
  enum { addr_offset = 0x3026e296 - 0x3026e266 };

  DWORD addr = SafeMatchBytesInPS2Memory(bytes, sizeof(bytes));
  //addr = 0x30403967;
  if (!addr)
    ConsoleOutput("vnreng: Marvelous PS2: pattern not found");
  else {
    //GROWL_DWORD(addr + addr_offset);
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.type = USING_STRING|NO_CONTEXT; // no context to get rid of return address
    hp.text_fun = SpecialPS2HookMarvelous;
    //hp.offset = pusha_ecx_off - 4; // ecx, get text in ds:[ecx]
    //hp.length_offset = 1;
    ConsoleOutput("vnreng: Marvelous PS2: INSERT");
    //GROWL_DWORD(hp.address);
    NewHook(hp, "Marvelous PS2");
  }

  ConsoleOutput("vnreng: Marvelous PS2: leave");
  return addr;
}

/** 8/3/2014 jichi
 *  Tested game: School Rumble 二学� *
 *  Fixed memory address.
 *  There is only one matched address.
 *
 *  Debug method: Breakpoint the memory address.
 *
 *  Issue: It cannot extract character name.
 *
 *  302072bd   a3 c0ae9e01      mov dword ptr ds:[0x19eaec0],eax
 *  302072c2   2b05 809e9d01    sub eax,dword ptr ds:[0x19d9e80]         ; cdvdgiga.5976f736
 *  302072c8  ^0f88 f3cafcff    js 301d3dc1
 *  302072ce  -e9 2dcd21d3      jmp pcsx2.03424000
 *  302072d3   8b0d 50ab9e01    mov ecx,dword ptr ds:[0x19eab50]
 *  302072d9   89c8             mov eax,ecx
 *  302072db   c1e8 0c          shr eax,0xc
 *  302072de   8b0485 3000e511  mov eax,dword ptr ds:[eax*4+0x11e50030]
 *  302072e5   bb f5722030      mov ebx,0x302072f5
 *  302072ea   01c1             add ecx,eax
 *  302072ec  -0f88 0e9d19d3    js pcsx2.033a1000
 *  302072f2   0fb601           movzx eax,byte ptr ds:[ecx]
 *  302072f5   a3 20ab9e01      mov dword ptr ds:[0x19eab20],eax
 *  302072fa   c705 24ab9e01 00>mov dword ptr ds:[0x19eab24],0x0
 *  30207304   8305 60ab9e01 ff add dword ptr ds:[0x19eab60],-0x1
 *  3020730b   9f               lahf
 *  3020730c   66:c1f8 0f       sar ax,0xf
 *  30207310   98               cwde
 *  30207311   a3 64ab9e01      mov dword ptr ds:[0x19eab64],eax
 *  30207316   8305 50ab9e01 01 add dword ptr ds:[0x19eab50],0x1
 *  3020731d   9f               lahf
 *  3020731e   66:c1f8 0f       sar ax,0xf
 *  30207322   98               cwde
 *  30207323   a3 54ab9e01      mov dword ptr ds:[0x19eab54],eax
 *  30207328   8b15 20ab9e01    mov edx,dword ptr ds:[0x19eab20]
 *  3020732e   8b0d 30ab9e01    mov ecx,dword ptr ds:[0x19eab30]
 *  30207334   89c8             mov eax,ecx
 *  30207336   c1e8 0c          shr eax,0xc
 *  30207339   8b0485 3000e511  mov eax,dword ptr ds:[eax*4+0x11e50030]
 *  30207340   bb 4f732030      mov ebx,0x3020734f
 *  30207345   01c1             add ecx,eax
 *  30207347  -0f88 739e19d3    js pcsx2.033a11c0
 *  3020734d   8811             mov byte ptr ds:[ecx],dl    ; jichi: hook here, text in dl
 *  3020734f   8305 30ab9e01 01 add dword ptr ds:[0x19eab30],0x1
 *  30207356   9f               lahf
 *  30207357   66:c1f8 0f       sar ax,0xf
 *  3020735b   98               cwde
 *  3020735c   a3 34ab9e01      mov dword ptr ds:[0x19eab34],eax
 *  30207361   a1 60ab9e01      mov eax,dword ptr ds:[0x19eab60]
 *  30207366   3b05 40ab9e01    cmp eax,dword ptr ds:[0x19eab40]
 *  3020736c   75 11            jnz short 3020737f
 *  3020736e   a1 64ab9e01      mov eax,dword ptr ds:[0x19eab64]
 *  30207373   3b05 44ab9e01    cmp eax,dword ptr ds:[0x19eab44]
 *  30207379   0f84 28000000    je 302073a7
 *  3020737f   c705 a8ad9e01 34>mov dword ptr ds:[0x19eada8],0x17eb34
 *  30207389   a1 c0ae9e01      mov eax,dword ptr ds:[0x19eaec0]
 *  3020738e   83c0 09          add eax,0x9
 *  30207391   a3 c0ae9e01      mov dword ptr ds:[0x19eaec0],eax
 *  30207396   2b05 809e9d01    sub eax,dword ptr ds:[0x19d9e80]         ; cdvdgiga.5976f736
 *  3020739c  ^0f88 31ffffff    js 302072d3
 *  302073a2  -e9 59cc21d3      jmp pcsx2.03424000
 *  302073a7   c705 a8ad9e01 50>mov dword ptr ds:[0x19eada8],0x17eb50
 *  302073b1   a1 c0ae9e01      mov eax,dword ptr ds:[0x19eaec0]
 *  302073b6   83c0 09          add eax,0x9
 *  302073b9   a3 c0ae9e01      mov dword ptr ds:[0x19eaec0],eax
 *  302073be   2b05 809e9d01    sub eax,dword ptr ds:[0x19d9e80]         ; cdvdgiga.5976f736
 *  302073c4  ^0f88 75cbfcff    js 301d3f3f
 *  302073ca  -e9 31cc21d3      jmp pcsx2.03424000
 *  302073cf   8b15 10ac9e01    mov edx,dword ptr ds:[0x19eac10]
 *  302073d5   8b0d 20ac9e01    mov ecx,dword ptr ds:[0x19eac20]
 *  302073db   83c1 04          add ecx,0x4
 *  302073de   89c8             mov eax,ecx
 *  302073e0   c1e8 0c          shr eax,0xc
 *  302073e3   8b0485 3000e511  mov eax,dword ptr ds:[eax*4+0x11e50030]
 *  302073ea   bb f9732030      mov ebx,0x302073f9
 *  302073ef   01c1             add ecx,eax
 *  302073f1  -0f88 499e19d3    js pcsx2.033a1240
 *  302073f7   8911             mov dword ptr ds:[ecx],edx
 *  302073f9   c705 a8ad9e01 5c>mov dword ptr ds:[0x19eada8],0x18d25c
 *  30207403   a1 c0ae9e01      mov eax,dword ptr ds:[0x19eaec0]
 *  30207408   83c0 03          add eax,0x3
 *  3020740b   a3 c0ae9e01      mov dword ptr ds:[0x19eaec0],eax
 *  30207410   2b05 809e9d01    sub eax,dword ptr ds:[0x19d9e80]         ; cdvdgiga.5976f736
 *  30207416   0f88 05000000    js 30207421
 *  3020741c  -e9 dfcb21d3      jmp pcsx2.03424000
 *  30207421   a1 50ac9e01      mov eax,dword ptr ds:[0x19eac50]
 *  30207426   05 00a2ffff      add eax,0xffffa200
 *  3020742b   99               cdq
 *  3020742c   a3 00ac9e01      mov dword ptr ds:[0x19eac00],eax
 *  30207431   8915 04ac9e01    mov dword ptr ds:[0x19eac04],edx
 *  30207437   31d2             xor edx,edx
 *  30207439   8b0d d0ac9e01    mov ecx,dword ptr ds:[0x19eacd0]
 *  3020743f   89c8             mov eax,ecx
 *  30207441   c1e8 0c          shr eax,0xc
 *  30207444   8b0485 3000e511  mov eax,dword ptr ds:[eax*4+0x11e50030]
 *  3020744b   bb 5a742030      mov ebx,0x3020745a
 *  30207450   01c1             add ecx,eax
 *  30207452  -0f88 e89d19d3    js pcsx2.033a1240
 *  30207458   8911             mov dword ptr ds:[ecx],edx
 *  3020745a   a1 00ac9e01      mov eax,dword ptr ds:[0x19eac00]
 *  3020745f   8b15 04ac9e01    mov edx,dword ptr ds:[0x19eac04]
 */
// Use fixed split for this hook
static void SpecialPS2HookMarvelous2(DWORD esp_base, HookParam *, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  DWORD text = esp_base + pusha_edx_off - 4; // get text in dl: 3020734d   8811  mov byte ptr ds:[ecx],dl
  if (BYTE c = *(BYTE *)text) { // BYTE is unsigned
    *data = text;
    *len = 1;
    //*split = FIXED_SPLIT_VALUE * 4; // merge all threads
    *split = regof(esi, esp_base);
    //*split = *(DWORD *)(esp_base + 4*5); // esp[5]
  }
}

bool InsertMarvelous2PS2Hook()
{
  ConsoleOutput("vnreng: Marvelous2 PS2: enter");
  const BYTE bytes[] =  {
    // The following pattern is not sufficient
    0x89,0xc8,            // 30207334   89c8             mov eax,ecx
    0xc1,0xe8, 0x0c,      // 30207336   c1e8 0c          shr eax,0xc
    0x8b,0x04,0x85, XX4,  // 30207339   8b0485 3000e511  mov eax,dword ptr ds:[eax*4+0x11e50030]
    0xbb, XX4,            // 30207340   bb 4f732030      mov ebx,0x3020734f
    0x01,0xc1,            // 30207345   01c1             add ecx,eax
    0x0f,0x88, XX4,       // 30207347  -0f88 739e19d3    js pcsx2.033a11c0
    0x88,0x11,            // 3020734d   8811             mov byte ptr ds:[ecx],dl    ; jichi: hook here, text in dl
    0x83,0x05, XX4, 0x01, // 3020734f   8305 30ab9e01 01 add dword ptr ds:[0x19eab30],0x1
    0x9f,                 // 30207356   9f               lahf
    0x66,0xc1,0xf8, 0x0f, // 30207357   66:c1f8 0f       sar ax,0xf
    0x98,                 // 3020735b   98               cwde
    // The above pattern is not sufficient
    0xa3, XX4,            // 3020735c   a3 34ab9e01      mov dword ptr ds:[0x19eab34],eax
    0xa1, XX4,            // 30207361   a1 60ab9e01      mov eax,dword ptr ds:[0x19eab60]
    0x3b,0x05, XX4,       // 30207366   3b05 40ab9e01    cmp eax,dword ptr ds:[0x19eab40]
    0x75, 0x11,           // 3020736c   75 11            jnz short 3020737f
    0xa1, XX4,            // 3020736e   a1 64ab9e01      mov eax,dword ptr ds:[0x19eab64]
    0x3b,0x05, XX4,       // 30207373   3b05 44ab9e01    cmp eax,dword ptr ds:[0x19eab44]
    0x0f,0x84, XX4,       // 30207379   0f84 28000000    je 302073a7
    0xc7,0x05, XX8,       // 3020737f   c705 a8ad9e01 34>mov dword ptr ds:[0x19eada8],0x17eb34
    // The above pattern is not sufficient
    0xa1, XX4,            // 30207389   a1 c0ae9e01      mov eax,dword ptr ds:[0x19eaec0]
    0x83,0xc0, 0x09,      // 3020738e   83c0 09          add eax,0x9
    0xa3, XX4,            // 30207391   a3 c0ae9e01      mov dword ptr ds:[0x19eaec0],eax
    0x2b,0x05, XX4,       // 30207396   2b05 809e9d01    sub eax,dword ptr ds:[0x19d9e80]         ; cdvdgiga.5976f736
    0x0f,0x88, XX4,       // 3020739c  ^0f88 31ffffff    js 302072d3
    0xe9, XX4,            // 302073a2  -e9 59cc21d3      jmp pcsx2.03424000
    0xc7,0x05, XX8,       // 302073a7   c705 a8ad9e01 50>mov dword ptr ds:[0x19eada8],0x17eb50
    0xa1, XX4,            // 302073b1   a1 c0ae9e01      mov eax,dword ptr ds:[0x19eaec0]
    0x83,0xc0, 0x09,      // 302073b6   83c0 09          add eax,0x9
    0xa3, XX4,            // 302073b9   a3 c0ae9e01      mov dword ptr ds:[0x19eaec0],eax
    0x2b,0x05, XX4,       // 302073be   2b05 809e9d01    sub eax,dword ptr ds:[0x19d9e80]         ; cdvdgiga.5976f736
    0x0f,0x88, XX4,       // 302073c4  ^0f88 75cbfcff    js 301d3f3f
    0xe9, XX4,            // 302073ca  -e9 31cc21d3      jmp pcsx2.03424000
    0x8b,0x15, XX4,       // 302073cf   8b15 10ac9e01    mov edx,dword ptr ds:[0x19eac10]
    0x8b,0x0d, XX4,       // 302073d5   8b0d 20ac9e01    mov ecx,dword ptr ds:[0x19eac20]
    0x83,0xc1, 0x04,      // 302073db   83c1 04          add ecx,0x4
    0x89,0xc8,            // 302073de   89c8             mov eax,ecx
    0xc1,0xe8, 0x0c,      // 302073e0   c1e8 0c          shr eax,0xc
    0x8b,0x04,0x85, XX4,  // 302073e3   8b0485 3000e511  mov eax,dword ptr ds:[eax*4+0x11e50030]
    0xbb, XX4,            // 302073ea   bb f9732030      mov ebx,0x302073f9
    0x01,0xc1             // 302073ef   01c1             add ecx,eax
  };
  enum { addr_offset = 0x3020734d - 0x30207334 };

  DWORD addr = SafeMatchBytesInPS2Memory(bytes, sizeof(bytes));
  //addr = 0x30403967;
  if (!addr)
    ConsoleOutput("vnreng: Marvelous2 PS2: pattern not found");
  else {
    //GROWL_DWORD(addr + addr_offset);
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.type = USING_STRING|NO_CONTEXT; // no context to get rid of return address
    hp.text_fun = SpecialPS2HookMarvelous2;
    //hp.offset = pusha_ecx_off - 4; // ecx, get text in ds:[ecx]
    //hp.length_offset = 1;
    ConsoleOutput("vnreng: Marvelous2 PS2: INSERT");
    //GROWL_DWORD(hp.address);
    NewHook(hp, "Marvelous2 PS2");
  }

  ConsoleOutput("vnreng: Marvelous2 PS2: leave");
  return addr;
}

#if 0 // jichi 7/19/2014: duplication text

/** 7/19/2014 jichi
 *  Tested game: .hack//G.U. Vol.1
 */
bool InsertNamcoPS2Hook()
{
  ConsoleOutput("vnreng: Namco PS2: enter");
  const BYTE bytes[1] =  {
  };
  enum { addr_offset = 0 };

  //DWORD addr = SafeMatchBytesInPSPMemory(bytes, sizeof(bytes));
  //DWORD addr = 0x303baf26;
  DWORD addr = 0x303C4B72;
  if (!addr)
    ConsoleOutput("vnreng: Namco PS2: pattern not found");
  else {
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.type = USING_STRING|USING_SPLIT; // no context to get rid of return address
    hp.offset = pusha_ecx_off - 4; // ecx
    hp.split = hp.offset; // use ecx address to split
    ConsoleOutput("vnreng: Namco PS2: INSERT");
    //GROWL_DWORD(hp.address);
    NewHook(hp, "Namco PS2");
  }

  ConsoleOutput("vnreng: Namco PS2: leave");
  return addr;
}
#endif // 0

} // namespace Engine

// EOF

#if 0 // SEGA: loop text. BANDAI and Imageepoch should be sufficient
/** 7/25/2014 jichi sega.jp PSP engine
 *  Sample game: Shining Hearts
 *  Encoding: UTF-8
 *
 *  Debug method: simply add hardware break points to the matched memory
 *  All texts are in the memory.
 *  There are two memory addresses, but only one function addresses them.
 *
 *  This function seems to be the same as Tecmo?
 *
 *  13513476   f0:90            lock nop                                 ; lock prefix is not allowed
 *  13513478   77 0f            ja short 13513489
 *  1351347a   c705 a8aa1001 38>mov dword ptr ds:[0x110aaa8],0x89cae38
 *  13513484  -e9 7bcb4ff0      jmp 03a10004
 *  13513489   8b05 7ca71001    mov eax,dword ptr ds:[0x110a77c]
 *  1351348f   81e0 ffffff3f    and eax,0x3fffffff
 *  13513495   8bb0 00004007    mov esi,dword ptr ds:[eax+0x7400000] ; jichi: there are too many garbage here
 *  1351349b   8b3d 7ca71001    mov edi,dword ptr ds:[0x110a77c]
 *  135134a1   8d7f 04          lea edi,dword ptr ds:[edi+0x4]
 *  135134a4   8b05 84a71001    mov eax,dword ptr ds:[0x110a784]
 *  135134aa   81e0 ffffff3f    and eax,0x3fffffff
 *  135134b0   89b0 00004007    mov dword ptr ds:[eax+0x7400000],esi ; extract from esi
 *  135134b6   8b2d 84a71001    mov ebp,dword ptr ds:[0x110a784]
 *  135134bc   8d6d 04          lea ebp,dword ptr ss:[ebp+0x4]
 *  135134bf   8b15 78a71001    mov edx,dword ptr ds:[0x110a778]
 *  135134c5   81fa 01000000    cmp edx,0x1
 *  135134cb   8935 70a71001    mov dword ptr ds:[0x110a770],esi
 *  135134d1   893d 7ca71001    mov dword ptr ds:[0x110a77c],edi
 *  135134d7   892d 84a71001    mov dword ptr ds:[0x110a784],ebp
 *  135134dd   c705 88a71001 01>mov dword ptr ds:[0x110a788],0x1
 *  135134e7   0f84 16000000    je 13513503
 *  135134ed   832d c4aa1001 09 sub dword ptr ds:[0x110aac4],0x9
 *  135134f4   e9 23000000      jmp 1351351c
 *  135134f9   013cae           add dword ptr ds:[esi+ebp*4],edi
 *  135134fc   9c               pushfd
 *  135134fd   08e9             or cl,ch
 *  135134ff   20cb             and bl,cl
 *  13513501   4f               dec edi
 *  13513502   f0:832d c4aa1001>lock sub dword ptr ds:[0x110aac4],0x9    ; lock prefix
 *  1351350a   e9 b1000000      jmp 135135c0
 *  1351350f   015cae 9c        add dword ptr ds:[esi+ebp*4-0x64],ebx
 *  13513513   08e9             or cl,ch
 *  13513515   0acb             or cl,bl
 *  13513517   4f               dec edi
 *  13513518   f0:90            lock nop                                 ; lock prefix is not allowed
 *  1351351a   cc               int3
 *  1351351b   cc               int3
 */
// Read text from esi
static void SpecialPSPHookSega(DWORD esp_base, HookParam *, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  LPCSTR text = LPCSTR(esp_base + pusha_esi_off - 4); // esi address
  if (*text) {
    *data = (DWORD)text;
    *len = !text[0] ? 0 : !text[1] ? 1 : text[2] ? 2 : text[3] ? 3 : 4;
    *split = regof(ebx, esp_base);
  }
}

bool InsertSegaPSPHook()
{
  ConsoleOutput("vnreng: SEGA PSP: enter");
  const BYTE bytes[] =  {
    0x77, 0x0f,                     // 13513478   77 0f            ja short 13513489
    0xc7,0x05, XX8,                 // 1351347a   c705 a8aa1001 38>mov dword ptr ds:[0x110aaa8],0x89cae38
    0xe9, XX4,                      // 13513484  -e9 7bcb4ff0      jmp 03a10004
    0x8b,0x05, XX4,                 // 13513489   8b05 7ca71001    mov eax,dword ptr ds:[0x110a77c]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 1351348f   81e0 ffffff3f    and eax,0x3fffffff
    0x8b,0xb0, XX4,                 // 13513495   8bb0 00004007    mov esi,dword ptr ds:[eax+0x7400000] ; jichi: here are too many garbage
    0x8b,0x3d, XX4,                 // 1351349b   8b3d 7ca71001    mov edi,dword ptr ds:[0x110a77c]
    0x8d,0x7f, 0x04,                // 135134a1   8d7f 04          lea edi,dword ptr ds:[edi+0x4]
    0x8b,0x05, XX4,                 // 135134a4   8b05 84a71001    mov eax,dword ptr ds:[0x110a784]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 135134aa   81e0 ffffff3f    and eax,0x3fffffff
    0x89,0xb0   //, XX4,            // 135134b0   89b0 00004007    mov dword ptr ds:[eax+0x7400000],esi ; jichi: hook here, get text in esi
  };
  enum { memory_offset = 2 };
  enum { addr_offset = sizeof(bytes) - memory_offset };
  //enum { addr_offset = 0x13513495 - 0x13513478 };

  DWORD addr = SafeMatchBytesInPSPMemory(bytes, sizeof(bytes));
  if (!addr)
    ConsoleOutput("vnreng: SEGA PSP: pattern not found");
  else {
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.type = USING_STRING|NO_CONTEXT; // UTF-8
    hp.text_fun = SpecialPSPHookSega;
    ConsoleOutput("vnreng: SEGA PSP: INSERT");
    NewHook(hp, "SEGA PSP");
  }

  ConsoleOutput("vnreng: SEGA PSP: leave");
  return addr;
}
#endif // 0


#if 0 // jichi 7/14/2014: TODO there is text duplication issue?

/** 7/13/2014 jichi SHADE.co.jp PSP engine
 *  Sample game: とある科学の趛�磁� (b-railgun.iso)
 *
 *  CheatEngine/Ollydbg shew there are 4 memory hits to full text in SHIFT-JIS.
 *  CheatEngine is not able to trace JIT instructions.
 *  Ollydbg can track the latter two memory accesses > 0x1ffffffff
 *
 *  The third access is 12ab3d64. There is one write access and 3 read accesses.
 *  But all the accesses are in a loop.
 *  So, the extracted text would suffer from infinite loop problem.
 *
 *  Memory range: 0x0400000 - 139f000
 *
 *  13400e10   90               nop
 *  13400e11   cc               int3
 *  13400e12   cc               int3
 *  13400e13   cc               int3
 *  13400e14   77 0f            ja short 13400e25
 *  13400e16   c705 a8aa1001 08>mov dword ptr ds:[0x110aaa8],0x88c1308
 *  13400e20  -e9 dff161f3      jmp 06a20004
 *  13400e25   8b35 78a71001    mov esi,dword ptr ds:[0x110a778]
 *  13400e2b   81c6 01000000    add esi,0x1
 *  13400e31   8b05 78a71001    mov eax,dword ptr ds:[0x110a778]
 *  13400e37   81e0 ffffff3f    and eax,0x3fffffff
 *  13400e3d   0fb6b8 00004007  movzx edi,byte ptr ds:[eax+0x7400000] ; jichi: the data is in [eax+0x7400000]
 *  13400e44   8b2d 78a71001    mov ebp,dword ptr ds:[0x110a778]
 *  13400e4a   8d6d 01          lea ebp,dword ptr ss:[ebp+0x1]
 *  13400e4d   81ff 00000000    cmp edi,0x0
 *  13400e53   8935 70a71001    mov dword ptr ds:[0x110a770],esi
 *  13400e59   893d 74a71001    mov dword ptr ds:[0x110a774],edi
 *  13400e5f   892d 78a71001    mov dword ptr ds:[0x110a778],ebp
 *  13400e65   0f84 16000000    je 13400e81
 *  13400e6b   832d c4aa1001 04 sub dword ptr ds:[0x110aac4],0x4
 *  13400e72   e9 21000000      jmp 13400e98
 *  13400e77   010c13           add dword ptr ds:[ebx+edx],ecx
 *  13400e7a   8c08             mov word ptr ds:[eax],cs
 *  13400e7c  -e9 a2f161f3      jmp 06a20023
 *  13400e81   832d c4aa1001 04 sub dword ptr ds:[0x110aac4],0x4
 *  13400e88   e9 7f000000      jmp 13400f0c
 *  13400e8d   0118             add dword ptr ds:[eax],ebx
 *  13400e8f   138c08 e98cf161  adc ecx,dword ptr ds:[eax+ecx+0x61f18ce9>
 *  13400e96   f3:              prefix rep:                              ; superfluous prefix
 *  13400e97   90               nop
 *  13400e98   77 0f            ja short 13400ea9
 *  13400e9a   c705 a8aa1001 0c>mov dword ptr ds:[0x110aaa8],0x88c130c
 *  13400ea4  -e9 5bf161f3      jmp 06a20004
 *  13400ea9   8b05 78a71001    mov eax,dword ptr ds:[0x110a778]
 *  13400eaf   81e0 ffffff3f    and eax,0x3fffffff
 *  13400eb5   0fb6b0 00004007  movzx esi,byte ptr ds:[eax+0x7400000]
 *  13400ebc   8b3d 78a71001    mov edi,dword ptr ds:[0x110a778]
 *  13400ec2   8d7f 01          lea edi,dword ptr ds:[edi+0x1]
 *  13400ec5   81fe 00000000    cmp esi,0x0
 *  13400ecb   8935 74a71001    mov dword ptr ds:[0x110a774],esi
 *  13400ed1   893d 78a71001    mov dword ptr ds:[0x110a778],edi
 *  13400ed7   0f84 16000000    je 13400ef3
 *  13400edd   832d c4aa1001 03 sub dword ptr ds:[0x110aac4],0x3
 *  13400ee4  ^e9 afffffff      jmp 13400e98
 *  13400ee9   010c13           add dword ptr ds:[ebx+edx],ecx
 *  13400eec   8c08             mov word ptr ds:[eax],cs
 *  13400eee  -e9 30f161f3      jmp 06a20023
 *  13400ef3   832d c4aa1001 03 sub dword ptr ds:[0x110aac4],0x3
 *  13400efa   e9 0d000000      jmp 13400f0c
 *  13400eff   0118             add dword ptr ds:[eax],ebx
 *  13400f01   138c08 e91af161  adc ecx,dword ptr ds:[eax+ecx+0x61f11ae9>
 *  13400f08   f3:              prefix rep:                              ; superfluous prefix
 *  13400f09   90               nop
 *  13400f0a   cc               int3
 *  13400f0b   cc               int3
 */
static void SpecialPSPHookShade(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  DWORD eax = regof(eax, esp_base);
  LPCSTR text = LPCSTR(eax + hp->user_value);
  if (*text) {
    *data = (DWORD)text;
    *len = ::strlen(text);
  }
}

bool InsertShadePSPHook()
{
  ConsoleOutput("vnreng: Shade PSP: enter");
  // TODO: Query MEM_Mapped at runtime
  // http://msdn.microsoft.com/en-us/library/windows/desktop/aa366902%28v=vs.85%29.aspx
  enum : DWORD { StartAddress = 0x13390000, StopAddress = 0x13490000 };

  const BYTE bytes[] =  {
    0xcc,                           // 13400e12   cc               int3
    0xcc,                           // 13400e13   cc               int3
    0x77, 0x0f,                     // 13400e14   77 0f            ja short 13400e25
    0xc7,0x05, XX8,                 // 13400e16   c705 a8aa1001 08>mov dword ptr ds:[0x110aaa8],0x88c1308
    0xe9, XX4,                      // 13400e20  -e9 dff161f3      jmp 06a20004
    0x8b,0x35, XX4,                 // 13400e25   8b35 78a71001    mov esi,dword ptr ds:[0x110a778]
    0x81,0xc6, 0x01,0x00,0x00,0x00, // 13400e2b   81c6 01000000    add esi,0x1
    0x8b,0x05, XX4,                 // 13400e31   8b05 78a71001    mov eax,dword ptr ds:[0x110a778]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 13400e37   81e0 ffffff3f    and eax,0x3fffffff
    0x0f,0xb6,0xb8, XX4,            // 13400e3d   0fb6b8 00004007  movzx edi,byte ptr ds:[eax+0x7400000] ; jichi: the data is in [eax+0x7400000]
    0x8b,0x2d, XX4,                 // 13400e44   8b2d 78a71001    mov ebp,dword ptr ds:[0x110a778]
    0x8d,0x6d, 0x01,                // 13400e4a   8d6d 01          lea ebp,dword ptr ss:[ebp+0x1]
    0x81,0xff, 0x00,0x00,0x00,0x00  // 13400e4d   81ff 00000000    cmp edi,0x0
  };
  enum{ memory_offset = 3 };
  enum { addr_offset = 0x13400e3d - 0x13400e12 };

  ULONG addr = SafeMatchBytesInPSPMemory(bytes, sizeof(bytes));
  if (!addr)
    ConsoleOutput("vnreng: Shade PSP: failed");
  else {
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.user_value = *(DWORD *)(hp.address + memory_offset);
    hp.text_fun = SpecialPSPHookShade;
    hp.type = USING_STRING;
    ConsoleOutput("vnreng: Shade PSP: INSERT");

    // CHECKPOINT 7/14/2014: This would crash vnrcli
    // I do not have permission to modify the JIT code region?
    NewHook(hp, "Shade PSP");
  }

  //DWORD peek = 0x13400e14;
  //GROWL_DWORD(*(BYTE *)peek); // supposed to be 0x77 ja
  ConsoleOutput("vnreng: Shade PSP: leave");
  return addr;
}

#endif // 0

#if 0 // jichi 7/17/2014: Disabled as there are so many text threads
/** jichi 7/17/2014 alternative Alchemist hook
 *
 *  Sample game: your diary+ (moe-ydp.iso)
 *  The debugging method is the same as Alchemist1.
 *
 *  It seems that hooks found in Alchemist games
 *  also exist in other games.
 *
 *  This function is executed in a looped.
 *
 *  13400e12   cc               int3
 *  13400e13   cc               int3
 *  13400e14   77 0f            ja short 13400e25
 *  13400e16   c705 a8aa1001 84>mov dword ptr ds:[0x110aaa8],0x8931084
 *  13400e20  -e9 dff148f0      jmp 03890004
 *  13400e25   8b05 78a71001    mov eax,dword ptr ds:[0x110a778]
 *  13400e2b   81e0 ffffff3f    and eax,0x3fffffff
 *  13400e31   0fbeb0 00004007  movsx esi,byte ptr ds:[eax+0x7400000] ; jichi: hook here
 *  13400e38   8b3d 78a71001    mov edi,dword ptr ds:[0x110a778]
 *  13400e3e   81fe 00000000    cmp esi,0x0
 *  13400e44   893d 7ca71001    mov dword ptr ds:[0x110a77c],edi
 *  13400e4a   8935 80a71001    mov dword ptr ds:[0x110a780],esi
 *  13400e50   0f85 16000000    jnz 13400e6c
 *  13400e56   832d c4aa1001 03 sub dword ptr ds:[0x110aac4],0x3
 *  13400e5d   e9 16010000      jmp 13400f78
 *  13400e62   01a0 109308e9    add dword ptr ds:[eax+0xe9089310],esp
 *  13400e68   b7 f1            mov bh,0xf1
 *  13400e6a   48               dec eax
 *  13400e6b   f0:832d c4aa1001>lock sub dword ptr ds:[0x110aac4],0x3    ; lock prefix
 *  13400e73   e9 0c000000      jmp 13400e84
 *  13400e78   0190 109308e9    add dword ptr ds:[eax+0xe9089310],edx
 *  13400e7e   a1 f148f090      mov eax,dword ptr ds:[0x90f048f1]
 *  13400e83   cc               int3
 *  13400e84   77 0f            ja short 13400e95
 *  13400e86   c705 a8aa1001 90>mov dword ptr ds:[0x110aaa8],0x8931090
 *  13400e90  -e9 6ff148f0      jmp 03890004
 *  13400e95   8b35 78a71001    mov esi,dword ptr ds:[0x110a778]
 *  13400e9b   8d76 01          lea esi,dword ptr ds:[esi+0x1]
 *  13400e9e   8bc6             mov eax,esi
 *  13400ea0   81e0 ffffff3f    and eax,0x3fffffff
 *  13400ea6   0fbeb8 00004007  movsx edi,byte ptr ds:[eax+0x7400000]
 *  13400ead   81ff 00000000    cmp edi,0x0
 *  13400eb3   8935 78a71001    mov dword ptr ds:[0x110a778],esi
 *  13400eb9   893d 80a71001    mov dword ptr ds:[0x110a780],edi
 *  13400ebf   0f84 25000000    je 13400eea
 *  13400ec5   8b35 78a71001    mov esi,dword ptr ds:[0x110a778]
 *  13400ecb   8d76 01          lea esi,dword ptr ds:[esi+0x1]
 *  13400ece   8935 78a71001    mov dword ptr ds:[0x110a778],esi
 *  13400ed4   832d c4aa1001 04 sub dword ptr ds:[0x110aac4],0x4
 *  13400edb   e9 24000000      jmp 13400f04
 *  13400ee0   019410 9308e939  add dword ptr ds:[eax+edx+0x39e90893],ed>
 *  13400ee7   f1               int1
 *  13400ee8   48               dec eax
 *  13400ee9   f0:832d c4aa1001>lock sub dword ptr ds:[0x110aac4],0x4    ; lock prefix
 *  13400ef1   e9 82000000      jmp 13400f78
 *  13400ef6   01a0 109308e9    add dword ptr ds:[eax+0xe9089310],esp
 *  13400efc   23f1             and esi,ecx
 *  13400efe   48               dec eax
 *  13400eff   f0:90            lock nop                                 ; lock prefix is not allowed
 *  13400f01   cc               int3
 *  13400f02   cc               int3
 */
// jichi 7/17/2014: Why this function is exactly the same as SpecialPSPHookImageepoch?
static void SpecialPSPHookAlchemist3(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *split, DWORD *len)
{
  DWORD eax = regof(eax, esp_base);
  DWORD text = eax + hp->user_value;
  static DWORD lasttext;
  if (text != lasttext && *(LPCSTR)text) {
    *data = lasttext = text;
    *len = ::strlen((LPCSTR)text);
    *split = regof(ecx, esp_base); // use ecx "this" as split value?
  }
}
bool InsertAlchemist3PSPHook()
{
  ConsoleOutput("vnreng: Alchemist3 PSP: enter");
  const BYTE bytes[] =  {
    //0xcc,                         // 13400e12   cc               int3
    //0xcc,                         // 13400e13   cc               int3
    0x77, 0x0f,                     // 13400e14   77 0f            ja short 13400e25
    0xc7,0x05, XX8,                 // 13400e16   c705 a8aa1001 84>mov dword ptr ds:[0x110aaa8],0x8931084
    0xe9, XX4,                      // 13400e20  -e9 dff148f0      jmp 03890004
    0x8b,0x05, XX4,                 // 13400e25   8b05 78a71001    mov eax,dword ptr ds:[0x110a778]
    0x81,0xe0, 0xff,0xff,0xff,0x3f, // 13400e2b   81e0 ffffff3f    and eax,0x3fffffff
    0x0f,0xbe,0xb0, XX4,            // 13400e31   0fbeb0 00004007  movsx esi,byte ptr ds:[eax+0x7400000] ; jichi: hook here
    0x8b,0x3d, XX4,                 // 13400e38   8b3d 78a71001    mov edi,dword ptr ds:[0x110a778]
    0x81,0xfe, 0x00,0x00,0x00,0x00, // 13400e3e   81fe 00000000    cmp esi,0x0
    0x89,0x3d, XX4,                 // 13400e44   893d 7ca71001    mov dword ptr ds:[0x110a77c],edi
    0x89,0x35, XX4,                 // 13400e4a   8935 80a71001    mov dword ptr ds:[0x110a780],esi
    0x0f,0x85 //, 16000000          // 13400e50   0f85 16000000    jnz 13400e6c
  };
  enum { memory_offset = 3 };
  enum { addr_offset = 0x13407711 - 0x134076f4 };

  DWORD addr = SafeMatchBytesInPSPMemory(bytes, sizeof(bytes));
  if (!addr)
    ConsoleOutput("vnreng: Alchemist3 PSP: pattern not found");
  else {
    HookParam hp = {};
    hp.address = addr + addr_offset;
    hp.user_value = *(DWORD *)(hp.address + memory_offset); // use module to pass membase
    hp.text_fun = SpecialPSPHookAlchemist3;
    hp.type = USING_STRING|NO_CONTEXT; // no context is needed to get rid of variant retaddr
    ConsoleOutput("vnreng: Alchemist3 PSP: INSERT");
    NewHook(hp, "Alchemist3 PSP");
  }

  ConsoleOutput("vnreng: Alchemist3 PSP: leave");
  return addr;
}
#endif // 0


#if 0 // jichi 4/21/2014: Disabled as this does not work before mono.dll is loaded

static HMODULE WaitForModuleReady(const char *name, int retryCount = 100, int sleepInterval = 100) // retry for 10 seconds
{
  for (int i = 0; i < retryCount; i++) {
    if (HMODULE h = ::GetModuleHandleA(name))
      return h;
    ::Sleep(sleepInterval);
  }
  return nullptr;
}

/**
 *  jichi 4/21/2014: Mono (Unity3D)
 *  See (ok123): http://sakuradite.com/topic/214
 *  Pattern: 33DB66390175
 *
 *  FIXME: This approach won't work before mono is loaded into the memory.
 *
 *  Example: /HWN-8*0:3C@ mono.dll search 33DB66390175
 *  - length_offset: 1
 *  - module: 1690566707 = 0x64c40033
 *  - off: 4294967284 = 0xfffffff4 = -0xc
 *  - split: 60 = 0x3c
 *  - type: 1114 = 0x45a
 *
 *  Function starts:
 *  1003b818  /$ 55             push ebp
 *  1003b819  |. 8bec           mov ebp,esp
 *  1003b81b  |. 51             push ecx
 *  1003b81c  |. 807d 10 00     cmp byte ptr ss:[ebp+0x10],0x0
 *  1003b820  |. 8b50 08        mov edx,dword ptr ds:[eax+0x8]
 *  1003b823  |. 53             push ebx
 *  1003b824  |. 8b5d 08        mov ebx,dword ptr ss:[ebp+0x8]
 *  1003b827  |. 56             push esi
 *  1003b828  |. 8b75 0c        mov esi,dword ptr ss:[ebp+0xc]
 *  1003b82b  |. 57             push edi
 *  1003b82c  |. 8d78 0c        lea edi,dword ptr ds:[eax+0xc]
 *  1003b82f  |. 897d 08        mov dword ptr ss:[ebp+0x8],edi
 *  1003b832  |. 74 44          je short mono.1003b878
 *  1003b834  |. 2bf2           sub esi,edx
 *  1003b836  |. 03f1           add esi,ecx
 *  1003b838  |. 894d 10        mov dword ptr ss:[ebp+0x10],ecx
 *  1003b83b  |. 8975 08        mov dword ptr ss:[ebp+0x8],esi
 *  1003b83e  |. 3bce           cmp ecx,esi
 *  1003b840  |. 7f 67          jg short mono.1003b8a9
 *  1003b842  |. 8d4c4b 0c      lea ecx,dword ptr ds:[ebx+ecx*2+0xc]
 *  1003b846  |> 0fb707         /movzx eax,word ptr ds:[edi]
 *  1003b849  |. 33db           |xor ebx,ebx    ; jichi hook here
 *  1003b84b  |. 66:3901        |cmp word ptr ds:[ecx],ax
 *  1003b84e  |. 75 16          |jnz short mono.1003b866
 *  1003b850  |. 8bf1           |mov esi,ecx
 *  1003b852  |> 43             |/inc ebx
 *  1003b853  |. 83c6 02        ||add esi,0x2
 *  1003b856  |. 3bda           ||cmp ebx,edx
 *  1003b858  |. 74 19          ||je short mono.1003b873
 *  1003b85a  |. 66:8b06        ||mov ax,word ptr ds:[esi]
 *  1003b85d  |. 66:3b045f      ||cmp ax,word ptr ds:[edi+ebx*2]
 *  1003b861  |.^74 ef          |\je short mono.1003b852
 *  1003b863  |. 8b75 08        |mov esi,dword ptr ss:[ebp+0x8]
 *  1003b866  |> ff45 10        |inc dword ptr ss:[ebp+0x10]
 *  1003b869  |. 83c1 02        |add ecx,0x2
 *  1003b86c  |. 3975 10        |cmp dword ptr ss:[ebp+0x10],esi
 *  1003b86f  |.^7e d5          \jle short mono.1003b846
 */
bool InsertMonoHook()
{
  enum { module = 0x64c40033 }; // hash of "mono.dll"
  DWORD base = Util::FindModuleBase(module);
  if (!base && WaitForModuleReady("mono.dll"))
    base = Util::FindModuleBase(module);

  if (!base) {
    ConsoleOutput("vnreng:Mono: module not found");
    return false;
  }

  // Instruction pattern: 90FF503C83C4208B45EC
  const BYTE ins[] = {
    0x33,0xdb,      // 1003b849  |. 33db           |xor ebx,ebx    ; jichi hook here
    0x66,0x39,0x01, // 1003b84b  |. 66:3901        |cmp word ptr ds:[ecx],ax
    0x75 //,0x16    // 1003b84e  |. 75 16          |jnz short mono.1003b866
  };
  enum { addr_offset = 0 }; // no offset
  enum { range = 0x50000 }; // larger than relative addresses = 0x3b849
  ULONG reladdr = SearchPattern(base, range, ins, sizeof(ins));
  //reladdr = 0x3b849;
  GROWL(reladdr);
  if (!reladdr) {
    ConsoleOutput("vnreng:Mono: pattern not found");
    return false;
  }

  HookParam hp = {};
  hp.address = base + reladdr + addr_offset;
  //hp.module = module;
  hp.length_offset = 1;
  hp.offset = -0xc;
  hp.split = 0x3c;
  //hp.type = NO_CONTEXT|USING_SPLIT|MODULE_OFFSET|USING_UNICODE|DATA_INDIRECT; // 0x45a;
  hp.type = NO_CONTEXT|USING_SPLIT|USING_UNICODE|DATA_INDIRECT;

  ConsoleOutput("vnreng: INSERT Mono");
  NewHook(hp, "Mono");
  return true;
}
#endif // 0