├── .gitignore ├── KeyNamesDX.ahk ├── LICENSE ├── Lib ├── LVRow.ahk ├── LVUtils.ahk ├── PEHelper.ahk ├── PatchProcessor.ahk ├── WZSplashWindow.ahk ├── utils.ahk └── wzclipboard.ahk ├── README.md ├── docs ├── GNUmakefile ├── help.html ├── help.md ├── style.css └── thkmc_sample_ini.html ├── gamedata.ahk ├── gamedata ├── buildgamedata.vim ├── gamedata10.ahk ├── gamedata11.ahk ├── gamedata12.ahk ├── gamedata125.ahk ├── gamedata128.ahk ├── gamedata13.ahk ├── gamedata14.ahk ├── gamedata143.ahk ├── gamedata15.ahk ├── gamedata16.ahk ├── gamedata165.ahk ├── gamedata17.ahk ├── gamedata17tr.ahk ├── gamedata6.ahk ├── gamedata6cn.ahk ├── gamedata7.ahk ├── gamedata8.ahk ├── gamedata95.ahk └── gamedataalcostg.ahk ├── readme.mak ├── thkmc.ahk ├── thkmc.ahk.ini ├── thkmc.ico └── thkmc_sample.ini /.gitignore: -------------------------------------------------------------------------------- 1 | tags 2 | 3 | *.log 4 | *__.* 5 | *__ 6 | 7 | *.bak 8 | *.*~ 9 | 10 | *.exe 11 | recentfiles* 12 | thkmc.ini 13 | config.ini 14 | README.html 15 | /style.css 16 | *.zip 17 | *.cmd 18 | -------------------------------------------------------------------------------- /KeyNamesDX.ahk: -------------------------------------------------------------------------------- 1 | ; 键位名称和其扫描码的对照表 2 | ; 注意这是 DirectInput 的扫描码,与 Win32 API 中的扫描码不完全相同(虽然有大部分是相同的)。 3 | ; 4 | ; 来自 MSDN - Keyboard Device Enumeration: 5 | ; https://msdn.microsoft.com/en-us/library/windows/desktop/ee418641%28v=vs.85%29.aspx 6 | ; 然而实际上 MSDN 上的并不全,需要完整的可以打开 Visual Studio 的 include/dinput.h 然后搜索 "DIK_" 7 | ; 8 | ; 以下的内容转换自 VS2010 的 dinput.h 中与扫描码相关的内容。 9 | ; 不过部分名称根据我自己的习惯作了一些修改,主要是一些较长或现在不太用的名称, 10 | ; 比如 ESCAPE 改成 ESC,RETURN 改成 ENTER 等等。 11 | ; 当然以后如果有需要可能还会添加更多的别名。 12 | ; 13 | ; 【注意】INI 文件里的键位名称是大小写不敏感的,也就是说 "ESC" 可以写成 "esc" 或者 "eSc" 等等,都是相同的意思。 14 | ; 15 | ; 以下的 THKMC_KeyNamesDX["XXX"] 中的 XXX 部分便是 INI 文件中可以使用的键位名称。 16 | ; 【注意1】不一定所有的键位都可用。 17 | ; 【注意2】主键盘上的数字键不能直接写数字(因为数字直接代表扫描码),而要写成 "VK数字" 。 18 | ; 比如如果你要将 "Z" 键映射为主键盘上的数字键 "1",不能写 "Z=1",而要写成 "Z=VK1" 。 19 | ; 当然,由于数字键“1”的扫描码是 0x02,所以你也可以写成“Z=0x02”。 20 | ; (虽然“Z=2”也是相同的意思,不过不建议这么写,因为很容易让别人误以为是数字键“2”) 21 | 22 | THKMC_KeyNamesDX := {} 23 | THKMC_KeyNamesDX["ESC"] := 0x01 24 | THKMC_KeyNamesDX["VK1"] := 0x02 ; 主键盘数字键“1” 25 | THKMC_KeyNamesDX["VK2"] := 0x03 26 | THKMC_KeyNamesDX["VK3"] := 0x04 27 | THKMC_KeyNamesDX["VK4"] := 0x05 28 | THKMC_KeyNamesDX["VK5"] := 0x06 29 | THKMC_KeyNamesDX["VK6"] := 0x07 30 | THKMC_KeyNamesDX["VK7"] := 0x08 31 | THKMC_KeyNamesDX["VK8"] := 0x09 32 | THKMC_KeyNamesDX["VK9"] := 0x0A 33 | THKMC_KeyNamesDX["VK0"] := 0x0B 34 | THKMC_KeyNamesDX["MINUS"] := 0x0C ; 主键盘区的减号键 35 | THKMC_KeyNamesDX["EQUALS"] := 0x0D ; 等号 36 | THKMC_KeyNamesDX["BACK"] := 0x0E ; 退格键 backspace 37 | THKMC_KeyNamesDX["TAB"] := 0x0F 38 | THKMC_KeyNamesDX["Q"] := 0x10 39 | THKMC_KeyNamesDX["W"] := 0x11 40 | THKMC_KeyNamesDX["E"] := 0x12 41 | THKMC_KeyNamesDX["R"] := 0x13 42 | THKMC_KeyNamesDX["T"] := 0x14 43 | THKMC_KeyNamesDX["Y"] := 0x15 44 | THKMC_KeyNamesDX["U"] := 0x16 45 | THKMC_KeyNamesDX["I"] := 0x17 46 | THKMC_KeyNamesDX["O"] := 0x18 47 | THKMC_KeyNamesDX["P"] := 0x19 48 | THKMC_KeyNamesDX["LBRACKET"] := 0x1A ; 左方括号 [ 49 | THKMC_KeyNamesDX["RBRACKET"] := 0x1B ; 右方括号 ] 50 | THKMC_KeyNamesDX["ENTER"] := 0x1C ; Enter on main keyboard 51 | THKMC_KeyNamesDX["LCONTROL"] := 0x1D ; 左 CTRL 52 | THKMC_KeyNamesDX["A"] := 0x1E 53 | THKMC_KeyNamesDX["S"] := 0x1F 54 | THKMC_KeyNamesDX["D"] := 0x20 55 | THKMC_KeyNamesDX["F"] := 0x21 56 | THKMC_KeyNamesDX["G"] := 0x22 57 | THKMC_KeyNamesDX["H"] := 0x23 58 | THKMC_KeyNamesDX["J"] := 0x24 59 | THKMC_KeyNamesDX["K"] := 0x25 60 | THKMC_KeyNamesDX["L"] := 0x26 61 | THKMC_KeyNamesDX["SEMICOLON"] := 0x27 ; 分号 ; 62 | THKMC_KeyNamesDX["APOSTROPHE"] := 0x28 ; 单引号 ' 63 | THKMC_KeyNamesDX["GRAVE"] := 0x29 ; accent grave ` 64 | THKMC_KeyNamesDX["LSHIFT"] := 0x2A ; 左 SHIFT 65 | THKMC_KeyNamesDX["BACKSLASH"] := 0x2B ; 反斜杠 \ 66 | THKMC_KeyNamesDX["Z"] := 0x2C 67 | THKMC_KeyNamesDX["X"] := 0x2D 68 | THKMC_KeyNamesDX["C"] := 0x2E 69 | THKMC_KeyNamesDX["V"] := 0x2F 70 | THKMC_KeyNamesDX["B"] := 0x30 71 | THKMC_KeyNamesDX["N"] := 0x31 72 | THKMC_KeyNamesDX["M"] := 0x32 73 | THKMC_KeyNamesDX["COMMA"] := 0x33 ; 逗号 , 74 | THKMC_KeyNamesDX["PERIOD"] := 0x34 ; 点号 . on main keyboard 75 | THKMC_KeyNamesDX["SLASH"] := 0x35 ; 斜杠 / on main keyboard 76 | THKMC_KeyNamesDX["RSHIFT"] := 0x36 77 | THKMC_KeyNamesDX["MULTIPLY"] := 0x37 ; 星号 * on numeric keypad 78 | THKMC_KeyNamesDX["LMENU"] := 0x38 ; 左 Alt 79 | THKMC_KeyNamesDX["SPACE"] := 0x39 80 | THKMC_KeyNamesDX["CAPITAL"] := 0x3A ; Capslock 81 | THKMC_KeyNamesDX["F1"] := 0x3B 82 | THKMC_KeyNamesDX["F2"] := 0x3C 83 | THKMC_KeyNamesDX["F3"] := 0x3D 84 | THKMC_KeyNamesDX["F4"] := 0x3E 85 | THKMC_KeyNamesDX["F5"] := 0x3F 86 | THKMC_KeyNamesDX["F6"] := 0x40 87 | THKMC_KeyNamesDX["F7"] := 0x41 88 | THKMC_KeyNamesDX["F8"] := 0x42 89 | THKMC_KeyNamesDX["F9"] := 0x43 90 | THKMC_KeyNamesDX["F10"] := 0x44 91 | THKMC_KeyNamesDX["NUMLOCK"] := 0x45 92 | THKMC_KeyNamesDX["SCROLL"] := 0x46 ; Scroll Lock 93 | THKMC_KeyNamesDX["NUMPAD7"] := 0x47 94 | THKMC_KeyNamesDX["NUMPAD8"] := 0x48 95 | THKMC_KeyNamesDX["NUMPAD9"] := 0x49 96 | THKMC_KeyNamesDX["SUBTRACT"] := 0x4A ; - on numeric keypad 97 | THKMC_KeyNamesDX["NUMPAD4"] := 0x4B 98 | THKMC_KeyNamesDX["NUMPAD5"] := 0x4C 99 | THKMC_KeyNamesDX["NUMPAD6"] := 0x4D 100 | THKMC_KeyNamesDX["ADD"] := 0x4E ; + on numeric keypad 101 | THKMC_KeyNamesDX["NUMPAD1"] := 0x4F 102 | THKMC_KeyNamesDX["NUMPAD2"] := 0x50 103 | THKMC_KeyNamesDX["NUMPAD3"] := 0x51 104 | THKMC_KeyNamesDX["NUMPAD0"] := 0x52 105 | THKMC_KeyNamesDX["DECIMAL"] := 0x53 ; . on numeric keypad 106 | THKMC_KeyNamesDX["OEM_102"] := 0x56 ; <> or \| on RT 102-key keyboard (Non-U.S.) 107 | THKMC_KeyNamesDX["F11"] := 0x57 108 | THKMC_KeyNamesDX["F12"] := 0x58 109 | THKMC_KeyNamesDX["F13"] := 0x64 ; (NEC PC98) 110 | THKMC_KeyNamesDX["F14"] := 0x65 ; (NEC PC98) 111 | THKMC_KeyNamesDX["F15"] := 0x66 ; (NEC PC98) 112 | THKMC_KeyNamesDX["KANA"] := 0x70 ; (Japanese keyboard) 113 | THKMC_KeyNamesDX["ABNT_C1"] := 0x73 ; /? on Brazilian keyboard 114 | THKMC_KeyNamesDX["CONVERT"] := 0x79 ; (Japanese keyboard) 115 | THKMC_KeyNamesDX["NOCONVERT"] := 0x7B ; (Japanese keyboard) 116 | THKMC_KeyNamesDX["YEN"] := 0x7D ; (Japanese keyboard) 117 | THKMC_KeyNamesDX["ABNT_C2"] := 0x7E ; Numpad . on Brazilian keyboard 118 | THKMC_KeyNamesDX["NUMPADEQUALS"] := 0x8D ; = on numeric keypad (NEC PC98) 119 | THKMC_KeyNamesDX["PREVTRACK"] := 0x90 ; Previous Track (DIK_CIRCUMFLEX on Japanese keyboard) 120 | THKMC_KeyNamesDX["AT"] := 0x91 ; (NEC PC98) 121 | THKMC_KeyNamesDX["COLON"] := 0x92 ; (NEC PC98) 122 | THKMC_KeyNamesDX["UNDERLINE"] := 0x93 ; (NEC PC98) 123 | THKMC_KeyNamesDX["KANJI"] := 0x94 ; (Japanese keyboard) 124 | THKMC_KeyNamesDX["STOP"] := 0x95 ; (NEC PC98) 125 | THKMC_KeyNamesDX["AX"] := 0x96 ; (Japan AX) 126 | THKMC_KeyNamesDX["UNLABELED"] := 0x97 ; (J3100) 127 | THKMC_KeyNamesDX["NEXTTRACK"] := 0x99 ; Next Track 128 | THKMC_KeyNamesDX["NUMPADENTER"] := 0x9C ; Enter on numeric keypad 129 | THKMC_KeyNamesDX["RCONTROL"] := 0x9D 130 | THKMC_KeyNamesDX["MUTE"] := 0xA0 ; Mute 131 | THKMC_KeyNamesDX["CALCULATOR"] := 0xA1 ; Calculator 132 | THKMC_KeyNamesDX["PLAYPAUSE"] := 0xA2 ; Play / Pause 133 | THKMC_KeyNamesDX["MEDIASTOP"] := 0xA4 ; Media Stop 134 | THKMC_KeyNamesDX["VOLUMEDOWN"] := 0xAE ; Volume - 135 | THKMC_KeyNamesDX["VOLUMEUP"] := 0xB0 ; Volume + 136 | THKMC_KeyNamesDX["WEBHOME"] := 0xB2 ; Web home 137 | THKMC_KeyNamesDX["NUMPADCOMMA"] := 0xB3 ; , on numeric keypad (NEC PC98) 138 | THKMC_KeyNamesDX["DIVIDE"] := 0xB5 ; / on numeric keypad 139 | THKMC_KeyNamesDX["SYSRQ"] := 0xB7 140 | THKMC_KeyNamesDX["RMENU"] := 0xB8 ; 右 Alt 141 | THKMC_KeyNamesDX["PAUSE"] := 0xC5 ; Pause 142 | THKMC_KeyNamesDX["HOME"] := 0xC7 ; Home on arrow keypad 143 | THKMC_KeyNamesDX["UP"] := 0xC8 ; ↑ 144 | THKMC_KeyNamesDX["PRIOR"] := 0xC9 ; PgUp 145 | THKMC_KeyNamesDX["LEFT"] := 0xCB ; ← 146 | THKMC_KeyNamesDX["RIGHT"] := 0xCD ; → 147 | THKMC_KeyNamesDX["END"] := 0xCF ; End 148 | THKMC_KeyNamesDX["DOWN"] := 0xD0 ; ↓ 149 | THKMC_KeyNamesDX["NEXT"] := 0xD1 ; PgDn 150 | THKMC_KeyNamesDX["INSERT"] := 0xD2 ; Insert 151 | THKMC_KeyNamesDX["DELETE"] := 0xD3 ; Delete 152 | THKMC_KeyNamesDX["LWIN"] := 0xDB ; 左 Windows 徽标键 153 | THKMC_KeyNamesDX["RWIN"] := 0xDC ; 右 Windows 徽标键 154 | THKMC_KeyNamesDX["APPS"] := 0xDD ; AppMenu key 155 | THKMC_KeyNamesDX["POWER"] := 0xDE ; System Power 156 | THKMC_KeyNamesDX["SLEEP"] := 0xDF ; System Sleep 157 | THKMC_KeyNamesDX["WAKE"] := 0xE3 ; System Wake 158 | THKMC_KeyNamesDX["WEBSEARCH"] := 0xE5 ; Web Search 159 | THKMC_KeyNamesDX["WEBFAVORITES"] := 0xE6 ; Web Favorites 160 | THKMC_KeyNamesDX["WEBREFRESH"] := 0xE7 ; Web Refresh 161 | THKMC_KeyNamesDX["WEBSTOP"] := 0xE8 ; Web Stop 162 | THKMC_KeyNamesDX["WEBFORWARD"] := 0xE9 ; Web Forward 163 | THKMC_KeyNamesDX["WEBBACK"] := 0xEA ; Web Back 164 | THKMC_KeyNamesDX["MYCOMPUTER"] := 0xEB ; My Computer 165 | THKMC_KeyNamesDX["MAIL"] := 0xEC ; Mail 166 | THKMC_KeyNamesDX["MEDIASELECT"] := 0xED ; Media Select 167 | 168 | 169 | ; 这些是 DINPUT.H 里定义的个别键位的别名 170 | THKMC_KeyNamesDX["BACKSPACE"] := THKMC_KeyNamesDX["BACK"] 171 | THKMC_KeyNamesDX["NUMPADSTAR"] := THKMC_KeyNamesDX["MULTIPLY"] 172 | THKMC_KeyNamesDX["LALT"] := THKMC_KeyNamesDX["LMENU"] 173 | THKMC_KeyNamesDX["CAPSLOCK"] := THKMC_KeyNamesDX["CAPITAL"] 174 | THKMC_KeyNamesDX["NUMPADMINUS"] := THKMC_KeyNamesDX["SUBTRACT"] 175 | THKMC_KeyNamesDX["NUMPADPLUS"] := THKMC_KeyNamesDX["ADD"] 176 | THKMC_KeyNamesDX["NUMPADPERIOD"] := THKMC_KeyNamesDX["DECIMAL"] 177 | THKMC_KeyNamesDX["NUMPADSLASH"] := THKMC_KeyNamesDX["DIVIDE"] 178 | THKMC_KeyNamesDX["RALT"] := THKMC_KeyNamesDX["RMENU"] 179 | THKMC_KeyNamesDX["PGUP"] := THKMC_KeyNamesDX["PRIOR"] 180 | THKMC_KeyNamesDX["PGDN"] := THKMC_KeyNamesDX["NEXT"] 181 | THKMC_KeyNamesDX["DIK_CIRCUMFLEX"] := THKMC_KeyNamesDX["PREVTRACK"] 182 | 183 | 184 | 185 | ; 这是我自己定义的别名 186 | THKMC_KeyNamesDX["EQUAL"] := THKMC_KeyNamesDX["EQUALS"] 187 | THKMC_KeyNamesDX["SCROLLLOCK"] := THKMC_KeyNamesDX["SCROLL"] 188 | 189 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Lib/LVRow.ahk: -------------------------------------------------------------------------------- 1 | ; Use with "for loop" to enumerate each selected line of a ListView control in an AHK GUI 2 | class LVRow { 3 | _NewEnum() { 4 | return new CLVRowEnumerator() 5 | } 6 | } 7 | class CLVRowEnumerator { 8 | __New( ) { 9 | this.first := True 10 | this.count := 0 ; how many times Next() has been called 11 | } 12 | Next(ByRef c, ByRef row) { 13 | if (this.first) { 14 | this.Remove("first") 15 | row := 0 16 | } 17 | 18 | row := LV_GetNext(row) 19 | result := row != 0 20 | if ( result ) { 21 | ++this.count 22 | } 23 | c := this.count 24 | return result 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /Lib/LVUtils.ahk: -------------------------------------------------------------------------------- 1 | ; ListView 辅助函数 2 | 3 | ; LV_InsertIfNotExist(textToFind, columnToFind, RowNumber, [Options, Field1, Field2, ...]) 4 | ; return a value returned by LV_Insert(). 5 | LV_InsertIfNotExist(textToFind, columnToFind, params*) 6 | { 7 | found := False 8 | result := 0 9 | loop, % LV_GetCount() 10 | { 11 | result_get := LV_GetText(text, A_Index, columnToFind) 12 | if (result_get && text=textToFind) { 13 | found := True 14 | } 15 | } 16 | if ( !found ) { 17 | result := LV_Insert(params*) 18 | } 19 | return result 20 | } 21 | 22 | ; AHK自带的自动调节宽度还不尽人意,部分表头不能完全显示,通过发送消息解决 23 | ; LVName: ListView 控件名 24 | LVAutoSizeHeader(LVName) { 25 | LV_ModifyCol() 26 | LVM_SETCOLUMNWIDTH = 0x101E 27 | LVSCW_AUTOSIZE_USEHEADER = 0xFFFFFFFE 28 | GuiControlGet, hwnd, hwnd, %LVName% 29 | Loop % LV_GetCount("Column") 30 | { 31 | SendMessage, LVM_SETCOLUMNWIDTH, A_Index, LVSCW_AUTOSIZE_USEHEADER, , ahk_id %hwnd% 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Lib/PEHelper.ahk: -------------------------------------------------------------------------------- 1 | ; 获取 PE 文件头信息 2 | ; 目前只能获取 timestamp 3 | 4 | class CPEHelper { 5 | ; pEXEData: 指向 EXE 数据的指针。【注意】本类不会复制指针所指内容。 6 | __New( pEXEData, datasize ) { 7 | this._p := pEXEData 8 | this._size := datasize 9 | this.isPE := -1 10 | this.isPE := this._verify() 11 | } 12 | 13 | _verify() { 14 | if ( this.isPE != -1 ) { 15 | throw Exception("Do not call me twice. Use isPE property") 16 | } 17 | result := False 18 | ; 文件大小必须大于 0x40 (sizeof(IMAGE_DOS_HEADER)) 19 | if ( this._size > 0x40 ) { 20 | ; 'MZ' magic number 21 | uMZ := NumGet(this._p, 0x00, "UShort") 22 | if ( uMZ = 0x5a4d ) { 23 | ; 获取 IMAGE_NT_HEADERS 结构偏移 24 | oPEHeader := NumGet(this._p, 0x3c, "UInt") 25 | ; 偏移不得超出文件大小 26 | if ( oPEHeader < this._size ) { 27 | ; 'PE' magic number 28 | uPE := NumGet(this._p, oPEHeader+0, "UInt") 29 | if ( uPE = 0x00004550 ) { 30 | this.oPEHeader := oPEHeader 31 | result := True 32 | } 33 | } 34 | } 35 | } 36 | return result 37 | } 38 | 39 | ; 如果 isPE 为 False 则扔异常 40 | checkIsPE() { 41 | if ( !this.isPE ) { 42 | throw Exception("Invalid PE Header") 43 | } 44 | } 45 | 46 | timestamp[] 47 | { 48 | get { 49 | this.checkIsPE() 50 | 51 | ; PEHeader+08h 即是 timestamp 52 | return NumGet(this._p, this.oPEHeader+0x08, "UInt") 53 | } 54 | set { 55 | throw Exception("Currently not supported") 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Lib/PatchProcessor.ahk: -------------------------------------------------------------------------------- 1 | ; 用于生成已打补丁的EXE的类 2 | class THKMC_PatchProcessor { 3 | __New( pEXEData, exesize, kmcdata ) { 4 | this._pEXEData := pEXEData ; 指向 EXE 二进制数据(不是HEX数据)的指针 5 | this._exesize := exesize ; EXE 文件大小 6 | this._d := kmcdata ; THKMC_GameData 或其子类的实例 7 | } 8 | 9 | ; 有错扔异常 10 | doPatch() { 11 | global THKMC_KeyNamesDX 12 | key_names := THKMC_KeyNamesDX 13 | 14 | inifilepath := this._d.inifilepath 15 | inifilesection := this._d.gamename 16 | 17 | ; 获取 Default 段和当前游戏所属段中是否有被 INCLUDE section,若有则记下 18 | IniRead, includesection_default, %inifilepath%, Default, INCLUDE 19 | IniRead, includesection, %inifilepath%, %inifilesection%, INCLUDE 20 | 21 | ; 遍历与键位相关的操作数 22 | matchstr= 23 | startingpos := 1 24 | patchdata := this._d.patchdata 25 | Loop { 26 | startingpos += StrLen(matchstr1) 27 | ; 找出所有 BYTE PTR SS:[ EBPorESP ± CONST32 ] 28 | startingpos := RegExMatch(patchdata, this._d.addrPattern, matchstr, startingpos) 29 | if ( !startingpos ) { 30 | break ; 已处理完所有按键 31 | } 32 | 33 | KeymapValue := this._d.str2KeymapValue(matchstr1) 34 | if (KeymapValue = "OUT") { 35 | continue ; 超出范围, 忽略 36 | } 37 | 38 | ; msgbox % KeymapValue 39 | KeymapKeys := this._findKeymapKeyFromValue(KeymapValue, key_names) 40 | if ( !KeymapKeys.MaxIndex() ) { 41 | ; DEBUG: 用户不应该看到下面的内部错误信息 42 | msg = 【DEBUG】内部错误ERR001。 43 | AddLog(msg, True) 44 | continue ; 执行到这里表明需要重新DEBUG,先跳过 45 | } 46 | 47 | ; 开始从 INI 中读取键位设置,并根据 this._d 中的设置修改相应的 EXE 数据(HEX-ASCII) 48 | for i, keymap in KeymapKeys { 49 | log_message= 50 | newkeymap := this._readKeymapFromINI(keymap, this._d.gamename, includesection, log_message) ; 当前游戏所属段 51 | if ( newkeymap = "ERROR" || newkeymap = "" ) { 52 | newkeymap := this._readKeymapFromINI(keymap, "Default", includesection_default, log_message) ; 读取失败,尝试从 Default 段读取 53 | if ( newkeymap = "ERROR" || newkeymap = "" ) { 54 | AddLogIfNotEmpty(log_message) 55 | continue ; 还是读取失败(该键位的设置不存在或者等号右边为空),跳过 56 | } 57 | } 58 | 59 | ; 检查等号右边的是否是数值,如果是,则直接看做是扫描码,不进行转换 60 | if newkeymap is Integer 61 | { 62 | newkeymapValue := newkeymap & 0xff ; 扫描码应该只有低 8 位有效 63 | } 64 | else { 65 | newkeymapValue := key_names[newkeymap] 66 | } 67 | if ( newkeymapValue = "" ) { 68 | ; 该键位名不存在于 key_names 。是个错误的键位名 69 | errmsg = 【警告】无法为【%keymap%】设置新映射:无法识别的键位名【%newkeymap%】 70 | 71 | ; DEPRECATED: throw Exception(errmsg, inifilepath) 72 | ; 现在将错误 INI 中的错误键位名视为警告 73 | AddLog(errmsg) 74 | continue 75 | } 76 | 77 | newkeymapValue := this._d.keymapValue2Str( newkeymapValue ) 78 | 79 | ; 修改完毕,替换掉原来的数据 80 | patchdata := RegExReplace(patchdata, "\b" . matchstr1 . "\b", newkeymapValue, replcount, 1, startingpos) 81 | if ( replcount = 1 ) { 82 | msg = 【设置成功】【%keymap%】->【%newkeymap%】 83 | AddLog(msg) 84 | } 85 | else { 86 | errmsg = 映射【%keymap%】为【%newkeymap%】失败! 87 | throw Exception(errmsg, inifilepath) 88 | } 89 | } 90 | } 91 | 92 | ; 在 patchdata 的最后增加 JMP 指令以跳转到原来的流程 93 | codelen := MCode(code, patchdata) 94 | addr_beginjmp := this._d.patchaddr + codelen 95 | 96 | ; **DEBUG**,确保补丁数据长度小于原始数据长度。这两条信息不应该出现在最终用户面前 97 | if ( addr_beginjmp = this._d.patchaddrjmpto ) { 98 | throw Exception("补丁数据长度等于原始数据长度。请将此信息汇报给作者。", this._d.inifilepath) 99 | } 100 | else if ( addr_beginjmp > this._d.patchaddrjmpto ) { 101 | throw Exception("补丁数据长度超过原始数据长度。请将此信息汇报给作者。", this._d.inifilepath) 102 | } 103 | 104 | patchaddr_diff := this._d.patchaddrjmpto - addr_beginjmp 105 | if ( patchaddr_diff <= 6 ) { 106 | ; 若补丁比原内容少了不大于 6 个字节,补相应数量的 NOP 107 | this._d.patchdata := patchdata . StringRepeat("90", patchaddr_diff) 108 | } 109 | else { 110 | ; 大于 6 个字节,则补 JMP 跳到原来的流程 111 | calladdr := addr2CallAddr( addr_beginjmp, this._d.patchaddrjmpto ) 112 | this._d.patchdata := patchdata . " " . longjmpOrShortjmp("E9", "EB", calladdr) 113 | } 114 | } 115 | 116 | ; 将 patchdata 写入内存(非文件)中的 EXE 数据(二进制) 117 | updateEXECache() { 118 | codelen := MCode(code, this._d.patchdata) 119 | offset := this._d.patchaddr - this._d.patchVAfix ;; Virtual Address -> Raw Offset in EXE 120 | try DllCall("RtlMoveMemory", "ptr", this._pEXEData + offset , "ptr", &code, "uint", codelen) 121 | this._d.doCustomPatch( this._pEXEData ) 122 | } 123 | 124 | ; 检查 EXE 文件的内容是否属于某一东方 STG 游戏 125 | isEXEThisGame() { 126 | if (this._checkFilesize()) { 127 | pehelper := new CPEHelper(this._pEXEData, this._exesize) 128 | return pehelper.isPE && (pehelper.timestamp = this._d.timestamp) 129 | } 130 | return False 131 | } 132 | 133 | ;private 134 | _checkFilesize() { 135 | ; 这么写方便用 msgbox 调试 136 | s1 := this._exesize 137 | s2 := this._d.minfilesize 138 | return s1 >= s2 139 | } 140 | 141 | ; 将 KeymapValue 作为键查询 key_names 中的键名 142 | ; 由于可能有多个,所以返回列表 143 | _findKeymapKeyFromValue(KeymapValue, key_names) { 144 | result := [] 145 | for k, v in key_names { 146 | if ( v = KeymapValue ) { 147 | result.Insert(k) 148 | } 149 | } 150 | return result 151 | } 152 | 153 | ; 从 this._d.inifilepath 文件中读取 154 | ; 如果从 section 段中读取失败,则尝试 includesection ,都失败则 AddLog 并返回 "ERROR" 155 | ; 如果返回空,则同样调用 AddLog() 156 | ; log_message 用于返回执行时的 log。应该传给 AddLog() 。 157 | ; 由于本方法内部可能会递归,在本方法内部调用 AddLog() 可能会造成多个重复的 log,所以调用 AddLog() 的任务交给了调用者 158 | _readKeymapFromINI(keymap, section, includesection, ByRef log_message) { 159 | inifilepath := this._d.inifilepath 160 | IniRead, newkeymap, %inifilepath%, %section%, %keymap% 161 | if ( newkeymap = "ERROR" ) { 162 | ; 读取 INCLUDE 段中的相应设置 163 | if ( includesection != "ERROR" ) { 164 | IniRead, newkeymap, %inifilepath%, %includesection%, %keymap% 165 | } 166 | if ( newkeymap = "ERROR" ) { 167 | log_message := "{未指定}" . keymap 168 | return newkeymap ; 该键位的设置不存在,跳过 169 | } 170 | } 171 | 172 | ; 等号右边为空,可以忽略掉被 INCLUDE 的设置。 173 | if ( newkeymap = "" ) { 174 | log_message := "{已忽略}:" . keymap 175 | } 176 | else { 177 | ; 看看读取的值中有无冒号,有的话则将左边那部分当做段名,右边部分当做键名进行引用 178 | ; 比如 th7:UP 表示引用 [th7] 段中的 UP= 右边的值 179 | newkeymap_bycolon := StrSplit(newkeymap, ":") 180 | if ( newkeymap_bycolon[1] != "" && newkeymap_bycolon[2] != "" ) { 181 | ; 引用了其他值,重新读取指定键中的值 182 | newkeymap := this._readKeymapFromINI(newkeymap_bycolon[2], newkeymap_bycolon[1], "ERROR", log_message) 183 | } 184 | } 185 | 186 | return newkeymap 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /Lib/WZSplashWindow.ahk: -------------------------------------------------------------------------------- 1 | 2 | class WZSplashWindow { 3 | static CURSOR_APPSTARTING := 0x7F8A 4 | static CURSOR_WAIT := 0x7F02 5 | 6 | ; NOTE: CURRENTLY THE cursor PARAMETER IS NOT ACTUALLY USED 7 | ; 8 | ; "cursor:=CURSOR_APPSTARTING" is not allowed because the default 9 | ; value for an optional parameter cannot be a variable. 10 | ; So use "cursor:=0x7F8A" instead. 11 | __New(name, title, message, cursor:=0x7F8A) { 12 | this.name := name 13 | this.title := title 14 | this.message := message 15 | ; this.cursor := cursor 16 | this.defaultgui := A_DefaultGui 17 | this._destroyed := False 18 | 19 | Gui, %name%: New, +Owner%defaultgui% -SysMenu HwndhSplash, %title% 20 | Gui, Add, Text, w200 r1 Center HwndhText, %message% 21 | 22 | ; TODO: This will also affect the main window, so disable it at the moment 23 | ; if ( cursor ) { 24 | if ( 0 ) { 25 | Ptr := A_PtrSize ? "Ptr" : "UInt" 26 | GCL_HCURSOR=0xFFFFFFF4 27 | hCursor := DllCall("LoadCursor", Ptr, 0, Ptr, cursor) 28 | DllCall("SetClassLong", Ptr, hSplash, int, GCL_HCURSOR, Ptr, hCursor) 29 | DllCall("SetClassLong", Ptr, hText, int, GCL_HCURSOR, Ptr, hCursor) 30 | } 31 | } 32 | 33 | Show() { 34 | if ( this._destroyed ) { 35 | return 36 | } 37 | 38 | defaultgui := this.defaultgui 39 | Gui, Show, Center AutoSize 40 | Gui, %defaultgui%:Default 41 | Gui, %defaultgui%:+Disabled 42 | } 43 | 44 | Destroy() { 45 | if ( this._destroyed ) { 46 | return 47 | } 48 | 49 | defaultgui := this.defaultgui 50 | name := this.name 51 | Gui, %defaultgui%:-Disabled 52 | Gui, %name%: Destroy 53 | 54 | this._destroyed := True 55 | } 56 | 57 | __Delete() { 58 | this.Destroy() 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Lib/utils.ahk: -------------------------------------------------------------------------------- 1 | ; 本文件是 THKMC 的一部分 2 | ; 3 | ; -- 一些辅助函数 4 | 5 | 6 | ;将10进制数转为16进制数。 7 | ToHex(Hex){ 8 | oldformat := A_FormatInteger 9 | SetFormat, integer, H 10 | Hex += 0 11 | ;~ StringReplace, Hex, Hex, 0x 12 | SetFormat, integer, %oldformat% 13 | return, Hex 14 | } 15 | 16 | Pad0x(s){ 17 | return "0x" . s 18 | } 19 | UnPad0x(s){ 20 | return RegExReplace(s, "^0x", "") 21 | } 22 | ToSigned(n) { 23 | return n > 0x7FFFFFFF ? -(~n) - 1 : n 24 | } 25 | ToUnsigned(n) { 26 | return n & 0xffffffff 27 | } 28 | BSwap32_ahk(a) { 29 | return ((a<<24) & 0xFF000000) | ((a<<8) & 0xFF0000) | ((a>>8) & 0xFF00) | ((a>>24) & 0xFF) 30 | } 31 | 32 | MCode(ByRef code, hex) { 33 | hex := RegExReplace(hex, "i)[^0-9a-f]") ; delete all non-hex chars 34 | codelen := StrLen(hex)//2 35 | VarSetCapacity(code,codelen) 36 | Loop % codelen 37 | NumPut("0x" . SubStr(hex,2*A_Index-1,2), code, A_Index-1, "Char") 38 | return codelen 39 | } 40 | 41 | ; CURRENTLY ONLY LONG-JMP IS SUPPORTED 42 | ; addrfix is instruction-dependent, 43 | ; for example: 44 | ; For jmp,call, addrfix is 5; 45 | ; For je,jnz, addrfix is 6 46 | addr2CallAddr(callAddr, targetAddr, addrfix=5) { 47 | return (targetAddr-(callAddr+addrfix)) & 0xffffffff 48 | } 49 | 50 | addr2Hex(addr) { 51 | return PadLeadingZero( UnPad0x(ToHex(BSwap32_ahk(addr))), 8 ) 52 | } 53 | 54 | ; opr_longjmp := operand for longjmp, here 'jmp' includes jmp,jz,jnz ... 55 | ; return hex representation of the jmp instruction 56 | longjmpOrShortjmp(opr_longjmp, opr_shortjmp, addr) { 57 | ; addr+3 for shortjmp, because the instruction would be 3 bytes shorter than longjmp 58 | return addr > 0x7f 59 | ? opr_longjmp . " " . addr2Hex(addr) 60 | : opr_shortjmp . " " . PadLeadingZero( UnPad0x(ToHex(addr+3)), 2 ) 61 | } 62 | 63 | PadLeadingZero(s, len) { 64 | slen := strlen(s) 65 | zlen := len - slen 66 | zeros = 67 | Loop % zlen 68 | zeros .= "0" 69 | return zeros . s 70 | } 71 | 72 | 73 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 74 | ; LOG 相关 75 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 76 | 77 | AddLog(text, seterror=False) { 78 | global logtext, HasError 79 | 80 | if ( seterror ) { 81 | HasError := True 82 | } 83 | 84 | if logtext= 85 | logtext := text 86 | else 87 | logtext .= "`n" . text 88 | } 89 | 90 | AddLogIfNotEmpty(text, params*) { 91 | if ( StrLen(text) != 0 ) { 92 | AddLog(text, params*) 93 | } 94 | } 95 | 96 | ; 返回原先的 HasError 标志 97 | ClearLog() { 98 | global logtext, HasError 99 | logtext= 100 | _HasError := HasError 101 | HasError:=False 102 | return _HasError 103 | } 104 | 105 | /* ; DEPRECATED 106 | DoShowLog() { 107 | global 108 | if (HasError || ShowLog) { 109 | OnMessage(0x53, "WM_HELP") 110 | Gui, +OwnDialogs ; WM_HELP needs a GUI 111 | if (HasError) { 112 | MsgBox, 16400, %title%, %logtext% 113 | } 114 | else { 115 | MsgBox, 16448, %title%, %logtext% 116 | } 117 | } 118 | } 119 | */ 120 | 121 | DoShowLog() { 122 | global logtext, HasError, ShowLog, chkTestMode 123 | 124 | if (!HasError && !ShowLog) { 125 | return 126 | } 127 | 128 | global vLogText, vOK, vHelp 129 | Gui, WndLog:New, +Owner1 -SysMenu +Resize +LastFound, 结果 - THKMC 130 | 131 | if ( chkTestMode ) { 132 | Gui, Color, Green 133 | } 134 | 135 | if RegExMatch(logtext, "`nm)^【警告】") { 136 | Gui, Color, Yellow 137 | } 138 | 139 | if ( HasError ) { 140 | Gui, Color, Red 141 | } 142 | Gui, Add, Edit, w500 r30 vvLogText, %logtext% 143 | Gui, Add, Button, gCloseLogWindow Default w150 vvOK, 确定 144 | Gui, Add, Button, gWM_HELP x+20 w150 vvHelp, 帮助(&H) 145 | Gui, Show, Center AutoSize 146 | Gui, 1:+Disabled 147 | GuiControl, Focus, vOK 148 | WinWaitClose 149 | Gui, 1:Default 150 | } 151 | 152 | WndLogGuiSize(GuiHwnd, EventInfo, Width, Height) { 153 | if ( EventInfo != 1 ) { 154 | neww := Width - 20 155 | newh := Height - 40 156 | GuiControl, Move, vLogText, w%neww% h%newh% 157 | newh := Height - 30 158 | GuiControl, Move, vOK, y%newh% 159 | GuiControl, Move, vHelp, y%newh% 160 | } 161 | } 162 | 163 | CloseLogWindow() { 164 | Gui, 1:-Disabled 165 | Gui, WndLog: Destroy 166 | } 167 | 168 | 169 | WM_HELP(wParam, lParam) { 170 | RunHelpAndExit() 171 | } 172 | 173 | RunHelpAndExit() { 174 | global helpfilecmd, title 175 | 176 | ; 只打开第一个找到的 177 | loop, Parse, helpfilecmd, `, 178 | { 179 | if ( RegExMatch(A_LoopField, "^https?://")) { ; URL 180 | CriticalRun(A_LoopField) 181 | return 182 | } 183 | else if ( FileExist(A_LoopField) ) { 184 | cmd := A_LoopField 185 | ; 如果文件名以 txt 或 md 结尾,则用 notepad 打开 186 | if ( RegExMatch(cmd, "\.(md)$") ) { 187 | cmd := "notepad " . cmd 188 | } 189 | CriticalRun(cmd) 190 | return 191 | } 192 | } 193 | ; 没找到 194 | Gui, +OwnDialogs ; WM_HELP needs a GUI 195 | MsgBox, 16, %title%, 没有找到帮助文件…… 196 | 197 | ; ExitApp 198 | } 199 | 200 | ; 运行 cmd。 201 | ; 如果运行失败,用 MsgBox 显示出错信息 202 | ; 注:如果 workingdir 为 "CMDDIR"(必须为大写),则使用 cmd 的 dir 部分作 workingdir 203 | CriticalRun(cmd, workingdir="") { 204 | global title 205 | 206 | if (workingdir == "CMDDIR") 207 | SplitPath, cmd, , workingdir 208 | 209 | Run, %cmd%, %workingdir%, UseErrorLevel 210 | le := A_LastError 211 | if ( le != 0 ) { 212 | errmsg := GetErrorMessage(le) 213 | OnMessage(0x53, "WM_HELP") 214 | Gui, +OwnDialogs 215 | MsgBox, 16400, %title%, 216 | (LTrim 217 | 无法运行指定文件: 218 | 【%cmd%】 219 | 220 | 错误码:%le% 221 | 错误信息:%errmsg% 222 | ) 223 | } 224 | } 225 | 226 | ;根据错误码返回错误信息。 227 | GetErrorMessage(ErrorCode) { 228 | VarSetCapacity(ErrorMsg, 256, 0) 229 | 230 | ;#define FORMAT_MESSAGE_FROM_SYSTEM 0x00001000 231 | DLLCall("FormatMessage","UInt",0x1000 ,"UInt",0,"UInt",ErrorCode,"UInt",0,"Str" 232 | ,ErrorMsg,"Uint", 256, "UInt", 0) 233 | 234 | StringReplace, ErrorMsg, ErrorMsg, `r`n, , All ;删除换行 235 | 236 | return ErrorMsg 237 | } 238 | 239 | 240 | 241 | 242 | ; 从 array 中随机选取一个元素 243 | ; arr: 必须是一个 array 244 | array_choose(arr) { 245 | count := arr.MaxIndex() 246 | Random, sel, 1, count 247 | return arr[sel] 248 | } 249 | 250 | ; 使用 sep 将 array 中的字符串进行连接,返回一个新的字符串 251 | ; sep 默认为 "`r`n" 252 | array_join(arr, sep:="`r`n") { 253 | str= 254 | for i, s in arr 255 | str .= s . sep 256 | return SubStr(str, 1, -StrLen(sep)) 257 | } 258 | 259 | 260 | ; 将以 ASCII-HEX 格式表示的二进制数据与另一组二进制数据进行比较 261 | compareHexToBinary(hex, pBinary, offsetBinary) { 262 | codelen := MCode(code, hex) 263 | try result := DllCall("msvcrt\memcmp", "ptr", pBinary + offsetBinary , "ptr", &code, "uint", codelen, "Cdecl") 264 | return result 265 | } 266 | 267 | ; 将以 ASCII-HEX 表示的二进制数据以覆盖方式写入另一组二进制数据中 268 | writeHexToBinary(hex, pBinary, offsetBinary) { 269 | codelen := MCode(code, hex) 270 | try result := DllCall("RtlMoveMemory", "ptr", pBinary + offsetBinary , "ptr", &code, "uint", codelen) 271 | return result 272 | } 273 | 274 | ; http://rosettacode.org/wiki/Repeat_a_string#AutoHotkey 275 | StringRepeat(s,num) { 276 | o= 277 | Loop, %num% 278 | o .= s 279 | Return o 280 | } 281 | 282 | 283 | ; https://autohotkey.com/boards/viewtopic.php?f=5&t=6436&p=38484 284 | AttachTipToControl(hCtrl, text:="") { 285 | hGui := text!="" ? DlLCall("GetParent", "Ptr", hCtrl) : hCtrl 286 | static hTip 287 | if !hTip 288 | { 289 | hTip := DllCall("CreateWindowEx", "UInt", 0x8, "Str", "tooltips_class32" 290 | , "Ptr", 0, "UInt", 0x80000142 ;// WS_POPUP:=0x80000000|TTS_NOPREFIX:=0x02 // added by wz520: TTS_BALLOON:=40 | TTS_USEVISUALSTYLE:=0x100 291 | , "Int", 0x80000000, "Int", 0x80000000, "Int", 0x80000000, "Int", 0x80000000 292 | , "Ptr", hGui, "Ptr", 0, "Ptr", 0, "Ptr", 0, "Ptr") 293 | 294 | ; TTM_SETMAXTIPWIDTH = 0x0418 295 | DllCall("SendMessage", "Ptr", hTip, "Int", 0x0418, "Ptr", 0, "Ptr", 0) 296 | 297 | if (A_OsVersion == "WIN_XP") 298 | AttachTipToControl(hGui) 299 | } 300 | 301 | static sizeof_TOOLINFO := 24 + (6 * A_PtrSize) 302 | VarSetCapacity(TOOLINFO, sizeof_TOOLINFO, 0) 303 | , NumPut(sizeof_TOOLINFO, TOOLINFO, 0, "UInt") 304 | , NumPut(0x0011, TOOLINFO, 4, "UInt") ; TTF_IDISHWND:=0x0001|TTF_SUBCLASS:=0x0010 // added by wz520: TTF_PARSELINKS:=0x1000 305 | , NumPut(hGui, TOOLINFO, 8, "Ptr") 306 | , NumPut(hCtrl, TOOLINFO, 8 + A_PtrSize, "Ptr") 307 | , NumPut(&text, TOOLINFO, 24 + (3 * A_PtrSize), "Ptr") 308 | 309 | static TTM_ADDTOOL := A_IsUnicode ? 0x0432 : 0x0404 310 | return DllCall("SendMessage", "Ptr", hTip, "Int", TTM_ADDTOOL, "Ptr", 0, "Ptr", &TOOLINFO) 311 | } 312 | 313 | ; 为数值添加千分位分隔符(使用逗号) 314 | ; https://msdn.microsoft.com/en-us/library/dd318110.aspx 315 | ; numdigits: Number of fractional digits. Default to 2 316 | DecimalMark(num, numdigits=2) { 317 | locale := 0x0400 ; LOCALE_USER_DEFAULT 318 | ; struct NUMBERFMT 319 | VarSetCapacity(numberfmt, 4*4+A_PtrSize*2, 0) 320 | NumPut(numdigits, numberfmt, 0, "UInt") ; NumDigits 321 | NumPut(1, numberfmt, 4, "UInt") ; LeadingZero 322 | NumPut(3, numberfmt, 8, "UInt") ; Grouping 323 | strDot := "." 324 | strComma := "," 325 | NumPut(&strDot, numberfmt, 12, "UPtr") ; lpDecimalSep 326 | NumPut(&strComma, numberfmt, 12+A_PtrSize, "UPtr") ; lpThousandSep 327 | NumPut(1, numberfmt, 12+A_PtrSize*2, "UInt") ; NegativeOrder 328 | 329 | size := DllCall("GetNumberFormat", "UInt", locale, "UInt", 0, "Str", num, "Ptr", &numberfmt, "Ptr", 0, "Int", 0) 330 | VarSetCapacity(buf, size * (A_IsUnicode ? 2 : 1), 0) 331 | DllCall("GetNumberFormat", "UInt", locale, "UInt", 0, "Str", num, "Ptr", &numberfmt, "Str", buf, "Int", size) 332 | return SubStr(buf, 1, StrLen(buf) - 0) 333 | } 334 | 335 | -------------------------------------------------------------------------------- /Lib/wzclipboard.ahk: -------------------------------------------------------------------------------- 1 | ; this works under both ANSI/Unicode build of AHK 2 | CopyFilesToClipboard(arrFilepath, bCopy) { 3 | ; set drop effect to determine whether the files are copied or moved. 4 | uDropEffect := DllCall("RegisterClipboardFormat", "str", "Preferred DropEffect", "uint") 5 | hGblEffect := DllCall("GlobalAlloc", "uint", 0x42, "ptr", 4, "ptr") 6 | pGblEffect := DllCall("GlobalLock", "ptr", hGblEffect, "ptr") 7 | ; 0x1=DROPEFFECT_COPY, 0x2=DROPEFFECT_MOVE 8 | NumPut(bCopy ? 1 : 2, pGblEffect+0) 9 | 10 | ; Unlock the moveable memory. 11 | DllCall("GlobalUnlock", "ptr", hGblEffect) 12 | 13 | 14 | charsize := A_IsUnicode ? 2 : 1 15 | AorW := A_IsUnicode ? "W" : "A" 16 | 17 | ; calculate the whole size of arrFilepath 18 | sizeFilepath := charsize*2 ; double null-terminator 19 | For k, v in arrFilepath { 20 | sizeFilepath += (StrLen(v)+1)*charsize 21 | } 22 | 23 | ; 0x42 = GMEM_MOVEABLE(0x2) | GMEM_ZEROINIT(0x40) 24 | hPath := DllCall("GlobalAlloc", "uint", 0x42, "ptr", sizeFilepath + 20, "ptr") 25 | pPath := DllCall("GlobalLock", "ptr", hPath, "ptr") 26 | 27 | NumPut(20, pPath+0) ;pFiles 28 | NumPut(A_IsUnicode, pPath+16) ;fWide 29 | 30 | pPath += 20 31 | 32 | ; Copy the list of files into moveable memory. 33 | For k, v in arrFilepath { 34 | DllCall("lstrcpy" . AorW, "ptr", pPath+0, "str", v) 35 | pPath += (StrLen(v)+1)*charsize 36 | } 37 | 38 | ; Unlock the moveable memory. 39 | DllCall("GlobalUnlock", "ptr", hPath) 40 | 41 | DllCall("OpenClipboard", "ptr", 0) 42 | ; Empty the clipboard, otherwise SetClipboardData may fail. 43 | DllCall("EmptyClipboard") 44 | ; Place the data on the clipboard. CF_HDROP=0xF 45 | DllCall("SetClipboardData","uint",0xF,"ptr",hPath) 46 | DllCall("SetClipboardData","uint",uDropEffect,"ptr",hGblEffect) 47 | DllCall("CloseClipboard") 48 | } 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # THKMC - 东方STG专用键盘映射修改工具 2 | 3 | THKMC(TouHou Keyboard Mapping Changer) 是一个为东方STG量身定做的键盘映射修改工具。通过直接修改东方STG游戏主程序文件中与键盘映射相关的代码,实现自定义游戏键位。 4 | 5 | **注意**:被修改的游戏主程序文件可能会被某些把不认识的都当病毒处理的杀毒软件报毒。您可以添加信任,或者放弃使用本软件及其生成的文件。 6 | 7 | ## 特点 8 | 9 | 跟常见的外挂型的键盘映射工具相比,有以下优点: 10 | 11 | * 因为本工具直接修改游戏主程序,所以玩家运行游戏时无需再运行其他辅助工具即可使用自定义键位。 12 | * 绝对不会出现任何延迟(游戏本来就有延迟的情况除外) 13 | * 通常不会出现因为权限问题而改不了键位的情况(除非连对EXE文件的写入权限都没有) 14 | 15 | 当然也有缺点: 16 | * 量身定制,不通用。 17 | * 目前不支持的游戏:《东方花映冢》。 18 | 19 | ## 支持的游戏版本 20 | 21 | * 东方红魔乡 1.02h 日文原版 22 | * 东方红魔乡 渔场汉化版(**主程序需脱壳**) 23 | * 东方妖妖梦 1.00b 日文原版、渔场汉化版 24 | * 东方永夜抄 1.00d 日文原版、渔场汉化版 25 | * 东方文花帖 1.02a 日文原版 26 | * 东方风神录 1.00a 日文原版、东方喝茶委员会汉化版 27 | * 东方地灵殿 1.00a 日文原版 28 | * 东方星莲船 1.00b 日文原版 29 | * 东方文花帖DS 1.00a 日文原版 30 | * 妖精大战争 ~ 东方三月精 1.00a 日文原版 31 | * 东方神灵庙 1.00c 日文原版 32 | * 东方辉针城 1.00b 日文原版、喵玉汉化版 33 | * 弹幕天邪鬼 1.00a 日文原版、喵玉汉化版 34 | * 东方绀珠传 1.00b 日文原版、喵玉汉化版 35 | * 东方天空璋 1.00a 日文原版、喵玉汉化版 36 | * 秘封噩梦日记 1.00a 日文原版 37 | * 东方鬼形兽 0.01a 体验版 38 | * 东方鬼形兽 1.00b 日文原版 39 | * 黄昏酒场 1.00a 40 | 41 | ## 文档 42 | 43 | * 完整的使用说明请参考: 44 | 45 | ## 如何运行 46 | 47 | 本程序使用 AutoHotkey 语言编写,可以直接使用 AutoHotkey 1.1.30.01 或更高版本的解释器运行。 48 | 49 | 假设你已经把本仓库的代码下载到了你机器上。 50 | 51 | * __如果您不知道如何下载,或者懒得安装 Git,可以直接点击本页面右上方绿色的 `Clone or download` 按钮,然后在弹出的框中选择 `Download ZIP` 即可下载到包含了本程序所有源码的 ZIP 压缩包。__ 52 | 53 | 1. 点击 [此处](https://autohotkey.com/) 进入 AutoHotkey 的官网。 54 | 2. 点击中间绿色的 `Download`。 55 | 3. 点击的 `Download Current Version`。开始下载最新版本的 AutoHotkey 安装包。 56 | 4. 运行安装包。其中在选择默认的解释器版本这一步请选择 `ANSI 32-bit` 或者 `Unicode 32-bit`。目前本程序暂时还不能在 64-bit 的解释器上运行。 57 | 5. 安装 AutoHotkey 完成后,双击本仓库里的 `thkmc.ahk` 即可启动本程序。 58 | 59 | ## 如何编译 60 | 61 | * [AutoHotkey 1.1.30.01+] 62 | + 按照上面 `如何运行` 所描述的步骤安装即可。 63 | 64 | * [Compile_AHK 0.9.1.3](https://autohotkey.com/board/topic/21189-compile-ahk-ii-for-those-who-compile/) 65 | + 安装完成后。右键点击 `thkmc.ahk`,选择 `Compile with Options`,然后直接点击左下角的 `Compile` 按钮即可。 66 | 67 | * [MPRESS 2.19(Optional)](http://www.matcode.com/mpress.htm) 68 | + 如果不安装 MPRESS,编译后的 EXE 文件大约有 1MB 左右。 69 | + 如果安装 MPRESS,使用上述步骤编译完本脚本后会自动调用 MPRESS 进行压缩。编译后的 EXE 文件会缩小到不到 400KB 。但可能会加大被杀毒软件误报的几率。 70 | 71 | ## 编译版&更新历史 72 | 73 | * Github: [Releases] 74 | * [喵玉殿论坛上的THKMC发布帖] 75 | 76 | ## 我编写的其他小程序 77 | 78 | * 79 | 80 | ## 如果您愿意捐助一点小钱 81 | 82 | ### 支付宝: 83 | ![支付宝](https://i.loli.net/2019/02/08/5c5d3764d5dd8.png "支付宝") 84 | 85 | ### 支付宝扫红包(使用支付宝扫一扫,你我都能得红包): 86 | ![支付宝扫红包](https://i.loli.net/2019/03/15/5c8b3c84994ff.png "支付宝扫红包") 87 | 88 | [AutoHotkey 1.1.30.01+]: https://autohotkey.com/ 89 | [Releases]: https://github.com/wz520/thkmc/releases 90 | [喵玉殿论坛上的THKMC发布帖]: http://bbs.nyasama.com/forum.php?mod=viewthread&tid=78904 91 | -------------------------------------------------------------------------------- /docs/GNUmakefile: -------------------------------------------------------------------------------- 1 | # Make documentation using mingw32-make 2 | # 3 | # Need additional programs: pandoc 2.7.2, gvim 4 | 5 | 6 | SHELL=cmd.exe 7 | 8 | TARGETS=help.html thkmc_sample_ini.html 9 | 10 | RM=del 11 | 12 | all: $(TARGETS) 13 | 14 | help.html: help.md 15 | pandoc -f markdown+multiline_tables+table_captions-implicit_figures -s --toc --toc-depth=4 --email-obfuscation=references -V lang=zh --css=style.css --metadata pagetitle="THKMC Help" -o $@ $< 16 | 17 | thkmc_sample_ini.html: ../thkmc_sample.ini | tohtmlhelper.vim 18 | gvim -S tohtmlhelper.vim $< 19 | 20 | .INTERMEDIATE: tohtmlhelper.vim 21 | tohtmlhelper.vim: 22 | @echo ---- Creating $@ ... 23 | echo set nonumber nofoldenable > $@ 24 | echo set fenc=utf-8 bomb >> $@ 25 | echo TOhtml >> $@ 26 | echo g/^/normal cc^THKMC_SAMPLE^ >> $@ 27 | echo sav! thkmc_sample_ini.html >> $@ 28 | echo qa! >> $@ 29 | @echo ---- END 30 | 31 | 32 | clean: 33 | -$(RM) $(TARGETS) 34 | 35 | 36 | .SUFFIXES: 37 | 38 | .PHONY: clean 39 | -------------------------------------------------------------------------------- /docs/help.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | THKMC Help 8 | 14 | 15 | 18 | 19 | 20 | 37 |

THKMC - 东方STG专用键盘映射修改工具

38 |

当前版本: 1.40

39 |

功能

40 |

众所周知,ZUN 制作的 东方STG 从来就不自带更改键盘键位的功能。为此,各路大神都写过各种键盘映射工具,但大多是外挂式的。随着新版本的 Windows 对系统安全性的要求越来越高,这些外挂式的改键工具都可能存在兼容性问题。

41 |

THKMC(TouHou Keyboard Mapping Changer) 通过修改东方STG游戏主程序文件中与键盘映射相关的代码,实现自定义游戏键位。

42 |

东方STG获取键盘按键状态的方法有两种:使用 DirectInput不使用 DirectInput 。 其中 使用 DirectInput 是通过调用 IDirectInputDevice8::GetDeviceState() 来获取键盘状态的;而 不使用 DirectInput 则是使用 Win32 API 中的 GetKeyboardState()

43 |

本程序 只支持 对使用 DirectInput 方式获取的按键映射进行更改。所以要求在游戏自带的 custom.exe 程序中不能勾选 “不使用 DirectInput 获取键盘输入”。若勾选的话使用本程序改键将没有任何效果。(不过一般不会有人去勾选吧,因为游戏默认的不勾选也能工作得很好。)

44 |

特点

45 |

相比较其他常见的外挂式通用键位修改工具,因为 THKMC 是为特定的EXE文件量身定制的,所以只要游戏版本正确且用户写的配置文件内容无问题,改键后通常不会有出错或者失效的现象发生。

46 |

本程序可修改的键位不仅包括在STG中用到的各种控制键(↑、↓、←、→、Z、X、C、SHIFT、CTRL、ESC等),还包括:

47 |
    48 |
  • 截图键(HomeP
  • 49 |
  • RESC+R 可以快速 RETRY) 50 |
  • 53 |
  • D(永夜抄 RESULT 画面会用到,可以将游戏纪录保存到文件中)
  • 54 |
  • 以及其他游戏中用到的一切键位。 55 |
      56 |
    • 不过 Windows 窗口程序的通用键位是改不了的,比如 Alt+Space 打开系统菜单, Alt+F4 关闭窗口什么的,这是 Windows 管的,本工具无法对这些系统组合键进行重映射。
    • 57 |
  • 58 |
59 |

除此之外,还有这些优点:

60 |
    61 |
  • 对于同一个玩家来讲,改键位一般不是经常要干的事情。直接改 EXE 的方法可以做到一劳永逸,玩家运行游戏时无需再运行其他辅助工具。
  • 62 |
  • 修改 EXE 只需操作者拥有对该 EXE 文件的读取和写入权限,所以通常不会有因为用户权限或内存布局等原因而导致修改失败的问题发生。
  • 63 |
64 |

当然缺点也很明显,就是不通用,除了下面 支持的游戏版本 中所列出的游戏的主程序文件外,不支持任何其他的文件。

65 |
    66 |
  • 另外,也不能为同一个功能映射多个按键,不过游戏本来就已经有多个映射的除外。 67 |
      68 |
    • 比如在几乎所有的东方STG游戏中 小键盘8 的作用和 方向键 相同,属于一个功能有多个映射的例子,本程序可以分别将它们映射到不同的功能上。
    • 69 |
  • 70 |
71 |

支持的游戏版本

72 |
    73 |
  • 东方红魔乡 1.02h 日文原版
  • 74 |
  • 东方红魔乡 渔场汉化版(主程序需脱壳
  • 75 |
  • 东方妖妖梦 1.00b 日文原版、渔场汉化版
  • 76 |
  • 东方永夜抄 1.00d 日文原版、渔场汉化版
  • 77 |
  • 东方文花帖 1.02a 日文原版
  • 78 |
  • 东方风神录 1.00a 日文原版、东方喝茶委员会汉化版
  • 79 |
  • 东方地灵殿 1.00a 日文原版
  • 80 |
  • 东方星莲船 1.00b 日文原版
  • 81 |
  • 东方文花帖DS 1.00a 日文原版
  • 82 |
  • 妖精大战争 ~ 东方三月精 1.00a 日文原版
  • 83 |
  • 东方神灵庙 1.00c 日文原版
  • 84 |
  • 东方辉针城 1.00b 日文原版、喵玉汉化版
  • 85 |
  • 弹幕天邪鬼 1.00a 日文原版、喵玉汉化版
  • 86 |
  • 东方绀珠传 1.00b 日文原版、喵玉汉化版
  • 87 |
  • 东方天空璋 1.00a 日文原版、喵玉汉化版
  • 88 |
  • 秘封噩梦日记 1.00a 日文原版
  • 89 |
  • 东方鬼形兽 0.01a 体验版
  • 90 |
  • 东方鬼形兽 1.00b 日文原版
  • 91 |
  • 黄昏酒场 1.00a
  • 92 |
93 |

注意:若有哪一作未提到支持汉化版,只是未测试而已,多数情况下只要游戏版本号相同应该也能支持

94 |

对于其他打了非官方补丁的版本,本程序可能也能识别,但不保证能达到正确的修改效果。

95 |
    96 |
  • 喵玉汉化版请选择原版的 exe(比如 th14.exe),而不是带 c 的 exe(比如 th14c.exe。那只是个启动器而已,而非游戏主程序)。
  • 97 |
  • 花映冢由于比较复杂,所以暂时坑掉。
  • 98 |
  • 地灵殿目前其实是可以打补丁的,但是打了补丁也没用,原因在ZUN。请参见下面的 常见问题 从 1.20 版起已经全面支持。
  • 99 |
100 |

使用方法

101 |
    102 |
  1. 启动 thkmc.exe 。初次启动界面如图所示。 103 |
      104 |
    • 105 |
    • 注意:启动时若 thkmc.ini 文件不存在,会提示该文件已被自动生成。
    • 106 |
  2. 107 |
  3. 点击左上角的“编辑 thkmc.ini”,会使用默认程序(通常是记事本)打开 thkmc.ini。你可以根据自己的喜好更改键位。 108 |
      109 |
    • 110 |
    • 若你未修改过自动生成的 thkmc.ini 文件,则按照以下规则修改键位: 111 |
        112 |
      • ↑=W
      • 113 |
      • ↓=S
      • 114 |
      • ←=A
      • 115 |
      • →=D
      • 116 |
      • Z=J
      • 117 |
      • X=K
      • 118 |
      • C=L
      • 119 |
      • 左SHIFT=I
      • 120 |
    • 121 |
    • 若要改成其他键位请参考本程序源代码中的 KeyNamesDX.ahk
    • 122 |
    • 举个栗子:比如如果要将 ESC 键映射为 ` 键(即“反引号”键。通常位于主键盘数字 1 键的左边),则可以在 LSHIFT=i 下面插入一行 ESC=GRAVE 即可。 123 |
        124 |
      • 除了字母键外,thkmc.ini 文件中其他的按键都必须写键位名称,这需要使用者自行参考 KeyNamesDX.ahk,然后将其填入 INI 文件中。其中 THKMC_KeyNamesDX["XXX"] 中的 XXX 部分便是可以使用的键位名称。

      • 125 |
      • 比如该文件中有一行是:

        126 |
          THKMC_KeyNamesDX["GRAVE"] := 0x29 ; accent grave `
      • 127 |
      • 如图:

      • 128 |
      • 右边的注释 accent grave ` 提示 GRAVE` 键的名称。所以将 GRAVE 填入 INI 文件中即可。

      • 129 |
      • 完整的 INI 编写说明可参考 INI设置项说明

      • 130 |
    • 131 |
  4. 132 |
  5. 编辑好后别忘了保存哦。怎么保存文件相信大家都会所以这里就不赘述啦(
  6. 133 |
  7. 点击上面的“打开程序并应用补丁”按钮。(下图以 东方鬼形兽 0.01a 体验版 为例) 134 |
      135 |
    • 136 |
  8. 137 |
  9. 选择一个本程序支持的东方STG的主程序。具体支持哪些游戏主程序请参考上面的 支持的游戏版本 。 138 |
      139 |
    • 140 |
  10. 141 |
  11. THKMC 会自动根据 thkmc.ini 里设置的键位修改程序,并覆盖原EXE,然后报告结果。 142 |
      143 |
    • 144 |
        145 |
      • {未指定} 就是INI文件中没有设置重映射,但是本游戏程序支持该键被重映射。比如说这个结果窗口里写了 {未指定} F10,就说明东方鬼形兽支持 F10 键(切换全屏/窗口) 被重映射,如果你想要改成比如 F12,你可以在INI文件里写 F10=F12 ,那么以后按 F12 就可以切换全屏了。
      • 146 |
      • 另外要注意,能被重映射的键位,不一定表示该键位一定有功能。比如 D 键,曾经在永夜抄里有作用,而后来的作品基本都用不到了。但是你可以看到东方鬼形兽还会监视 D 键,但是其实是没有功能的。所以我们可以写 RIGHT=D 来赋予 D 键新的功能。
      • 147 |
    • 148 |
    • 在覆盖原文件前, THKMC 会根据主界面中 备份到 *.thkmcbackup? 的设置,执行自动备份。
    • 149 |
    • 如果主窗口的 测试模式 复选框处于选中状态,也会弹出该结果框,但并不会真正地修改游戏主程序,备份文件也不会被生成(且结果框底色通常会是原谅绿)。
    • 150 |
  12. 151 |
  13. 成功应用补丁的EXE会添加到列表中。 152 |
      153 |
    • 154 |
    • 如果主窗口的 总是添加至列表 复选框处于选中状态,那么即使打补丁失败,其文件路径也会添加的列表中。 155 |
        156 |
      • 什么?你说“上面的图里找不到 总是添加至列表 复选框”?嘛,还是那句话:图片仅供参考,请以实物为准hhhhh 天之声:你就不会说你懒得再截图吗
      • 157 |
    • 158 |
    • 以后如果要换一套键位,可以先修改 INI 文件(记得保存文件!),然后通过上面的“应用补丁”按钮或者右键菜单中的“应用改键补丁”命令再次应用补丁,这样就可以使用新键位了。
    • 159 |
    • 也可以通过双击直接运行该程序,甚至还可以在右键菜单中运行 vpatch、custom.exe 等。所以也可以把 THKMC 当一个简单的 东方STG游戏启动器 来使用。
    • 160 |
  14. 161 |
162 |

使用时的注意事项

163 |
    164 |
  • 本程序仅仅通过 EXE 的文件大小和 PE 文件头中的UNIX时间戳(dwTimestamp) 来判断是哪个游戏,所以应该可以兼容各种打过第三方补丁(包括汉化补丁)的版本。

    165 |
      166 |
    • 注意】一个例外是红魔乡渔场汉化版,这个版本的 EXE 因为是加过壳的,所以无法直接修改,必须要脱过壳的版本才可以。 167 |
        168 |
      • 而且网上流传的红魔乡渔场汉化版与最新的日文版版本号不同,并非 1.02h (这也是为什么只有红魔乡的汉化版不能使用VP的原因)。为此有人针对 1.02h 做过一个汉化版,可以使用 VP。获取方式请参考 喵玉殿论坛上的THKMC发布帖
      • 169 |
    • 170 |
  • 171 |
  • 根据打补丁成功与否,“结果”窗口的底色(非文本框颜色)会有所不同。

    172 |
      173 |
    • 姨妈红:有错误。
    • 174 |
    • 菊花黄:有警告(常见的情况是 INI 文件中有至少一个键位名称没有写对,导致 THKMC 无法识别)。
    • 175 |
    • 原谅绿:测试模式已开启。改键补丁并未实际打到游戏程序上。请在主窗口中取消 测试模式 勾选,然后重试。
    • 176 |
    • 正常颜色:打补丁成功。
    • 177 |
    • 优先级为 姨妈红>菊花黄>原谅绿。 178 |
        179 |
      • 假如既有错误,又有警告,又开启了测试模式,那么底色会是姨妈红,因为优先级最高。
      • 180 |
    • 181 |
  • 182 |
  • 由于作者患有懒癌晚期,本程序在修改前不会校验被修改的数据,所以如果有哪个第三方补丁修改的地方恰好与本程序相同,或者在补丁数据位置之前插入了其他数据导致位置发生了偏差,那么应用改键补丁后的结果将没有人知道

  • 183 |
184 |

使用技巧

185 |
    186 |
  • 可以一次打开多个文件。
  • 187 |
  • 可以一次对多个文件应用补丁。其结果会统一显示在一个结果窗口中。
  • 188 |
  • 支持文件拖拽。 189 |
      190 |
    • (1.40版起)如果拖入的是目录,会自动遍历其中的所有 .exe 文件(包括子目录)。
    • 191 |
  • 192 |
193 |

INI设置项说明

194 |

请参阅软件目录下的 thkmc_sample.ini 文件。

195 | 198 |

常见问题

199 |
    200 |
  • 如何从打过补丁后的EXE中获取键位?

    201 |
    202 |

    目前无法获取。

    203 |
  • 204 |
  • 地灵殿键位修改无效。

    205 |
    206 |

    请更新到最新版本的 THKMC。

    207 |
  • 208 |
  • 各种杀软报毒?

    209 |
    210 |

    本程序使用 AutoHotkey 语言编写,并使用 MPRESS 压缩。可能有些杀软对 MPRESS 壳敏感。若报毒请添加信任。

    211 |

    作者承诺决不暗藏任何恶意代码。不信自己看源代码吧。

    212 |
  • 213 |
214 |

联系作者

215 |

@wz520 度娘贴吧ID: 天使的枷锁

216 |

下载地址&更新历史

217 |

本程序的新版本可以在以下地址获取到:

218 |

编译版&更新历史:

219 | 223 |

源代码:

224 | 227 |

我编写的其他小程序:

228 | 231 | 232 | 233 | -------------------------------------------------------------------------------- /docs/help.md: -------------------------------------------------------------------------------- 1 | # THKMC - 东方STG专用键盘映射修改工具 2 | 3 | 当前版本: `1.40` 4 | 5 | ## 功能 6 | 7 | 众所周知,ZUN 制作的 东方STG 从来就不自带更改键盘键位的功能。为此,各路大神都写过各种键盘映射工具,但大多是外挂式的。随着新版本的 Windows 对系统安全性的要求越来越高,这些外挂式的改键工具都可能存在兼容性问题。 8 | 9 | THKMC(TouHou Keyboard Mapping Changer) 通过修改东方STG游戏主程序文件中与键盘映射相关的代码,实现自定义游戏键位。 10 | 11 | 东方STG获取键盘按键状态的方法有两种:`使用 DirectInput` 和 `不使用 DirectInput` 。 12 | 其中 `使用 DirectInput` 是通过调用 [IDirectInputDevice8::GetDeviceState()] 来获取键盘状态的;而 `不使用 DirectInput` 则是使用 Win32 API 中的 [GetKeyboardState()]。 13 | 14 | 本程序 **只支持** 对使用 DirectInput 方式获取的按键映射进行更改。所以要求在游戏自带的 custom.exe 程序中不能勾选 “不使用 DirectInput 获取键盘输入”。若勾选的话使用本程序改键将没有任何效果。(不过一般不会有人去勾选吧,因为游戏默认的不勾选也能工作得很好。) 15 | 16 | ## 特点 17 | 18 | 相比较其他常见的外挂式通用键位修改工具,因为 THKMC 是为特定的EXE文件量身定制的,所以只要游戏版本正确且用户写的配置文件内容无问题,改键后通常不会有出错或者失效的现象发生。 19 | 20 | 本程序可修改的键位不仅包括在STG中用到的各种控制键(↑、↓、←、→、Z、X、C、SHIFT、CTRL、ESC等),还包括: 21 | 22 | * 截图键(`Home`或`P`) 23 | * `R`( `ESC+R` 可以快速 RETRY) 24 | + 鬼形兽 1.00b 除外。因为游戏本来就不支持该功能,疑似BUG。详见 [THBWIKI:官方游戏错误解决办法] 页面 25 | * `D`(永夜抄 RESULT 画面会用到,可以将游戏纪录保存到文件中) 26 | * 以及其他游戏中用到的一切键位。 27 | + 不过 Windows 窗口程序的通用键位是改不了的,比如 `Alt+Space` 打开系统菜单, `Alt+F4` 关闭窗口什么的,这是 Windows 管的,本工具无法对这些系统组合键进行重映射。 28 | 29 | 除此之外,还有这些优点: 30 | 31 | * 对于同一个玩家来讲,改键位一般不是经常要干的事情。直接改 EXE 的方法可以做到一劳永逸,玩家运行游戏时无需再运行其他辅助工具。 32 | * 修改 EXE 只需操作者拥有对该 EXE 文件的读取和写入权限,所以通常不会有因为用户权限或内存布局等原因而导致修改失败的问题发生。 33 | 34 | 当然缺点也很明显,就是不通用,除了下面 `支持的游戏版本` 中所列出的游戏的主程序文件外,不支持任何其他的文件。 35 | 36 | * 另外,也不能为同一个功能映射多个按键,不过游戏本来就已经有多个映射的除外。 37 | + 比如在几乎所有的东方STG游戏中 `小键盘8` 的作用和 `↑` 方向键 相同,属于一个功能有多个映射的例子,本程序可以分别将它们映射到不同的功能上。 38 | 39 | ## 支持的游戏版本 40 | 41 | * 东方红魔乡 1.02h 日文原版 42 | * 东方红魔乡 渔场汉化版(**主程序需脱壳**) 43 | * 东方妖妖梦 1.00b 日文原版、渔场汉化版 44 | * 东方永夜抄 1.00d 日文原版、渔场汉化版 45 | * 东方文花帖 1.02a 日文原版 46 | * 东方风神录 1.00a 日文原版、东方喝茶委员会汉化版 47 | * 东方地灵殿 1.00a 日文原版 48 | * 东方星莲船 1.00b 日文原版 49 | * 东方文花帖DS 1.00a 日文原版 50 | * 妖精大战争 ~ 东方三月精 1.00a 日文原版 51 | * 东方神灵庙 1.00c 日文原版 52 | * 东方辉针城 1.00b 日文原版、喵玉汉化版 53 | * 弹幕天邪鬼 1.00a 日文原版、喵玉汉化版 54 | * 东方绀珠传 1.00b 日文原版、喵玉汉化版 55 | * 东方天空璋 1.00a 日文原版、喵玉汉化版 56 | * 秘封噩梦日记 1.00a 日文原版 57 | * 东方鬼形兽 0.01a 体验版 58 | * 东方鬼形兽 1.00b 日文原版 59 | * 黄昏酒场 1.00a 60 | 61 | 62 | **注意**:若有哪一作未提到支持汉化版,只是未测试而已,多数情况下只要游戏版本号相同应该也能支持 63 | 64 | _对于其他打了非官方补丁的版本,本程序可能也能识别,但不保证能达到正确的修改效果。_ 65 | 66 | * 喵玉汉化版请选择原版的 exe(比如 th14.exe),而不是带 c 的 exe(比如 th14c.exe。那只是个启动器而已,而非游戏主程序)。 67 | * **花映冢**由于比较复杂,所以暂时坑掉。 68 | * **地灵殿**目前其实是可以打补丁的,但是打了补丁也没用,原因在ZUN。请参见下面的 `常见问题`。 从 1.20 版起已经全面支持。 69 | 70 | ## 使用方法 71 | 72 | 1. 启动 thkmc.exe 。初次启动界面如图所示。 73 | * ![](https://i.loli.net/2019/05/18/5cdffe2ac806134694.png "主界面") 74 | * **注意**:启动时若 thkmc.ini 文件不存在,会提示该文件已被自动生成。 75 | 2. 点击左上角的“编辑 thkmc.ini”,会使用默认程序(通常是`记事本`)打开 thkmc.ini。你可以根据自己的喜好更改键位。 76 | * ![](https://i.loli.net/2019/05/18/5cdffe2abae5c22341.png "编辑按钮") 77 | * 若你未修改过自动生成的 thkmc.ini 文件,则按照以下规则修改键位: 78 | + ↑=W 79 | + ↓=S 80 | + ←=A 81 | + →=D 82 | + Z=J 83 | + X=K 84 | + C=L 85 | + 左SHIFT=I 86 | * 若要改成其他键位请参考本程序源代码中的 [KeyNamesDX.ahk] 。 87 | * **举个栗子**:比如如果要将 `ESC` 键映射为 `` ` `` 键(即“反引号”键。通常位于主键盘数字 `1` 键的左边),则可以在 `LSHIFT=i` 下面插入一行 `ESC=GRAVE` 即可。 88 | + 除了字母键外,thkmc.ini 文件中其他的按键都必须写键位名称,这需要使用者自行参考 [KeyNamesDX.ahk],然后将其填入 INI 文件中。其中 `THKMC_KeyNamesDX["XXX"]` 中的 `XXX` 部分便是可以使用的键位名称。 89 | + 比如该文件中有一行是: 90 | 91 | THKMC_KeyNamesDX["GRAVE"] := 0x29 ; accent grave ` 92 | + 如图: ![](https://i.loli.net/2019/05/18/5ce001ba90eb971608.png "反引号键") 93 | + 右边的注释 ``accent grave ` `` 提示 `GRAVE` 是 `` ` `` 键的名称。所以将 `GRAVE` 填入 INI 文件中即可。 94 | + 完整的 INI 编写说明可参考 [INI设置项说明] 。 95 | 3. 编辑好后**别忘了保存**哦。怎么保存文件相信大家都会所以这里就不赘述啦( 96 | 4. 点击上面的“打开程序并应用补丁”按钮。(下图以 `东方鬼形兽 0.01a 体验版` 为例) 97 | * ![](https://i.loli.net/2019/05/18/5ce0003b8930a44219.png "打开程序并应用补丁") 98 | 5. 选择一个本程序支持的东方STG的主程序。具体支持哪些游戏主程序请参考上面的 [支持的游戏版本] 。 99 | * ![](https://i.loli.net/2019/05/18/5cdffe2acb8ca53457.png "选择文件") 100 | 6. THKMC 会自动根据 thkmc.ini 里设置的键位修改程序,并覆盖原EXE,然后报告结果。 101 | * ![](https://i.loli.net/2019/05/18/5cdffe2ac4b9f46747.png "打补丁结果") 102 | - {未指定} 就是INI文件中没有设置重映射,但是本游戏程序支持该键被重映射。比如说这个结果窗口里写了 `{未指定} F10`,就说明东方鬼形兽支持 `F10` 键(切换全屏/窗口) 被重映射,如果你想要改成比如 `F12`,你可以在INI文件里写 `F10=F12` ,那么以后按 `F12` 就可以切换全屏了。 103 | - 另外要注意,能被重映射的键位,不一定表示该键位一定有功能。比如 `D` 键,曾经在永夜抄里有作用,而后来的作品基本都用不到了。但是你可以看到东方鬼形兽还会监视 `D` 键,但是其实是没有功能的。所以我们可以写 `RIGHT=D` 来赋予 `D` 键新的功能。 104 | * 在覆盖原文件前, THKMC 会根据主界面中 `备份到 *.thkmcbackup?` 的设置,执行自动备份。 105 | * 如果主窗口的 `测试模式` 复选框处于选中状态,也会弹出该结果框,但并不会真正地修改游戏主程序,备份文件也不会被生成(且结果框底色通常会是原谅绿)。 106 | 7. 成功应用补丁的EXE会添加到列表中。 107 | * ![](https://i.loli.net/2019/05/18/5cdfff402e49144657.png "主界面2") 108 | * 如果主窗口的 `总是添加至列表` 复选框处于选中状态,那么即使打补丁失败,其文件路径也会添加的列表中。 109 | - 什么?你说“上面的图里找不到 `总是添加至列表` 复选框”?嘛,还是那句话:`图片仅供参考,请以实物为准`hhhhh ~~天之声:你就不会说你懒得再截图吗~~ 110 | * 以后如果要换一套键位,可以先修改 INI 文件(记得保存文件!),然后通过上面的“应用补丁”按钮或者右键菜单中的“应用改键补丁”命令再次应用补丁,这样就可以使用新键位了。 111 | * 也可以通过双击直接运行该程序,甚至还可以在右键菜单中运行 vpatch、custom.exe 等。所以也可以把 THKMC 当一个简单的 `东方STG游戏启动器` 来使用。 112 | 113 | 114 | ### 使用时的注意事项 115 | 116 | * 本程序仅仅通过 EXE 的文件大小和 PE 文件头中的UNIX时间戳(dwTimestamp) 来判断是哪个游戏,所以应该可以兼容各种打过第三方补丁(包括汉化补丁)的版本。 117 | 118 | - 【**注意**】一个例外是红魔乡渔场汉化版,这个版本的 EXE 因为是加过壳的,所以无法直接修改,必须要脱过壳的版本才可以。 119 | + 而且网上流传的红魔乡渔场汉化版与最新的日文版版本号不同,并非 1.02h (这也是为什么只有红魔乡的汉化版不能使用VP的原因)。为此有人针对 1.02h 做过一个汉化版,可以使用 VP。获取方式请参考 [喵玉殿论坛上的THKMC发布帖] 。 120 | 121 | * 根据打补丁成功与否,“结果”窗口的底色(非文本框颜色)会有所不同。 122 | - 姨妈红:有错误。 123 | - 菊花黄:有警告(常见的情况是 INI 文件中有至少一个键位名称没有写对,导致 THKMC 无法识别)。 124 | - 原谅绿:测试模式已开启。改键补丁并未实际打到游戏程序上。请在主窗口中取消 `测试模式` 勾选,然后重试。 125 | - 正常颜色:打补丁成功。 126 | - 优先级为 姨妈红>菊花黄>原谅绿。 127 | + 假如既有错误,又有警告,又开启了测试模式,那么底色会是姨妈红,因为优先级最高。 128 | 129 | * ~~由于作者患有懒癌晚期,~~本程序在修改前不会校验被修改的数据,所以如果有哪个第三方补丁修改的地方恰好与本程序相同,或者在补丁数据位置之前插入了其他数据导致位置发生了偏差,那么应用改键补丁后的结果将**没有人知道**! 130 | 131 | 132 | ### 使用技巧 133 | 134 | * 可以一次打开多个文件。 135 | * 可以一次对多个文件应用补丁。其结果会统一显示在一个结果窗口中。 136 | * 支持文件拖拽。 137 | + (1.40版起)如果拖入的是目录,会自动遍历其中的所有 .exe 文件(包括子目录)。 138 | 139 | ## INI设置项说明 140 | 141 | 请参阅软件目录下的 `thkmc_sample.ini` 文件。 142 | 143 | * 或者直接用浏览器看 [带高亮版的 thkmc_sample.ini](https://wz520.github.io/thkmc/thkmc_sample_ini.html) 。 144 | 145 | 146 | ## 常见问题 147 | 148 | * 如何从打过补丁后的EXE中获取键位? 149 | 150 | > 目前无法获取。 151 | 152 | * **地灵殿键位修改无效。** 153 | 154 | > 请更新到最新版本的 THKMC。 155 | 156 | * 各种杀软报毒? 157 | 158 | > 本程序使用 [AutoHotkey](https://autohotkey.com/) 语言编写,并使用 [MPRESS](http://www.matcode.com/mpress.htm) 压缩。可能有些杀软对 MPRESS 壳敏感。若报毒请添加信任。 159 | > 160 | > 作者承诺决不暗藏任何恶意代码。不信自己看源代码吧。 161 | 162 | ## 联系作者 163 | 164 | @wz520 165 | 度娘贴吧ID: [天使的枷锁](http://tieba.baidu.com/home/main?id=f861cceccab9b5c4bccfcbf83d00&fr=userbar) 166 | [邮箱](mailto:wingzero1040@gmail.com) 167 | 168 | 169 | ## 下载地址&更新历史 170 | 171 | 本程序的新版本可以在以下地址获取到: 172 | 173 | 编译版&更新历史: 174 | 175 | * Github: 176 | * [喵玉殿论坛上的THKMC发布帖] 177 | 178 | 源代码: 179 | 180 | * Github: 181 | 182 | 183 | 我编写的其他小程序: 184 | 185 | * 186 | 187 | 188 | [thkmc_sample.ini]: https://github.com/wz520/thkmc/blob/master/thkmc_sample.ini 189 | [IDirectInputDevice8::GetDeviceState()]: https://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.idirectinputdevice8.idirectinputdevice8.getdevicestate(v=vs.85).aspx 190 | [GetKeyboardState()]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms646299(v=vs.85).aspx 191 | [IDirectInputDevice8::SetCooperativeLevel]: https://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.idirectinputdevice8.idirectinputdevice8.setcooperativelevel(v=vs.85).aspx 192 | [CreateWindowEx()]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms632680(v=vs.85).aspx 193 | [KeyNamesDX.ahk]: https://github.com/wz520/thkmc/blob/master/KeyNamesDX.ahk 194 | [喵玉殿论坛上的THKMC发布帖]: http://bbs.nyasama.com/forum.php?mod=viewthread&tid=78904 195 | 196 | [THBWIKI:官方游戏错误解决办法]: https://thwiki.cc/%E5%AE%98%E6%96%B9%E6%B8%B8%E6%88%8F/%E9%94%99%E8%AF%AF%E8%A7%A3%E5%86%B3%E5%8A%9E%E6%B3%95 197 | -------------------------------------------------------------------------------- /docs/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | /* font-family: Segoe UI, "Microsoft YaHei", Georgia, Helvetica, Arial, sans-serif, 宋体, PMingLiU, serif; */ 3 | font-family: Segoe UI, "Microsoft YaHei", Arial, sans-serif, 宋体, PMingLiU, serif; 4 | font-size: 10.5pt; 5 | line-height: 1.5; 6 | padding-top: 10px; 7 | padding-bottom: 10px; 8 | background-color: #222222; 9 | color: white; 10 | padding: 10px; 11 | } 12 | 13 | strong { 14 | font-size: 1.4em; 15 | background-color: yellowgreen; 16 | color: blue; 17 | border: 3px ridge slateblue; 18 | border-radius: 5px; 19 | transition: color 0.5s, background-color 0.5s; 20 | } 21 | strong:hover { 22 | color: red; 23 | background-color: bisque ; 24 | } 25 | em { 26 | font-size: 1.2em; 27 | } 28 | 29 | /* DEPRECATED */ 30 | /* 31 | em:before { 32 | content: "❢ "; 33 | font-size: 1.5em; 34 | color: crimson; 35 | } 36 | em:after { 37 | content: " ❢"; 38 | font-size: 1.5em; 39 | color: crimson; 40 | } 41 | */ 42 | em:before { 43 | content: "☢CAUTION!!☢ "; 44 | font-size: 1.5em; 45 | color: crimson; 46 | font-style: normal; 47 | font-weight: bold; 48 | } 49 | em:after { 50 | content: " ☢CAUTION!!☢"; 51 | font-size: 1.5em; 52 | color: crimson; 53 | font-style: normal; 54 | font-weight: bold; 55 | } 56 | 57 | 58 | nav#TOC:before { 59 | content: "目录:"; 60 | font-size: 1.5em; 61 | } 62 | nav#TOC{ 63 | border: 3px solid white; 64 | border-radius: 5px; 65 | line-height: 30%; 66 | padding-top: 1em; 67 | width: 25em; 68 | background-color: darkgreen; 69 | } 70 | nav#TOC ul{ 71 | /* list-style-type: square; */ 72 | /* display: inline; */ 73 | } 74 | nav#TOC ul li{ 75 | /* display: inline; */ 76 | } 77 | nav#TOC ul li a{ 78 | color: yellow; 79 | transition: background-color 0.5s, color 0.5s; 80 | } 81 | nav#TOC ul li a:active { 82 | color: red; 83 | } 84 | nav#TOC ul li a:hover { 85 | /* color: fuchsia; */ 86 | /* background-color: darkblue; */ 87 | color: orange; 88 | background-color: darkred; 89 | border: 4px inset gray; 90 | } 91 | 92 | body > *:first-child { 93 | margin-top: 0 !important; 94 | } 95 | body > *:last-child { 96 | margin-bottom: 0 !important; 97 | } 98 | 99 | a {transition: color 0.5s;} 100 | a:link {color:skyblue;} 101 | a:visited {color:skyblue;} 102 | a:hover {color:yellow;background-color:blue;} 103 | a:active {color:red;} 104 | 105 | h1 a:link, h1 a:visited { 106 | color: hotpink; 107 | } 108 | h1 a:hover {color:yellow;background-color:blue;} 109 | h1 a:active {color:red;} 110 | 111 | h2 a:link, h2 a:visited { 112 | color: aqua; 113 | } 114 | h2 a:hover {color:yellow;background-color:blue;} 115 | h2 a:active {color:red;} 116 | 117 | h2:before { 118 | content: "❀ "; 119 | } 120 | 121 | h2.author a:link, h2.author a:visited { 122 | color: chartreuse; 123 | } 124 | h2.author:before { 125 | content: "✍ "; 126 | color: red; 127 | } 128 | h2.author a:hover {color:yellow;background-color:blue;} 129 | h2.author a:active {color:red;} 130 | 131 | h3:before { 132 | content: "☯ "; 133 | } 134 | 135 | /* ADDED */ 136 | h4:before { 137 | /* content: "❂ ";*/ 138 | content: "✦ "; 139 | } 140 | 141 | 142 | h1, h2, h3, h4, h5, h6 { 143 | margin: 20px 0 10px; 144 | padding: 0; 145 | font-weight: bold; 146 | -webkit-font-smoothing: antialiased; 147 | cursor: text; 148 | position: relative; 149 | } 150 | h1 { 151 | font-size: 28px; 152 | color: magenta; 153 | text-align: center; 154 | } 155 | h2 { 156 | font-size: 24px; 157 | border-bottom: 1px solid #cccccc; 158 | color: aqua; 159 | } 160 | h3 { 161 | font-size: 18px; 162 | color: gold; 163 | } 164 | h4 { 165 | font-size: 16px; 166 | } 167 | h5 { 168 | font-size: 14px; 169 | } 170 | h6 { 171 | color: #777777; 172 | font-size: 14px; 173 | } 174 | p, blockquote, ul, ol, dl, li, table, pre { 175 | margin: 15px 0; 176 | } 177 | hr { 178 | background: transparent; 179 | /* border: 0 none; */ 180 | color: #333333; 181 | height: 4px; 182 | padding: 0; 183 | } 184 | body > h2:first-child { 185 | margin-top: 0; 186 | padding-top: 0; 187 | } 188 | body > h1:first-child { 189 | margin-top: 0; 190 | padding-top: 0; 191 | } 192 | body > h1:first-child + h2 { 193 | margin-top: 0; 194 | padding-top: 0; 195 | } 196 | body > h3:first-child, body > h4:first-child, body > h5:first-child, body > h6:first-child { 197 | margin-top: 0; 198 | padding-top: 0; 199 | } 200 | 201 | h1 p, h2 p, h3 p, h4 p, h5 p, h6 p { 202 | margin-top: 0; 203 | } 204 | li p.first { 205 | display: inline-block; 206 | } 207 | ul, ol { 208 | padding-left: 30px; 209 | } 210 | 211 | 212 | /* DEPRECATED */ 213 | /* 214 | ol > li { 215 | border-bottom-width: 1px; 216 | border-bottom-style: dotted; 217 | border-color: goldenrod; 218 | } 219 | */ 220 | ol > li { 221 | border-width: 1px; 222 | border-style: solid; 223 | border-color: goldenrod; 224 | } 225 | 226 | ul :first-child, ol :first-child { 227 | margin-top: 0; 228 | } 229 | ul :last-child, ol :last-child { 230 | margin-bottom: 0; 231 | } 232 | dl { 233 | padding: 0; 234 | } 235 | dl dt { 236 | font-size: 1.2em; 237 | font-weight: bold; 238 | font-style: italic; 239 | padding: 0; 240 | margin: 15px 0 5px; 241 | } 242 | dl dt:first-child { 243 | padding: 0; 244 | } 245 | dl dt > :first-child { 246 | margin-top: 0; 247 | } 248 | dl dt > :last-child { 249 | margin-bottom: 0; 250 | } 251 | dl dd { 252 | margin: 0 0 15px; 253 | padding: 0 2em; 254 | } 255 | dl dd > :first-child { 256 | margin-top: 0; 257 | } 258 | dl dd > :last-child { 259 | margin-bottom: 0; 260 | } 261 | blockquote { 262 | border-left: 4px solid silver; 263 | padding: 0 15px; 264 | color: silver; 265 | } 266 | blockquote > :first-child { 267 | margin-top: 0; 268 | } 269 | blockquote > :last-child { 270 | margin-bottom: 0; 271 | } 272 | table { 273 | padding: 0; 274 | border: 4px ridge lightgray; 275 | /* width: 50%; */ 276 | caption-side: bottom; 277 | } 278 | table tr { 279 | border-top: 2px solid yellow; 280 | background-color: #202020; 281 | margin: 0; 282 | padding: 0; 283 | } 284 | 285 | /* table tr:nth-child(2n) { */ 286 | /* background-color: #101010; */ 287 | /* } */ 288 | table tr.even { 289 | background-color: #101010; 290 | } 291 | 292 | table tr th { 293 | font-weight: bold; 294 | border: 1px solid yellow; 295 | text-align: center; 296 | margin: 0; 297 | padding: 6px 13px; 298 | color: aqua; 299 | } 300 | table tr td { 301 | border: 1px solid silver; 302 | margin: 0; 303 | padding: 6px 13px; 304 | color: deeppink; 305 | } 306 | table tr th :first-child, table tr td :first-child { 307 | margin-top: 0; 308 | } 309 | table tr th :last-child, table tr td :last-child { 310 | margin-bottom: 0; 311 | } 312 | img { 313 | max-width: 100%; 314 | border: 2px groove gray; 315 | } 316 | code { 317 | margin: 0 2px; 318 | padding: 0 5px; 319 | white-space: nowrap; 320 | border: 1px solid #3a3a3a; 321 | background-color: blue; 322 | border-radius: 3px; 323 | color: lightgreen; 324 | } 325 | pre { 326 | background-color: #3a2a1a; 327 | border: 5px ridge silver; 328 | font-size: 13px; 329 | line-height: 19px; 330 | overflow: auto; 331 | padding: 6px 10px; 332 | border-radius: 3px; 333 | width: 50%; 334 | transition: background-color 0.3s; 335 | } 336 | pre:hover { 337 | background-color: #333; 338 | } 339 | pre code { 340 | margin: 0; 341 | padding: 0; 342 | white-space: pre; 343 | border: none; 344 | background: transparent; 345 | background-color: transparent; 346 | transition: inherit; 347 | } 348 | 349 | pre, code.sourceCode { 350 | color: mintcream; 351 | background-color: black; 352 | } 353 | 354 | del { 355 | color: black; 356 | background-color: black; 357 | text-decoration: none; 358 | } 359 | 360 | /* mysign */ 361 | span a { 362 | border: 3px outset gray; 363 | background-color: darkblue; 364 | } 365 | span.mail a:before { 366 | font-size: 1.5em; 367 | content: "✉ "; 368 | } 369 | 370 | /* footnotes */ 371 | div.footnotes ol:before { 372 | content: "✎ 脚注: "; 373 | color: deeppink; 374 | } 375 | 376 | /* dl, dt, dd */ 377 | dl { 378 | border: 3px ridge green; 379 | } 380 | dt { 381 | width: 20em; 382 | color: lightgreen; 383 | } 384 | dd { 385 | color: violet; 386 | width: 50em; 387 | } 388 | dd:before { 389 | content: "☞ "; 390 | } 391 | -------------------------------------------------------------------------------- /docs/thkmc_sample_ini.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | THKMC_SAMPLE 6 | 7 | 8 | 9 | 10 | 11 | 22 | 23 | 24 |
 25 | ; thkmc_sample.ini - THKMC 设置文件范本
 26 | ;
 27 | ; 如果你把 thkmc.ini 文件玩坏了,可以使用本文件覆盖 thkmc.ini ,然后根据需要参照注释修改。
 28 | ;
 29 | ; 本文件是 thkmc.ini 的范本,同时也是一个语法参考手册。因为 thkmc.ini 有用到一点点特殊的语法。
 30 | ; 如果你能耐心看完下面的所有注释(分号开头的每一行),相信用起 THKMC 来会更加得心应手。
 31 | ; 推荐使用带语法高亮的文本编辑器来阅读本文件。
 32 | ; 
 33 | ; 本文件适用的 THKMC 版本:1.40
 34 | 
 35 | 
 36 | ; [Default] - 全局键位配置
 37 | ;
 38 | ; UP、DOWN、LEFT、RIGHT 的意思分别是 ↑、↓、←、→ 四个方向键
 39 | [Default]
 40 | UP=W
 41 | DOWN=S
 42 | LEFT=A
 43 | RIGHT=D
 44 | 
 45 | ; 上面这4行设置的意思是将 ↑↓←→ 键分别映射到 WSAD 。
 46 | 
 47 | Z=j
 48 | X=k
 49 | C=l
 50 | LSHIFT=i
 51 | ; 上面这4行又将 Z 改成 J,X 改成 K,C 改成 L,LSHIFT(左SHIFT)改成 I。
 52 | ; 你可以看到上面的 jkli 都是小写,这无所谓,大写小写意思都是一样的。
 53 | ;
 54 | ; 等号右边不仅仅可以是字母,你可以设置成键盘上的几乎任何按键(只要 DirectInput8 支持)
 55 | ; 至于具体支持哪些键,以及键位名称该如何写,请参看源码。
 56 | ; https://github.com/wz520/thkmc/blob/master/KeyNamesDX.ahk
 57 | ;
 58 | ; 而等号左边,只有游戏中实际使用到的按键才会被映射。
 59 | ; 比如 C 键,红魔乡是用不到的,所以 "C=l" 这行对于红魔乡来讲实际上并没有什么卵用,会被忽略掉的。
 60 | ;
 61 | ; 若想要知道一个游戏具体用到了哪些键位,只要运行一下 thkmc,点击“打开程序并应用补丁”,选择你要改键的EXE,然后在消息框中会显示能被修改的键位。
 62 | ; 如果你只是想看键位但不想修改EXE,请在应用补丁前勾选“测试模式”复选框。
 63 | ;
 64 | ; 【如果等号右边是整数】
 65 | ; 如果等号右边是整数(可以是十六进制(以 0x 开头)),则该整数直接代表 DirectInput8 扫描码。
 66 | ; 比如:“GRAVE”(即“`”键,通常位于主键盘区数字键 1 左边)的扫描码为 0x29,那么如果你在INI中写“ESC=0x29”或者“ESC=41”,和写“ESC=GRAVE”是毫无区别的。
 67 | ; 【注意】换个角度说,如果你要将 Z 键映射到数字键上,比如“1”,不能写作 “Z=1”,而要写作 “Z=VK1”(因为这里的 1 代表扫描码为 1 的键,查 KeyNamesDX.ahk 可知该键是 “ESC”),
 68 | ; 当然,由于数字键“1”的扫描码是 0x02,所以可以写成“Z=0x02”或者“Z=2”。不过这样写很迷惑人,容易让别人误以为是数字键"2"。
 69 | 
 70 | 
 71 | 
 72 | 
 73 | ; 【各个游戏的专属配置】
 74 | ;
 75 | ; [Default] 段的映射配置会应用到所有游戏,如果要为每个游戏单独添加/修改/删除映射,可以创建一个以游戏代号为名的段,然后在下面写你要修改的映射。
 76 | ;
 77 | ; 以下列出所有游戏的代号和其对应的游戏名称:
 78 | ;
 79 | ; th6     : 东方红魔乡 1.02h 日文原版
 80 | ; th6     : 东方红魔乡 渔场汉化版(**主程序需脱壳**)
 81 | ; th7     : 东方妖妖梦 1.00b 日文原版、渔场汉化版
 82 | ; th8     : 东方永夜抄 1.00d 日文原版、渔场汉化版
 83 | ; th10    : 东方风神录 1.00a 日文原版、东方喝茶委员会汉化版
 84 | ; th11    : 东方地灵殿 1.00a 日文原版
 85 | ; th12    : 东方星莲船 1.00b 日文原版
 86 | ; th125   : 东方文花帖DS 1.00a 日文原版
 87 | ; th128   : 妖精大战争 ~ 东方三月精 1.00a 日文原版
 88 | ; th13    : 东方神灵庙 1.00c 日文原版
 89 | ; th14    : 东方辉针城 1.00b 日文原版、喵玉汉化版
 90 | ; th143   : 弹幕天邪鬼 1.00a 日文原版、喵玉汉化版
 91 | ; th15    : 东方绀珠传 1.00b 日文原版、喵玉汉化版
 92 | ; th16    : 东方天空璋 1.00a 日文原版、喵玉汉化版
 93 | ; th165   : 秘封噩梦日记 1.00a 日文原版、喵玉汉化版
 94 | ; th17tr  ; 东方鬼形兽 0.01a 体验版
 95 | ; th17    ; 东方鬼形兽 1.00b 日文原版
 96 | ; alcostg : 黄昏酒场 1.00a
 97 | ;
 98 | ; 例如:
 99 | ; 如果要为红魔乡(包括渔场汉化版)修改一些映射,那么就只要在 [th6] 下写上需要添加/修改/删除的映射即可。
100 | ; 在 [th6] 段中没有提到的映射仍然会在 [Default] 段中查找。
101 | 
102 | 
103 | ; 【导入其他配置段中的映射】
104 | ;
105 | ; 如果你在 [th6] 下写了一些映射,希望也能在 [th7] 里使用,那么你不必复制粘贴,只需要在 [th7] 下面添加一行:
106 | ;
107 | ; INCLUDE=th6
108 | ; 
109 | ; 即可。
110 | 
111 | 
112 | ; 【删除映射】
113 | ; 对于在 [Default] 段中写好的或者是从其他段中 INCLUDE 的一个映射,
114 | ; 比如 UP=W,如果要在另外一个游戏配置中删除该映射(恢复上方向键原有的功能),可以写成:
115 | ;
116 | ; UP=UP
117 | ;
118 | ; 或者干脆完全省略等号右边的内容,写成:
119 | ;
120 | ; UP=
121 | ;
122 | ; 即可。
123 | 
124 | 
125 | ; 【解决映射冲突】
126 | ; 新的映射可能与原有映射冲突。
127 | ; 比如在文花帖DS中,在通关后的菜单上按S键可以手动更新决定性照片。每次按下 S 可以听到拍照的音效。
128 | ; 但是本软件自带的 [Default] 配置中,S 键被映射到了下方向键上。
129 | ; 于是,每次按S键时光标会向下走一格,但同时也会听到拍照的音效。也就是一个键有了两个功能。
130 | ; 但有些作品,新的映射会完全覆盖原有按键的功能。
131 | ; 比如在永夜抄的 Result 界面中按 D 键可以导出游戏记录到 score.txt 文件中。
132 | ; 而本软件自带的 [Default] 配置中,D 键被映射到了右方向键上。
133 | ; 但是在这个作品中不是 D 键有了两个功能,而是 D 键原有功能失效。
134 | ;
135 | ; 那么,要解决上述冲突,非常简单,只需将受到影响的映射再重新映射到别的没有冲突的键上即可。
136 | ; 比如对于文花帖的 S 键,可以重新映射到没有被用到的键上,比如 ScrollLock 键:
137 | 
138 | [th125]
139 | S=ScrollLock
140 | 
141 | ; 对于永夜抄的 D 键,也可以映射到没有被用到的键上,比如同样是 ScrollLock 键。
142 | 
143 | [th8]
144 | D=ScrollLock
145 | 
146 | ; 现在在文花帖DS中按 ScrollLock 键,就是原来 S 键的功能;
147 | ; 而在永夜抄中按 ScrollLock 键,就是原来 D 键的功能。
148 | 
149 | 
150 | 
151 | 
152 | 
153 | ; th10 - 东方风神录
154 | [th10]
155 | ; 这里我们看一个新语法,我叫它“键位引用”:
156 | 
157 | ESC=th8:D
158 | 
159 | ; 这句话表示取出上面 [th8] 中为字母键 "D" 设置的值,再应用到本段([th10]) 的 "ESC" 这个键位上。
160 | ; 而上面 [th8] 里写着 D=ScrollLock ,所以这句话就等同于 ESC=ScrollLock 。
161 | ; 也就是说现在在风神录里按 ScrollLock 等于原来按 ESC 。
162 | ; 跟手动拷贝过来不同的是,当你改了上面的 D=ScrollLock 后,这里 ESC 的键位也会自动跟着改了。
163 | 
164 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
165 | ; 下面还有 [th12],[th13], ... 请自己补充吧……
166 | 
167 | 
168 | 
169 | 
170 | 
171 | ; 最后总结一下,除了 [thkmc] 外,每个段中的设置项的写法有以下三种:
172 | ;
173 | ; 1. 原键位=新键位
174 | ; 2. INCLUDE=段名
175 | ; 3. 原键位=段名:键位
176 | ; 4. 原键位=原键位      可以简写为: 原键位=
177 | ;
178 | ; * 键位设置的优先级:[游戏代号] > [游戏代号]中的INCLUDE > [Default] > [Default]中的INCLUDE
179 | 
180 | 
181 | 182 | 183 | 184 | -------------------------------------------------------------------------------- /gamedata.ahk: -------------------------------------------------------------------------------- 1 | ;; THKMC_GameDataXXX 的基类 2 | class THKMC_GameDataBase { 3 | __New( gamename, gamenameCN, timestamp, minfilesize ) { 4 | this._setEXEInfo( gamename, gamenameCN, timestamp, minfilesize ) 5 | 6 | ; 一串正则表达式,用于从 patchdata 中取出与键位码相关机器指令的操作数的 HEX 子串(取第一个子表达式。不能没有子表达式) 7 | ; 其中 0fb6 是 MOVZX R32, BYTE PTR SS:[EBP+CONST32] 系列指令的头两个字节 8 | ; 该表达式不适用于所有游戏,所以有些子类的构造函数会修改它。 9 | this.addrPattern := "i)\b0fb6[0-9a-f]+\s([0-9a-z]{2}f[ef]ffff)\s" 10 | } 11 | 12 | _setEXEInfo( gamename, gamenameCN, timestamp, minfilesize ) { 13 | this.gamename := gamename ;; 游戏代号(目前用于 INI 文件中的段名) 14 | this.gamenameCN := gamenameCN ;; 游戏的中文描述(显示给用户看的) 15 | 16 | ; PE 头中的时间戳。用于确定EXE文件的游戏版本 17 | this.timestamp := timestamp 18 | 19 | ;; EXE 的【最小】文件大小。为了支持给EXE添加了区段的第三方补丁,所以并不指定精确的文件大小 20 | this.minfilesize := minfilesize 21 | } 22 | 23 | _setPatchData( patchaddr, patchaddrjmpto, patchVAfix, patchvaluefix, patchdata ) { 24 | this.patchaddr := patchaddr ;; 补丁数据的起始地址(VA,即OD中所看到的地址) 25 | this.patchaddrjmpto := patchaddrjmpto ;; 目标地址,补丁最后的一个 jmp 指令的目标地址,也就是补丁的结束地址 26 | ;; VA - File_Offset(EXE内偏移) 27 | ;; 在 OD 的 CPU 窗口中随便选择一条EXE领空中的指令,右键菜单->查看->可执行文件,然后将 CPU 窗口中的地址减去“可执行文件”窗口中相同指令的地址即可算出该值 28 | ;; 对于大多数东方STG的EXE,该值都是 0x400c00,不过也有例外,比如永夜抄就是 0x400200 29 | this.patchVAfix := patchVAfix 30 | ;; 在 OD 中从 patchaddr 向上查找 LEA R32, [ebp-XXX],取其中的 XXX 31 | this.patchvaluefix := patchvaluefix 32 | 33 | ; 补丁的二进制数据的HEX字符串表示。所有非 HEX 字符都将被忽略 34 | this.patchdata := patchdata 35 | 36 | ; SplitPath, A_ScriptFullPath, , , , scriptname 37 | ; this.inifilepath := A_ScriptDir . "\" . scriptname . ".ini" 38 | this.inifilepath := "thkmc.ini" 39 | } 40 | 41 | ; 用于与按键映射无关的自定义补丁代码。 42 | ; 比如 TH07/TH08 需要加入破解自校验的代码才可以让修改过的 EXE 正常运行 43 | ; 目前用于被 7,8 两个子类覆盖。本基类并不做任何事 44 | doCustomPatch( pEXEData ) { 45 | } 46 | 47 | ; 把使用 HEX 字串表示的数值转换成 AHK 中的整数形式 48 | ; 被转换的 HEX 字串必须是32位小端格式 49 | ; 如果返回字符串 "OUT",则认为超出范围,应该被跳过 50 | str2KeymapValue( s ) { 51 | KeymapValue := ToSigned(BSwap32_ahk( Pad0x(s)+0 )) 52 | KeymapValue := KeymapValue + this.patchvaluefix 53 | 54 | if ( KeymapValue > 0xff || KeymapValue < 0 ) { 55 | return "OUT" ; 超出范围, 忽略 56 | } 57 | 58 | return KeymapValue 59 | } 60 | 61 | ; 上面的反函数 62 | keymapValue2Str( k ) { 63 | k := ToUnsigned( k - this.patchvaluefix ) 64 | return PadLeadingZero( UnPad0x(ToHex( BSwap32_ahk( k ) )), 8 ) 65 | } 66 | } 67 | 68 | 69 | 70 | #include gamedata\gamedata6.ahk 71 | #include gamedata\gamedata6cn.ahk 72 | #include gamedata\gamedata7.ahk 73 | #include gamedata\gamedata8.ahk 74 | #include gamedata\gamedata95.ahk 75 | 76 | #include gamedata\gamedata10.ahk 77 | #include gamedata\gamedata11.ahk 78 | #include gamedata\gamedata12.ahk 79 | #include gamedata\gamedata125.ahk 80 | #include gamedata\gamedata128.ahk 81 | 82 | #include gamedata\gamedata13.ahk 83 | #include gamedata\gamedata14.ahk 84 | #include gamedata\gamedata143.ahk 85 | #include gamedata\gamedata15.ahk 86 | #include gamedata\gamedata16.ahk 87 | #include gamedata\gamedata165.ahk 88 | #include gamedata\gamedata17tr.ahk 89 | #include gamedata\gamedata17.ahk 90 | 91 | #include gamedata\gamedataalcostg.ahk 92 | 93 | 94 | ; 类名 "THKMC_GameDataXXX" 的 XXX 部分 95 | THKMC_GameIDList := [ "6", "6cn", "7", "8", "95", "10", "11", "12", "125", "128", "13", "14", "143", "15", "16", "165", "17tr", "17", "alcostg" ] 96 | 97 | ; 获取所有支持的游戏列表字符串(目前用于“关于”对话框) 98 | THKMC_GetGameNameList() 99 | { 100 | global THKMC_GameIDList 101 | result := [] 102 | for k, id in THKMC_GameIDList { 103 | gamedata := new THKMC_GameData%id%() 104 | result.Push( Format("[{:s}]`t{:s}", gamedata.gamename, gamedata.gamenameCN) ) 105 | } 106 | return array_join(result) 107 | } 108 | -------------------------------------------------------------------------------- /gamedata/buildgamedata.vim: -------------------------------------------------------------------------------- 1 | " 用于把从 OD 粘贴过来的反汇编list转换成 patchdata 格式的辅助脚本。 2 | " 3 | " 粘贴过来的代码首先要手动把左边的地址去掉(用可视块模式(CTRL+Q)很容易解决,所以就不搞替换命令了),然后在每一行的机器码和汇编码之间加分号(同样用可视块手动来做就够了)。 4 | " 5 | " 当然,要使用本脚本里定义的命令,别忘了先 :source 一下。 6 | 7 | 8 | " ** 用于 th128+ 9 | " 10 | " **【注意】程序通常使用 MOVZX 获取按键状态的指令,但也可能是 MOV。 11 | 12 | " and r32, 80h ,将 80h 改成 edi,精简字节码,这样 EXE 中才有空间使用下面的 13 | " KMOVZX 系列命令。 14 | " 15 | " 用了本系列命令后,别忘了在前面加上: 16 | " 57 ; PUSH EDI 17 | " BF 80000000 ; MOV EDI,80 18 | " 代码最后要加上: 19 | " 5F ; POP EDI 20 | com! KEAX80EDI %s/^.\+and eax,80$/21f8 ; and eax,edi/gc 21 | com! KEDX80EDI %s/^.\+and edx,80$/21fa ; and edx,edi/gc 22 | com! KECX80EDI %s/^.\+and ecx,80$/21f9 ; and ecx,edi/gc 23 | com! KESI80EDI %s/^.\+and esi,80$/21fe ; and esi,edi/gc 24 | 25 | " 与上面类似,只不过源操作数改成了 EBX 26 | com! KEAX80EBX %s/^.\+and eax,80$/21d8 ; and eax,ebx/gc 27 | com! KEDX80EBX %s/^.\+and edx,80$/21da ; and edx,ebx/gc 28 | com! KECX80EBX %s/^.\+and ecx,80$/21d9 ; and ecx,ebx/gc 29 | 30 | " 修改 MOVZX R32, BYTE PTR [EBP-XXX] 31 | " 将8位操作数(XXX)改成32位 32 | com! KMOVZXBYTEEAX %s/^0fb645 \(\x\{2}\>\)/0fb685 \1ffffff/gc 33 | com! KMOVZXBYTEEDX %s/^0fb655 \(\x\{2}\>\)/0fb695 \1ffffff/gc 34 | com! KMOVZXBYTEECX %s/^0fb64d \(\x\{2}\>\)/0fb68d \1ffffff/gc 35 | 36 | 37 | " ** 用于使用 ESP 访问局部变量的游戏(th10, th11, th12, th125, alcostg) 38 | 39 | " 与 KXXX80EBX 类似,只不过源操作数改成了 BL,且目标操作数也都是 8 位(AL, CL, DL) 40 | com! KAL80BL %s/^.\+and al,80$/20d8 ; and al,bl/gc 41 | com! KCL80BL %s/^.\+and cl,80$/20d9 ; and cl,bl/gc 42 | com! KDL80BL %s/^.\+and dl,80$/20da ; and dl,bl/gc 43 | 44 | " 修改 MOVZX R32, BYTE PTR [ESP+XXX] 45 | " 将8位操作数(XXX)改成32位 46 | com! KESPMOVZXBYTEESI %s/^0fb67424 \(\x\{2}\>\)/0fb6b424 \1000000/gc 47 | com! KESPMOVZXBYTEEAX %s/^0fb64424 \(\x\{2}\>\)/0fb68424 \1000000/gc 48 | com! KESPMOVZXBYTEECX %s/^0fb64c24 \(\x\{2}\>\)/0fb68c24 \1000000/gc 49 | com! KESPMOVZXBYTEEDX %s/^0fb65424 \(\x\{2}\>\)/0fb69424 \1000000/gc 50 | 51 | " 修改 MOVZX R32, BYTE PTR [ESP+XXX] 52 | " 将32位操作数(XXX)改成8位 53 | com! K32T8ESPMOVZXBYTEESI %s/^0fb6b424 \(\x\{2}\)000000\>/0fb67424 \1/gc 54 | com! K32T8ESPMOVZXBYTEEAX %s/^0fb68424 \(\x\{2}\)000000\>/0fb64424 \1/gc 55 | com! K32T8ESPMOVZXBYTEECX %s/^0fb68c24 \(\x\{2}\)000000\>/0fb64c24 \1/gc 56 | com! K32T8ESPMOVZXBYTEEDX %s/^0fb69424 \(\x\{2}\)000000\>/0fb65424 \1/gc 57 | 58 | " 同上注意与上面的不同之处,这是 MOV 不是 MOVZX,目标操作数是 8 位 59 | com! K32T8ESPMOVBYTEAL %s/^8a8424 \(\x\{2}\)000000\>/8a4424 \1/gc 60 | com! K32T8ESPMOVBYTECL %s/^8a8c24 \(\x\{2}\)000000\>/8a4c24 \1/gc 61 | com! K32T8ESPMOVBYTEDL %s/^8a9424 \(\x\{2}\)000000\>/8a5424 \1/gc 62 | com! K32T8ESPMOVBYTEBL %s/^8a9c24 \(\x\{2}\)000000\>/8a5c24 \1/gc 63 | 64 | " 修改 OR R8, BYTE PTR [ESP+XXX] 65 | " 将32位操作数(XXX)改成8位 66 | " 同上注意与上面的不同之处,这是 MOV 不是 MOVZX,目标操作数是 8 位 67 | com! K32T8ESPORBYTEAL %s/^0a8424 \(\x\{2}\)000000\>/0a4424 \1/gc 68 | com! K32T8ESPORBYTECL %s/^0a8c24 \(\x\{2}\)000000\>/0a4c24 \1/gc 69 | com! K32T8ESPORBYTEDL %s/^0a9424 \(\x\{2}\)000000\>/0a5424 \1/gc 70 | 71 | " 对 [ESP+xxx] 或 [ESP-xxx] 中的 xxx 进行十六进制加减运算,并将替换掉原来的值 72 | " --- 这个用于操作分号左边的机器码 73 | com! -nargs=1 -range=% KADD ,s/^\%(0fb6\|8a\|0a\)\x\{2}24\s\zs\x\{2}\ze\>/\=AddHex(submatch(0),)/gc 74 | " --- 这个用于操作分号右边的注释 75 | com! -nargs=1 -range=% KADDForComment ,s/ESP\zs[+-]\x\{1,2}\ze/\=AddHexForComment(submatch(0),)/gc 76 | 77 | fun! s:AddHex( m, n ) "{{{ 78 | let result = str2nr(a:m,16) + a:n 79 | if result < 0 80 | let result = and(result, 0xff) 81 | endif 82 | return printf("%02X", result ) " 只需 8-bit 83 | endfunction "}}} 84 | 85 | fun! s:AddHexForComment( m, n ) "{{{ 86 | let result = str2nr(a:m,16) + a:n 87 | if result < 0 88 | " let result = and(result, 0xff) 89 | let result = -result 90 | return printf("-%02X", result ) " 只需 8-bit 91 | else 92 | return printf("+%02X", result ) " 只需 8-bit 93 | endif 94 | endfunction "}}} 95 | 96 | -------------------------------------------------------------------------------- /gamedata/gamedata10.ahk: -------------------------------------------------------------------------------- 1 | ; 本作很坑爹的按 [ESP+XX] 来访问局部变量... 2 | 3 | class THKMC_GameData10 extends THKMC_GameDataBase { 4 | __New( ) { 5 | base.__New("th10", "东方风神录 1.00a", 0x46b23cd0, 487936) 6 | pd = 7 | (LTrim Comment 8 | 9 | 53 ; PUSH EBX 10 | B380 ; MOV BL,80 11 | 12 | 81C4 94000000 ; add esp, 94 ; 后面的地址总共 -0x90。使用 KADD 和 KADDForComment 命令 13 | 14 | 8A4C24 AA ; MOV CL,BYTE PTR SS:[ESP-56] 15 | 8A5424 AD ; MOV DL,BYTE PTR SS:[ESP-53] 16 | 8A4424 AC ; MOV AL,BYTE PTR SS:[ESP-54] 17 | 80E2 BF ; AND DL,0BF 18 | D0E8 ; SHR AL,1 19 | 0AD0 ; OR DL,AL 20 | D0EA ; SHR DL,1 21 | 8A4424 81 ; MOV AL,BYTE PTR SS:[ESP-7F] 22 | 80E1 9F ; AND CL,9F 23 | 0AD1 ; OR DL,CL 24 | 8A4C24 C8 ; MOV CL,BYTE PTR SS:[ESP-38] 25 | D0EA ; SHR DL,1 26 | 24 8F ; AND AL,8F 27 | 0AD0 ; OR DL,AL 28 | 0a4c24 48 ; OR CL,BYTE PTR SS:[ESP+48] 29 | 8a4424 50 ; MOV AL,BYTE PTR SS:[ESP+50] 30 | D0EA ; SHR DL,1 31 | 80E1 87 ; AND CL,87 32 | 0AD1 ; OR DL,CL 33 | 0A4424 D0 ; OR AL,BYTE PTR SS:[ESP-30] 34 | 8A4C24 CB ; MOV CL,BYTE PTR SS:[ESP-35] 35 | D0EA ; SHR DL,1 36 | 24 83 ; AND AL,83 37 | 0AD0 ; OR DL,AL 38 | 0a4c24 4B ; OR CL,BYTE PTR SS:[ESP+4B] 39 | 8A4424 CD ; MOV AL,BYTE PTR SS:[ESP-33] 40 | D0EA ; SHR DL,1 41 | 80E1 81 ; AND CL,81 42 | 0AD1 ; OR DL,CL 43 | 0a4424 4D ; OR AL,BYTE PTR SS:[ESP+4D] 44 | 8A4C24 93 ; MOV CL,BYTE PTR SS:[ESP-6D] 45 | D0EA ; SHR DL,1 46 | 20d8 ; and al,bl 47 | 0AD0 ; OR DL,AL 48 | 8A4424 A0 ; MOV AL,BYTE PTR SS:[ESP-60] 49 | 66:0FB6FA ; MOVZX DI,DL 50 | 20d9 ; and cl,bl 51 | 20d8 ; and al,bl 52 | 66:0FB6D1 ; MOVZX DX,CL 53 | 66:0FB6C8 ; MOVZX CX,AL 54 | 8A4424 9C ; MOV AL,BYTE PTR SS:[ESP-64] 55 | 20d8 ; and al,bl 56 | D1E2 ; SHL EDX,1 57 | 0BD1 ; OR EDX,ECX 58 | 66:0FB6C8 ; MOVZX CX,AL 59 | 8A4424 99 ; MOV AL,BYTE PTR SS:[ESP-67] 60 | D1E2 ; SHL EDX,1 61 | 0BD1 ; OR EDX,ECX 62 | 0a4424 47 ; OR AL,BYTE PTR SS:[ESP+47] 63 | 20d8 ; and al,bl 64 | 66:0FB6C8 ; MOVZX CX,AL 65 | 8A4424 9F ; MOV AL,BYTE PTR SS:[ESP-61] 66 | D1E2 ; SHL EDX,1 67 | 20d8 ; and al,bl 68 | 0BD1 ; OR EDX,ECX 69 | 66:0FB6C8 ; MOVZX CX,AL 70 | 8A4424 90 ; MOV AL,BYTE PTR SS:[ESP-70] 71 | D1E2 ; SHL EDX,1 72 | 20d8 ; and al,bl 73 | 0BD1 ; OR EDX,ECX 74 | 66:0FB6C8 ; MOVZX CX,AL 75 | 8A4424 9D ; MOV AL,BYTE PTR SS:[ESP-63] 76 | D1E2 ; SHL EDX,1 77 | 20d8 ; and al,bl 78 | 0BD1 ; OR EDX,ECX 79 | 66:0FB6C8 ; MOVZX CX,AL 80 | D1E2 ; SHL EDX,1 81 | 0BD1 ; OR EDX,ECX 82 | D1E2 ; SHL EDX,1 83 | 0BFA ; OR EDI,EDX 84 | 8A5424 C9 ; MOV DL,BYTE PTR SS:[ESP-37] 85 | 20da ; and dl,bl 86 | F6DA ; NEG DL 87 | 1BD2 ; SBB EDX,EDX 88 | 81E2 90000000 ; AND EDX,90 89 | 0BFA ; OR EDI,EDX 90 | 8A4424 CF ; MOV AL,BYTE PTR SS:[ESP-31] 91 | 8A4C24 C7 ; MOV CL,BYTE PTR SS:[ESP-39] 92 | 8A5424 D1 ; MOV DL,BYTE PTR SS:[ESP-2F] 93 | 94 | 81EC 94000000 ; sub esp, 94 ; 恢复ESP 95 | 5B ; pop ebx 96 | ) 97 | ; OD中向上找 44a75b 处是 call dword ptr ds:[edx+24],这正是 "CALL IDirectInputDevice8::GetDeviceState()" 98 | ; 其参数有 lea ecx,dword ptr ss:[esp+10],于是 0x90-0x10=0x80 99 | this._setPatchData( 0x44a796, 0x44a88d, 0x400c00, 0x80, pd ) 100 | this.addrPattern := "i)(?:\b0fb6|\b[80]a[45])[0-9a-f]+\s([0-9a-z]{2})\s" 101 | } 102 | 103 | ; 如果返回字符串 "OUT",则认为超出范围,跳过 104 | ; 这个类只需要处理一个字节即可 105 | str2KeymapValue( s ) { 106 | KeymapValue := (Pad0x(s)+0) 107 | ; 转换成8位有符号 108 | KeymapValue := KeymapValue > 0x7f ? -(0x100-KeymapValue) : KeymapValue 109 | KeymapValue += this.patchvaluefix 110 | 111 | if ( KeymapValue > 0xff || KeymapValue < 0 ) { 112 | return "OUT" ; 超出范围, 忽略 113 | } 114 | 115 | return KeymapValue 116 | } 117 | 118 | keymapValue2Str( k ) { 119 | k := (k - this.patchvaluefix) & 0xff ; 转换成8位无符号 120 | return PadLeadingZero( UnPad0x(ToHex( k )), 2 ) 121 | } 122 | } 123 | 124 | -------------------------------------------------------------------------------- /gamedata/gamedata11.ahk: -------------------------------------------------------------------------------- 1 | ; 本作很坑爹的按 [ESP+XX] 来访问局部变量... 2 | 3 | 4 | class THKMC_GameData11 extends THKMC_GameData10 { 5 | __New( ) { 6 | base.__New() 7 | this._setEXEInfo("th11", "东方地灵殿 1.00a", 0x489526fa, 688128) 8 | pd = 9 | (LTrim Comment 10 | 11 | 53 ; PUSH EBX ; 这里 PUSH 了,后面的 ESP+XX 要 +4 12 | BB 80000000 ; MOV EBX,80 13 | 14 | 21de ; AND ESI,EBX 15 | 21d8 ; and eax,ebx 16 | 17 | 81C4 88000000 ; add esp, 88 ; 后面的地址总共 -80-4=-84 18 | 19 | 0FB67424 C4 ; MOVZX ESI,BYTE PTR SS:[ESP-3C] 20 | 0FB64424 93 ; MOVZX EAX,BYTE PTR SS:[ESP-6D] 21 | 0FB64C24 A0 ; MOVZX ECX,BYTE PTR SS:[ESP-60] 22 | 0FB65424 9C ; MOVZX EDX,BYTE PTR SS:[ESP-64] 23 | 81E6 80000000 ; AND ESI,80 24 | 21d8 ; and eax,ebx 25 | 03F6 ; ADD ESI,ESI 26 | 03F6 ; ADD ESI,ESI 27 | 0BF0 ; OR ESI,EAX 28 | 0FB64424 99 ; MOVZX EAX,BYTE PTR SS:[ESP-67] 29 | 03F6 ; ADD ESI,ESI 30 | 21d9 ; and ecx,ebx 31 | 0BF1 ; OR ESI,ECX 32 | 0fb64c24 47 ; MOVZX ECX,BYTE PTR SS:[ESP+47] 33 | 0BC1 ; OR EAX,ECX 34 | 0FB64C24 9D ; MOVZX ECX,BYTE PTR SS:[ESP-63] 35 | 21d8 ; and eax,ebx 36 | 03F6 ; ADD ESI,ESI 37 | 21da ; and edx,ebx 38 | 0BF2 ; OR ESI,EDX 39 | 0FB65424 9F ; MOVZX EDX,BYTE PTR SS:[ESP-61] 40 | 03F6 ; ADD ESI,ESI 41 | 0BF0 ; OR ESI,EAX 42 | 0FB64424 90 ; MOVZX EAX,BYTE PTR SS:[ESP-70] 43 | 21d8 ; and eax,ebx 44 | 03F6 ; ADD ESI,ESI 45 | 21da ; and edx,ebx 46 | 0BF2 ; OR ESI,EDX 47 | 0FB65424 81 ; MOVZX EDX,BYTE PTR SS:[ESP-7F] 48 | 21d9 ; and ecx,ebx 49 | 03F6 ; ADD ESI,ESI 50 | 0BF0 ; OR ESI,EAX 51 | 0FB64424 AD ; MOVZX EAX,BYTE PTR SS:[ESP-53] 52 | 21d8 ; and eax,ebx 53 | C1E6 07 ; SHL ESI,7 54 | 0BF1 ; OR ESI,ECX 55 | 0FB64C24 AC ; MOVZX ECX,BYTE PTR SS:[ESP-54] 56 | D1E9 ; SHR ECX,1 57 | 0BC1 ; OR EAX,ECX 58 | 0FB64C24 C8 ; MOVZX ECX,BYTE PTR SS:[ESP-38] 59 | 21da ; and edx,ebx 60 | C1E8 02 ; SHR EAX,2 61 | 03F6 ; ADD ESI,ESI 62 | 0BF2 ; OR ESI,EDX 63 | 0FB65424 AA ; MOVZX EDX,BYTE PTR SS:[ESP-56] 64 | 21da ; and edx,ebx 65 | 0BC2 ; OR EAX,EDX 66 | 0fb65424 48 ; MOVZX EDX,BYTE PTR SS:[ESP+48] 67 | 0BCA ; OR ECX,EDX 68 | 0FB65424 D0 ; MOVZX EDX,BYTE PTR SS:[ESP-30] 69 | D1E8 ; SHR EAX,1 70 | 21d9 ; and ecx,ebx 71 | 0BC1 ; OR EAX,ECX 72 | 0fb64c24 50 ; MOVZX ECX,BYTE PTR SS:[ESP+50] 73 | 0BCA ; OR ECX,EDX 74 | 0fb65424 4B ; MOVZX EDX,BYTE PTR SS:[ESP+4B] 75 | D1E8 ; SHR EAX,1 76 | 21d9 ; and ecx,ebx 77 | 0BC1 ; OR EAX,ECX 78 | 0FB64C24 CB ; MOVZX ECX,BYTE PTR SS:[ESP-35] 79 | D1E8 ; SHR EAX,1 80 | 0BCA ; OR ECX,EDX 81 | 0FB65424 C9 ; MOVZX EDX,BYTE PTR SS:[ESP-37] 82 | 21d9 ; and ecx,ebx 83 | 0BC1 ; OR EAX,ECX 84 | 0fb64c24 4D ; MOVZX ECX,BYTE PTR SS:[ESP+4D] 85 | D1E8 ; SHR EAX,1 86 | 03F6 ; ADD ESI,ESI 87 | 0BF0 ; OR ESI,EAX 88 | 0FB64424 CD ; MOVZX EAX,BYTE PTR SS:[ESP-33] 89 | 0BC1 ; OR EAX,ECX 90 | 8A4C24 CF ; MOV CL,BYTE PTR SS:[ESP-31] 91 | 21d8 ; and eax,ebx 92 | 0BF0 ; OR ESI,EAX 93 | 80E2 80 ; AND DL,80 94 | 0FB6C2 ; MOVZX EAX,DL 95 | F7D8 ; NEG EAX 96 | 1BC0 ; SBB EAX,EAX 97 | 25 90000000 ; AND EAX,90 98 | 0BF0 ; OR ESI,EAX 99 | 80E1 80 ; AND CL,80 100 | 8A4424 C7 ; MOV AL,BYTE PTR SS:[ESP-39] 101 | 0FB6D1 ; MOVZX EDX,CL 102 | F7DA ; NEG EDX 103 | 1BD2 ; SBB EDX,EDX 104 | 24 80 ; AND AL,80 105 | 83E2 60 ; AND EDX,60 106 | 0FB6C8 ; MOVZX ECX,AL 107 | 0BF2 ; OR ESI,EDX 108 | 0FB65424 D1 ; MOVZX EDX,BYTE PTR SS:[ESP-2F] 109 | 110 | 81EC 88000000 ; sub esp, 88 ; 恢复ESP 111 | 5B ; pop ebx 112 | ) 113 | this._setPatchData( 0x457883, 0x4579d1, 0x400c00, 0x80, pd ) 114 | this.addrPattern := "i)(?:\b0fb6|\b8a4)[0-9a-f]+\s([0-9a-z]{2})\s" 115 | } 116 | 117 | doCustomPatch( pExeData ) { 118 | ; 要让上面的补丁有效, 119 | ; 需要打个补丁以修正总是无法正常初始化 DirectInput 的 BUG 。 120 | 121 | ; original data 122 | od = 123 | (LTrim Comment 124 | E8 F11A0100 ; call 125 | E8 BC2A0100 ; call 126 | 8125 10384C00 FFF3FFFF ; and dword ptr ds:[4C3810],FFFFF3FF 127 | B8 80324C00 ; mov eax,th11.4C3280 128 | E8 E8230000 ; call 129 | A1 10384C00 ; mov eax,dword ptr ds:[4C3810] 130 | 33D2 ; xor edx,edx 131 | 391D A0324C00 ; cmp dword ptr ds:[4C32A0],ebx 132 | BE E4584A00 ; mov esi,th11.4A58E4 133 | 0F95C2 ; setne dl 134 | 33C9 ; xor ecx,ecx 135 | C1E2 0A ; shl edx,A 136 | 33D0 ; xor edx,eax 137 | 81E2 00040000 ; and edx,400 138 | 33C2 ; xor eax,edx 139 | 391D A4324C00 ; cmp dword ptr ds:[4C32A4],ebx 140 | 0F95C1 ; setne cl 141 | C1E1 0B ; shl ecx,B 142 | 33C8 ; xor ecx,eax 143 | 81E1 00080000 ; and ecx,800 144 | 33C1 ; xor eax,ecx 145 | A3 10384C00 ; mov dword ptr ds:[4C3810],eax 146 | E8 243D0100 ; call 147 | 6A 20 ; push 20 148 | E8 A3A20100 ; call 149 | A3 84324C00 ; mov dword ptr ds:[4C3284],eax 150 | 3BC3 ; cmp eax,ebx 151 | 75 17 ; jne th11.445733 152 | 68 5C684900 ; push th11.49685C 153 | BF 40594A00 ; mov edi,th11.4A5940 154 | E8 C5330100 ; call 155 | 83C4 04 ; add esp,4 156 | E9 66040000 ; jmp th11.445B99 157 | 8B5C24 10 ; mov ebx,dword ptr ss:[esp+10] 158 | E8 A4130000 ; call 159 | 85C0 ; test eax,eax 160 | 0F85 53040000 ; jne th11.445B97 161 | ) 162 | 163 | ; patch data 164 | pd = 165 | (LTrim Comment 166 | 8B5C24 10 ; mov ebx,dword ptr ss:[esp+10] 167 | E8 2D140000 ; call 168 | 85C0 ; test eax,eax 169 | 0F85 DC040000 ; jne th11.445B97 170 | E8 E01A0100 ; call 171 | E8 AB2A0100 ; call 172 | 8125 10384C00 FFF3FFFF ; and dword ptr ds:[4C3810],FFFFF3FF 173 | B8 80324C00 ; mov eax,th11.4C3280 174 | E8 D7230000 ; call 175 | A1 10384C00 ; mov eax,dword ptr ds:[4C3810] 176 | 33D2 ; xor edx,edx 177 | 3915 A0324C00 ; cmp dword ptr ds:[4C32A0],edx 178 | BE E4584A00 ; mov esi,th11.4A58E4 179 | 0F95C2 ; setne dl 180 | 33C9 ; xor ecx,ecx 181 | C1E2 0A ; shl edx,A 182 | 33D0 ; xor edx,eax 183 | 81E2 00040000 ; and edx,400 184 | 33C2 ; xor eax,edx 185 | 390D A4324C00 ; cmp dword ptr ds:[4C32A4],ecx 186 | 0F95C1 ; setne cl 187 | C1E1 0B ; shl ecx,B 188 | 33C8 ; xor ecx,eax 189 | 81E1 00080000 ; and ecx,800 190 | 33C1 ; xor eax,ecx 191 | A3 10384C00 ; mov dword ptr ds:[4C3810],eax 192 | E8 133D0100 ; call 193 | 6A 20 ; push 20 194 | E8 92A20100 ; call 195 | A3 84324C00 ; mov dword ptr ds:[4C3284],eax 196 | 3BC7 ; cmp eax,edi 197 | 75 17 ; jne th11.445744 198 | 68 5C684900 ; push th11.49685C 199 | BF 40594A00 ; mov edi,th11.4A5940 200 | E8 B4330100 ; call 201 | 83C4 04 ; add esp,4 202 | E9 55040000 ; jmp th11.445B99 203 | ) 204 | 205 | addr := 0x44AAA 206 | 207 | AddLog("【提示】开始打补丁以修正地灵殿特有的无法正确初始化 DirectInput8 的 BUG 。") 208 | if ( compareHexToBinary( od, pExeData, addr ) = 0 ) { 209 | AddLog("【提示】用于修正BUG的补丁应用成功!") 210 | writeHexToBinary( pd, pExeData, addr ) 211 | } 212 | else if ( compareHexToBinary( pd, pExeData, addr) = 0 ) { 213 | AddLog("【提示】无需打补丁。跳过。") 214 | } 215 | else { 216 | AddLog("【警告】无法打补丁。因为需要打补丁的数据已被第三方程序修改过。自定义键位可能无效。") 217 | } 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /gamedata/gamedata12.ahk: -------------------------------------------------------------------------------- 1 | ; 本作很坑爹的按 [ESP+XX] 来访问局部变量... 2 | ; 继承自 TH11 3 | 4 | class THKMC_GameData12 extends THKMC_GameData11 { 5 | __New( ) { 6 | base.__New() 7 | this._setEXEInfo("th12", "东方星莲船 1.00b", 0x4a95ffdc, 737280) 8 | ; 直接继承 th11 的 patchdata 9 | pd := this.patchdata 10 | this._setPatchData( 0x463093, 0x4631e1, 0x400c00, 0x80, pd ) 11 | } 12 | 13 | ; 屏蔽 TH11 的 custom patch 14 | doCustomPatch( pExeData ) { 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /gamedata/gamedata125.ahk: -------------------------------------------------------------------------------- 1 | ; 本作很坑爹的按 [ESP+XX] 来访问局部变量... 2 | 3 | class THKMC_GameData125 extends THKMC_GameData12 { 4 | __New( ) { 5 | base.__New() 6 | this._setEXEInfo("th125", "东方文花帖DS 1.00a", 0x4b916e39, 743424) 7 | pd = 8 | (LTrim Comment 9 | 10 | 57 ; PUSH EDI 11 | BF 80000000 ; MOV EDI,80 12 | 13 | 81C4 88000000 ; add esp, 88 ; 后面的地址总共 -80-4=-84 14 | 15 | 0FB67424 81 ; MOVZX ESI,BYTE PTR SS:[ESP-7F] 16 | 0FB64424 AE ; MOVZX EAX,BYTE PTR SS:[ESP-52] 17 | 0FB64C24 A6 ; MOVZX ECX,BYTE PTR SS:[ESP-5A] 18 | 0FB65424 C4 ; MOVZX EDX,BYTE PTR SS:[ESP-3C] 19 | 21fe ; and esi,edi 20 | 03F6 ; ADD ESI,ESI 21 | 21f9 ; and ecx,edi 22 | 21f8 ; and eax,edi 23 | 0BF0 ; OR ESI,EAX 24 | 0FB64424 93 ; MOVZX EAX,BYTE PTR SS:[ESP-6D] 25 | 03F6 ; ADD ESI,ESI 26 | 0BF1 ; OR ESI,ECX 27 | 0FB64C24 A0 ; MOVZX ECX,BYTE PTR SS:[ESP-60] 28 | 21fa ; and edx,edi 29 | 03F6 ; ADD ESI,ESI 30 | 03F6 ; ADD ESI,ESI 31 | 0BF2 ; OR ESI,EDX 32 | 0FB65424 9C ; MOVZX EDX,BYTE PTR SS:[ESP-64] 33 | 03F6 ; ADD ESI,ESI 34 | 03F6 ; ADD ESI,ESI 35 | 21f9 ; and ecx,edi 36 | 21f8 ; and eax,edi 37 | 0BF0 ; OR ESI,EAX 38 | 0FB64424 99 ; MOVZX EAX,BYTE PTR SS:[ESP-67] 39 | 21fa ; and edx,edi 40 | 03F6 ; ADD ESI,ESI 41 | 0BF1 ; OR ESI,ECX 42 | 0fb64c24 47 ; MOVZX ECX,BYTE PTR SS:[ESP+47] 43 | 0BC1 ; OR EAX,ECX 44 | 0FB64C24 AD ; MOVZX ECX,BYTE PTR SS:[ESP-53] 45 | 03F6 ; ADD ESI,ESI 46 | 0BF2 ; OR ESI,EDX 47 | 0FB65424 9F ; MOVZX EDX,BYTE PTR SS:[ESP-61] 48 | 21fa ; and edx,edi 49 | 21f8 ; and eax,edi 50 | 03F6 ; ADD ESI,ESI 51 | 0BF0 ; OR ESI,EAX 52 | 0FB64424 90 ; MOVZX EAX,BYTE PTR SS:[ESP-70] 53 | 21f9 ; and ecx,edi 54 | 03F6 ; ADD ESI,ESI 55 | 0BF2 ; OR ESI,EDX 56 | 0FB65424 AC ; MOVZX EDX,BYTE PTR SS:[ESP-54] 57 | D1EA ; SHR EDX,1 58 | 0BCA ; OR ECX,EDX 59 | 0FB65424 C8 ; MOVZX EDX,BYTE PTR SS:[ESP-38] 60 | 21f8 ; and eax,edi 61 | D1E9 ; SHR ECX,1 62 | 03F6 ; ADD ESI,ESI 63 | 0BF0 ; OR ESI,EAX 64 | 0FB64424 AA ; MOVZX EAX,BYTE PTR SS:[ESP-56] 65 | 21f8 ; and eax,edi 66 | 0BC8 ; OR ECX,EAX 67 | 0fb64424 48 ; MOVZX EAX,BYTE PTR SS:[ESP+48] 68 | 0BD0 ; OR EDX,EAX 69 | 0FB64424 D0 ; MOVZX EAX,BYTE PTR SS:[ESP-30] 70 | 21fa ; and edx,edi 71 | D1E9 ; SHR ECX,1 72 | 0BCA ; OR ECX,EDX 73 | 0fb65424 50 ; MOVZX EDX,BYTE PTR SS:[ESP+50] 74 | 0BD0 ; OR EDX,EAX 75 | 0fb64424 4B ; MOVZX EAX,BYTE PTR SS:[ESP+4B] 76 | 21fa ; and edx,edi 77 | D1E9 ; SHR ECX,1 78 | 0BCA ; OR ECX,EDX 79 | 0FB65424 CB ; MOVZX EDX,BYTE PTR SS:[ESP-35] 80 | 0BD0 ; OR EDX,EAX 81 | 0fb64424 4D ; MOVZX EAX,BYTE PTR SS:[ESP+4D] 82 | D1E9 ; SHR ECX,1 83 | 21fa ; and edx,edi 84 | 0BCA ; OR ECX,EDX 85 | 0FB65424 CD ; MOVZX EDX,BYTE PTR SS:[ESP-33] 86 | 0BD0 ; OR EDX,EAX 87 | D1E9 ; SHR ECX,1 88 | 21fa ; and edx,edi 89 | 0BCA ; OR ECX,EDX 90 | D1E9 ; SHR ECX,1 91 | C1E6 09 ; SHL ESI,9 92 | 0BF1 ; OR ESI,ECX 93 | 0FB64C24 C9 ; MOVZX ECX,BYTE PTR SS:[ESP-37] 94 | 80E1 80 ; AND CL,80 95 | 0FB6D1 ; MOVZX EDX,CL 96 | 8A4424 CF ; MOV AL,BYTE PTR SS:[ESP-31] 97 | F7DA ; NEG EDX 98 | 1BD2 ; SBB EDX,EDX 99 | 24 80 ; AND AL,80 100 | 83E2 48 ; AND EDX,48 101 | 0BF2 ; OR ESI,EDX 102 | 8A5424 C7 ; MOV DL,BYTE PTR SS:[ESP-39] 103 | 0FB6C8 ; MOVZX ECX,AL 104 | F7D9 ; NEG ECX 105 | 1BC9 ; SBB ECX,ECX 106 | 80E2 80 ; AND DL,80 107 | 83E1 30 ; AND ECX,30 108 | 0BF1 ; OR ESI,ECX 109 | 0FB64C24 D1 ; MOVZX ECX,BYTE PTR SS:[ESP-2F] 110 | 0FB6C2 ; MOVZX EAX,DL 111 | F7D8 ; NEG EAX 112 | 1BC0 ; SBB EAX,EAX 113 | 80E1 80 ; AND CL,80 114 | 83E0 28 ; AND EAX,28 115 | 0BF0 ; OR ESI,EAX 116 | 0FB64424 9D ; MOVZX EAX,BYTE PTR SS:[ESP-63] 117 | 118 | 119 | 81EC 88000000 ; sub esp, 88 ; 恢复ESP 120 | 5F ; pop edi 121 | ) 122 | this._setPatchData( 0x46245b, 0x4625ca, 0x400c00, 0x80, pd ) 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /gamedata/gamedata128.ahk: -------------------------------------------------------------------------------- 1 | class THKMC_GameData128 extends THKMC_GameDataBase { 2 | __New( ) { 3 | base.__New("th128", "妖精大战争 ~ 东方三月精 1.00a", 0x4C57A196, 752640) 4 | pd= 5 | (LTrim Comment 6 | 7 | 57 ; PUSH EDI 8 | BF 80000000 ; MOV EDI,80 9 | 10 | 0FB6B5 40FFFFFF ; MOVZX ESI,BYTE PTR SS:[EBP-C0] 11 | 0FB685 0FFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-F1] 12 | 0FB68D 1CFFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-E4] 13 | 0FB695 18FFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-E8] 14 | 21fe ; and esi,edi 15 | 03F6 ; ADD ESI,ESI 16 | 21f9 ; and ecx,edi 17 | 03F6 ; ADD ESI,ESI 18 | 21f8 ; and eax,edi 19 | 0BF0 ; OR ESI,EAX 20 | 0FB685 15FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-EB] 21 | 03F6 ; ADD ESI,ESI 22 | 0BF1 ; OR ESI,ECX 23 | 0fb68d C3ffffff ; MOVZX ECX,BYTE PTR SS:[EBP-3D] 24 | 0BC1 ; OR EAX,ECX 25 | 0FB68D 2AFFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-D6] 26 | 21fa ; and edx,edi 27 | 03F6 ; ADD ESI,ESI 28 | 0BF2 ; OR ESI,EDX 29 | 0FB695 1BFFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-E5] 30 | 03F6 ; ADD ESI,ESI 31 | 21f8 ; and eax,edi 32 | 0BF0 ; OR ESI,EAX 33 | 0FB685 0CFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-F4] 34 | 21fa ; and edx,edi 35 | 03F6 ; ADD ESI,ESI 36 | 0BF2 ; OR ESI,EDX 37 | 0FB695 19FFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-E7] 38 | 0BCA ; OR ECX,EDX 39 | 0FB695 28FFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-D8] 40 | 21f9 ; and ecx,edi 41 | 21f8 ; and eax,edi 42 | 03F6 ; ADD ESI,ESI 43 | 0BF0 ; OR ESI,EAX 44 | 0FB685 FDFEFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-103] 45 | C1E6 07 ; SHL ESI,7 46 | 0BF1 ; OR ESI,ECX 47 | 0FB68D 29FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-D7] 48 | 21f9 ; and ecx,edi 49 | 21f8 ; and eax,edi 50 | D1EA ; SHR EDX,1 51 | 0BCA ; OR ECX,EDX 52 | 0FB695 44FFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-BC] 53 | C1E9 02 ; SHR ECX,2 54 | 03F6 ; ADD ESI,ESI 55 | 0BF0 ; OR ESI,EAX 56 | 0FB685 26FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-DA] 57 | 21f8 ; and eax,edi 58 | 0BC8 ; OR ECX,EAX 59 | 0fb685 C4ffffff ; MOVZX EAX,BYTE PTR SS:[EBP-3C] 60 | 0BD0 ; OR EDX,EAX 61 | 0FB685 4CFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-B4] 62 | D1E9 ; SHR ECX,1 63 | 21fa ; and edx,edi 64 | 0BCA ; OR ECX,EDX 65 | 0fb695 CCffffff ; MOVZX EDX,BYTE PTR SS:[EBP-34] 66 | D1E9 ; SHR ECX,1 67 | 0BD0 ; OR EDX,EAX 68 | 0fb685 C7ffffff ; MOVZX EAX,BYTE PTR SS:[EBP-39] 69 | 21fa ; and edx,edi 70 | 0BCA ; OR ECX,EDX 71 | 0FB695 47FFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-B9] 72 | D1E9 ; SHR ECX,1 73 | 0BD0 ; OR EDX,EAX 74 | 0FB685 45FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-BB] 75 | 21fa ; and edx,edi 76 | 0BCA ; OR ECX,EDX 77 | 0fb695 C9ffffff ; MOVZX EDX,BYTE PTR SS:[EBP-37] 78 | D1E9 ; SHR ECX,1 79 | 03F6 ; ADD ESI,ESI 80 | 0BF1 ; OR ESI,ECX 81 | 0FB68D 49FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-B7] 82 | 0BCA ; OR ECX,EDX 83 | 21f9 ; and ecx,edi 84 | 0BF1 ; OR ESI,ECX 85 | 24 80 ; AND AL,80 86 | 0FB6C8 ; MOVZX ECX,AL 87 | F7D9 ; NEG ECX 88 | 1BC9 ; SBB ECX,ECX 89 | 81E1 90000000 ; AND ECX,90 90 | 0BF1 ; OR ESI,ECX 91 | 8A95 4BFFFFFF ; MOV DL,BYTE PTR SS:[EBP-B5] 92 | 8A8D 43FFFFFF ; MOV CL,BYTE PTR SS:[EBP-BD] 93 | 80E2 80 ; AND DL,80 94 | 0FB6C2 ; MOVZX EAX,DL 95 | F7D8 ; NEG EAX 96 | 1BC0 ; SBB EAX,EAX 97 | 80E1 80 ; AND CL,80 98 | 83E0 60 ; AND EAX,60 99 | 0FB6D1 ; MOVZX EDX,CL 100 | 0BF0 ; OR ESI,EAX 101 | 0FB685 4DFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-B3] 102 | 103 | 5F ; pop edi 104 | ) 105 | 106 | this._setPatchData( 0x469032, 0x46919e, 0x400c00, 0x104, pd ) 107 | } 108 | } 109 | 110 | -------------------------------------------------------------------------------- /gamedata/gamedata13.ahk: -------------------------------------------------------------------------------- 1 | class THKMC_GameData13 extends THKMC_GameDataBase { 2 | __New( ) { 3 | base.__New("th13", "东方神灵庙 1.00c", 0x4e4bc8a5, 790528) 4 | pd = 5 | (LTrim Comment 6 | 0FB6B5 40FFFFFF ; MOVZX ESI,BYTE PTR SS:[EBP-C0] 7 | 0FB68D 0FFFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-F1] 8 | 0FB695 1CFFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-E4] 9 | 57 ; PUSH EDI 10 | BF 80000000 ; MOV EDI,80 11 | 21FE ; AND ESI,EDI 12 | 03F6 ; ADD ESI,ESI 13 | 03F6 ; ADD ESI,ESI 14 | 21f9 ; and ecx,edi 15 | 0BF1 ; OR ESI,ECX 16 | 0FB68D 18FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-E8] 17 | 03F6 ; ADD ESI,ESI 18 | 21fa ; and edx,edi 19 | 0BF2 ; OR ESI,EDX 20 | 0FB695 15FFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-EB] 21 | 21f9 ; and ecx,edi 22 | 03F6 ; ADD ESI,ESI 23 | 0BF1 ; OR ESI,ECX 24 | 0fb68d C3ffffff ; MOVZX ECX,BYTE PTR SS:[EBP-3D] 25 | 0BD1 ; OR EDX,ECX 26 | 0FB68D 0CFFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-F4] 27 | 0FB685 2AFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-D6] 28 | 03F6 ; ADD ESI,ESI 29 | 21fa ; and edx,edi 30 | 0BF2 ; OR ESI,EDX 31 | 0FB695 1BFFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-E5] 32 | 03F6 ; ADD ESI,ESI 33 | 21fa ; and edx,edi 34 | 0BF2 ; OR ESI,EDX 35 | 21f9 ; and ecx,edi 36 | 8BD0 ; MOV EDX,EAX 37 | 03F6 ; ADD ESI,ESI 38 | 0BF1 ; OR ESI,ECX 39 | 0FB68D 19FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-E7] 40 | C1E6 05 ; SHL ESI,5 41 | 21fa ; and edx,edi 42 | 0BF2 ; OR ESI,EDX 43 | 0FB695 FDFEFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-103] 44 | 21f8 ; and eax,edi 45 | 21f9 ; and ecx,edi 46 | 03F6 ; ADD ESI,ESI 47 | 03F6 ; ADD ESI,ESI 48 | 0BF1 ; OR ESI,ECX 49 | 0FB68D 28FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-D8] 50 | 0BF0 ; OR ESI,EAX 51 | 0FB685 29FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-D7] 52 | 21f8 ; and eax,edi 53 | D1E9 ; SHR ECX,1 54 | 0BC1 ; OR EAX,ECX 55 | 0FB68D 44FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-BC] 56 | 21fa ; and edx,edi 57 | C1E8 02 ; SHR EAX,2 58 | 03F6 ; ADD ESI,ESI 59 | 0BF2 ; OR ESI,EDX 60 | 0FB695 26FFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-DA] 61 | 21fa ; and edx,edi 62 | 0BC2 ; OR EAX,EDX 63 | 0fb695 C4ffffff ; MOVZX EDX,BYTE PTR SS:[EBP-3C] 64 | 0BCA ; OR ECX,EDX 65 | 0FB695 4CFFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-B4] 66 | D1E8 ; SHR EAX,1 67 | 21f9 ; and ecx,edi 68 | 0BC1 ; OR EAX,ECX 69 | 0fb68d CCffffff ; MOVZX ECX,BYTE PTR SS:[EBP-34] 70 | 0BCA ; OR ECX,EDX 71 | 0fb695 C7ffffff ; MOVZX EDX,BYTE PTR SS:[EBP-39] 72 | D1E8 ; SHR EAX,1 73 | 21f9 ; and ecx,edi 74 | 0BC1 ; OR EAX,ECX 75 | 0FB68D 47FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-B9] 76 | D1E8 ; SHR EAX,1 77 | 0BCA ; OR ECX,EDX 78 | 0FB695 45FFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-BB] 79 | 21f9 ; and ecx,edi 80 | 0BC1 ; OR EAX,ECX 81 | 0fb68d C9ffffff ; MOVZX ECX,BYTE PTR SS:[EBP-37] 82 | D1E8 ; SHR EAX,1 83 | 03F6 ; ADD ESI,ESI 84 | 0BF0 ; OR ESI,EAX 85 | 0FB685 49FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-B7] 86 | 0BC1 ; OR EAX,ECX 87 | 21f8 ; and eax,edi 88 | 0BF0 ; OR ESI,EAX 89 | 8A8D 4BFFFFFF ; MOV CL,BYTE PTR SS:[EBP-B5] 90 | 80E2 80 ; AND DL,80 91 | 0FB6C2 ; MOVZX EAX,DL 92 | F7D8 ; NEG EAX 93 | 1BC0 ; SBB EAX,EAX 94 | 80E1 80 ; AND CL,80 95 | 25 90000000 ; AND EAX,90 96 | 0BF0 ; OR ESI,EAX 97 | 8A85 43FFFFFF ; MOV AL,BYTE PTR SS:[EBP-BD] 98 | 0FB6D1 ; MOVZX EDX,CL 99 | F7DA ; NEG EDX 100 | 1BD2 ; SBB EDX,EDX 101 | 24 80 ; AND AL,80 102 | 83E2 60 ; AND EDX,60 103 | 0FB6C8 ; MOVZX ECX,AL 104 | 0BF2 ; OR ESI,EDX 105 | 0FB695 4DFFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-B3] 106 | 5F ; pop edi 107 | ) 108 | this._setPatchData( 0x471845, 0x4719c6, 0x400c00, 0x104, pd ) 109 | } 110 | } 111 | 112 | -------------------------------------------------------------------------------- /gamedata/gamedata14.ahk: -------------------------------------------------------------------------------- 1 | class THKMC_GameData14 extends THKMC_GameDataBase { 2 | __New( ) { 3 | base.__New("th14", "东方辉针城 1.00b", 0x520dc559, 897536) 4 | pd = 5 | (LTrim Comment 6 | 7 | 0FB6B5 40FFFFFF ; MOVZX ESI,BYTE PTR SS:[EBP-C0] 8 | 0FB685 0FFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-F1] 9 | 10 | 57 ; PUSH EDI 11 | BF 80000000 ; MOV EDI,80 12 | 21f8 ; and eax,edi 13 | 21FE ; AND ESI,EDI 14 | C1E6 02 ; SHL ESI,2 15 | 0BF0 ; OR ESI,EAX 16 | 0FB685 1CFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-E4] 17 | 21f8 ; and eax,edi 18 | 0FB68D 15FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-EB] 19 | 03F6 ; ADD ESI,ESI 20 | 0BF0 ; OR ESI,EAX 21 | 0FB685 18FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-E8] 22 | 21f8 ; and eax,edi 23 | 03F6 ; ADD ESI,ESI 24 | 0BF0 ; OR ESI,EAX 25 | 0fb685 C3ffffff ; MOVZX EAX,BYTE PTR SS:[EBP-3D] 26 | 0BC8 ; OR ECX,EAX 27 | 0FB685 1BFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-E5] 28 | 0FB695 2AFFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-D6] 29 | 21f8 ; and eax,edi 30 | 03F6 ; ADD ESI,ESI 31 | 21f9 ; and ecx,edi 32 | 0BF1 ; OR ESI,ECX 33 | 03F6 ; ADD ESI,ESI 34 | 0BF0 ; OR ESI,EAX 35 | 0FB685 0CFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-F4] 36 | 21f8 ; and eax,edi 37 | 03F6 ; ADD ESI,ESI 38 | 0BF0 ; OR ESI,EAX 39 | 8BC2 ; MOV EAX,EDX 40 | 21f8 ; and eax,edi 41 | C1E6 05 ; SHL ESI,5 42 | 0BF0 ; OR ESI,EAX 43 | 0FB685 19FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-E7] 44 | 0BC2 ; OR EAX,EDX 45 | 0FB695 29FFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-D7] 46 | 21f8 ; and eax,edi 47 | 0FB68D 44FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-BC] 48 | C1E6 02 ; SHL ESI,2 49 | 0BF0 ; OR ESI,EAX 50 | 0FB685 FDFEFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-103] 51 | 21f8 ; and eax,edi 52 | 03F6 ; ADD ESI,ESI 53 | 0BF0 ; OR ESI,EAX 54 | 0FB685 28FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-D8] 55 | 21fa ; and edx,edi 56 | D1E8 ; SHR EAX,1 57 | 0BD0 ; OR EDX,EAX 58 | 0FB685 26FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-DA] 59 | C1EA 02 ; SHR EDX,2 60 | 21f8 ; and eax,edi 61 | 0BD0 ; OR EDX,EAX 62 | 0fb685 C4ffffff ; MOVZX EAX,BYTE PTR SS:[EBP-3C] 63 | 0BC8 ; OR ECX,EAX 64 | 0FB685 4CFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-B4] 65 | 21f9 ; and ecx,edi 66 | D1EA ; SHR EDX,1 67 | 0BD1 ; OR EDX,ECX 68 | 0fb68d CCffffff ; MOVZX ECX,BYTE PTR SS:[EBP-34] 69 | 0BC8 ; OR ECX,EAX 70 | 0fb685 C7ffffff ; MOVZX EAX,BYTE PTR SS:[EBP-39] 71 | 21f9 ; and ecx,edi 72 | D1EA ; SHR EDX,1 73 | 0BD1 ; OR EDX,ECX 74 | 0FB68D 47FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-B9] 75 | 0BC8 ; OR ECX,EAX 76 | 0fb685 C9ffffff ; MOVZX EAX,BYTE PTR SS:[EBP-37] 77 | 21f9 ; and ecx,edi 78 | D1EA ; SHR EDX,1 79 | 0BD1 ; OR EDX,ECX 80 | 0FB68D 49FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-B7] 81 | 0BC8 ; OR ECX,EAX 82 | 0FB685 45FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-BB] 83 | 03F6 ; ADD ESI,ESI 84 | 24 80 ; AND AL,80 85 | D1EA ; SHR EDX,1 86 | 0BF2 ; OR ESI,EDX 87 | 21f9 ; and ecx,edi 88 | 0FB6C0 ; MOVZX EAX,AL 89 | 0BF1 ; OR ESI,ECX 90 | F7D8 ; NEG EAX 91 | 1BC0 ; SBB EAX,EAX 92 | 25 90000000 ; AND EAX,90 93 | 0BF0 ; OR ESI,EAX 94 | 0FB685 4BFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-B5] 95 | 24 80 ; AND AL,80 96 | 0FB6C0 ; MOVZX EAX,AL 97 | F7D8 ; NEG EAX 98 | 1BC0 ; SBB EAX,EAX 99 | 83E0 60 ; AND EAX,60 100 | 0BF0 ; OR ESI,EAX 101 | 0FB685 43FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-BD] 102 | 24 80 ; AND AL,80 103 | 0FB6C0 ; MOVZX EAX,AL 104 | F7D8 ; NEG EAX 105 | 1BC0 ; SBB EAX,EAX 106 | 83E0 50 ; AND EAX,50 107 | 0BF0 ; OR ESI,EAX 108 | 0FB685 4DFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-B3] 109 | 110 | 5F ; pop edi 111 | ) 112 | this._setPatchData( 0x401ee7, 0x402063, 0x400c00, 0x104, pd ) 113 | } 114 | } 115 | 116 | -------------------------------------------------------------------------------- /gamedata/gamedata143.ahk: -------------------------------------------------------------------------------- 1 | ; 继承自 TH15 2 | 3 | class THKMC_GameData143 extends THKMC_GameData15 { 4 | __New( ) { 5 | base.__New() 6 | base._setEXEInfo("th143", "弹幕天邪鬼 1.00a", 0x53546435, 939008) 7 | ; 直接继承 th15 的 patchdata 8 | pd := this.patchdata 9 | this._setPatchData( 0x4021b7, 0x402339, 0x400c00, 0x104, pd ) 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /gamedata/gamedata15.ahk: -------------------------------------------------------------------------------- 1 | class THKMC_GameData15 extends THKMC_GameDataBase { 2 | __New( ) { 3 | ; base.__New("th15", "东方绀珠传 1.00a", 0x55BEB248, 951808) ; 去掉注释可直接支持 1.00a,但目前不可两者同时支持 4 | base.__New("th15", "东方绀珠传 1.00b", 0x56121EA7, 951808) 5 | pd = 6 | (LTrim Comment 7 | 0FB6B5 40FFFFFF ; MOVZX ESI,BYTE PTR SS:[EBP-C0] 8 | 0FB685 0FFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-F1] 9 | 57 ; PUSH EDI 10 | BF 80000000 ; MOV EDI,80 11 | 21FE ; AND ESI,EDI 12 | 21F8 ; AND EAX,EDI 13 | C1E6 02 ; SHL ESI,2 14 | 0BF0 ; OR ESI,EAX 15 | 0FB68D 15FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-EB] 16 | 0FB685 1CFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-E4] 17 | 03F6 ; ADD ESI,ESI 18 | 21F8 ; AND EAX,EDI 19 | 0FB695 2AFFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-D6] 20 | 0BF0 ; OR ESI,EAX 21 | 0FB685 18FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-E8] 22 | 21F8 ; AND EAX,EDI 23 | 03F6 ; ADD ESI,ESI 24 | 0BF0 ; OR ESI,EAX 25 | 0FB685 C3FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-3D] 26 | 0BC8 ; OR ECX,EAX 27 | 03F6 ; ADD ESI,ESI 28 | 0FB685 1BFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-E5] 29 | 21F9 ; AND ECX,EDI 30 | 0BF1 ; OR ESI,ECX 31 | 21F8 ; AND EAX,EDI 32 | 03F6 ; ADD ESI,ESI 33 | 0FB68D 44FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-BC] 34 | 0BF0 ; OR ESI,EAX 35 | 0FB685 0CFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-F4] 36 | 21F8 ; AND EAX,EDI 37 | 03F6 ; ADD ESI,ESI 38 | 0BF0 ; OR ESI,EAX 39 | 8BC2 ; MOV EAX,EDX 40 | 21F8 ; AND EAX,EDI 41 | C1E6 05 ; SHL ESI,5 42 | 0BF0 ; OR ESI,EAX 43 | 21FA ; AND EDX,EDI 44 | 0FB685 19FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-E7] 45 | 21F8 ; AND EAX,EDI 46 | C1E6 02 ; SHL ESI,2 47 | 0BF0 ; OR ESI,EAX 48 | 0FB685 FDFEFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-103] 49 | 0BF2 ; OR ESI,EDX 50 | 21F8 ; AND EAX,EDI 51 | 0FB695 29FFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-D7] 52 | 03F6 ; ADD ESI,ESI 53 | 0BF0 ; OR ESI,EAX 54 | 21FA ; AND EDX,EDI 55 | 0FB685 28FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-D8] 56 | 03F6 ; ADD ESI,ESI 57 | D1E8 ; SHR EAX,1 58 | 0BD0 ; OR EDX,EAX 59 | 0FB685 26FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-DA] 60 | C1EA 02 ; SHR EDX,2 61 | 21F8 ; AND EAX,EDI 62 | 0BD0 ; OR EDX,EAX 63 | 0FB685 C4FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-3C] 64 | 0BC8 ; OR ECX,EAX 65 | D1EA ; SHR EDX,1 66 | 0FB685 4CFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-B4] 67 | 21F9 ; AND ECX,EDI 68 | 0BD1 ; OR EDX,ECX 69 | 0FB68D CCFFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-34] 70 | 0BC8 ; OR ECX,EAX 71 | D1EA ; SHR EDX,1 72 | 0FB685 C7FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-39] 73 | 21F9 ; AND ECX,EDI 74 | 0BD1 ; OR EDX,ECX 75 | 0FB68D 47FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-B9] 76 | 0BC8 ; OR ECX,EAX 77 | D1EA ; SHR EDX,1 78 | 0FB685 C9FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-37] 79 | 21F9 ; AND ECX,EDI 80 | 0BD1 ; OR EDX,ECX 81 | 0FB68D 49FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-B7] 82 | 0BC8 ; OR ECX,EAX 83 | D1EA ; SHR EDX,1 84 | 0FB685 45FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-BB] 85 | 0BF2 ; OR ESI,EDX 86 | 21F9 ; AND ECX,EDI 87 | 0BF1 ; OR ESI,ECX 88 | 24 80 ; AND AL,80 89 | 0FB6C0 ; MOVZX EAX,AL 90 | F7D8 ; NEG EAX 91 | 1BC0 ; SBB EAX,EAX 92 | 25 90000000 ; AND EAX,90 93 | 0BF0 ; OR ESI,EAX 94 | 0FB685 4BFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-B5] 95 | 24 80 ; AND AL,80 96 | 0FB6C0 ; MOVZX EAX,AL 97 | F7D8 ; NEG EAX 98 | 1BC0 ; SBB EAX,EAX 99 | 83E0 60 ; AND EAX,60 100 | 0BF0 ; OR ESI,EAX 101 | 0FB685 43FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-BD] 102 | 24 80 ; AND AL,80 103 | 0FB6C0 ; MOVZX EAX,AL 104 | F7D8 ; NEG EAX 105 | 1BC0 ; SBB EAX,EAX 106 | 83E0 50 ; AND EAX,50 107 | 0BF0 ; OR ESI,EAX 108 | 0FB685 4DFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-B3] 109 | 5F ; pop edi 110 | ) 111 | this._setPatchData( 0x402167, 0x4022e9, 0x400c00, 0x104, pd ) 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /gamedata/gamedata16.ahk: -------------------------------------------------------------------------------- 1 | ; 继承自 TH15 2 | 3 | class THKMC_GameData16 extends THKMC_GameData15 { 4 | __New( ) { 5 | base.__New() 6 | base._setEXEInfo("th16", "东方天空璋 1.00a", 0x597F974D, 683520) 7 | ; 直接继承 th15 的 patchdata 8 | pd := this.patchdata 9 | this._setPatchData( 0x401f67, 0x4020e9, 0x400c00, 0x104, pd ) 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /gamedata/gamedata165.ahk: -------------------------------------------------------------------------------- 1 | ; 继承自 TH15 2 | 3 | class THKMC_GameData165 extends THKMC_GameData15 { 4 | __New( ) { 5 | base.__New() 6 | base._setEXEInfo("th165", "秘封噩梦日记 1.00a", 0x5B5FB2EF, 740352) 7 | ; 直接继承 th15 的 patchdata 8 | pd := this.patchdata 9 | this._setPatchData( 0x401bd7, 0x401d59, 0x400c00, 0x104, pd ) 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /gamedata/gamedata17.ahk: -------------------------------------------------------------------------------- 1 | class THKMC_GameData17 extends THKMC_GameDataBase { 2 | __New( ) { 3 | base.__New("th17", "东方鬼形兽 1.00b", 0x5D4E902C, 747520) 4 | pd = 5 | (LTrim Comment 6 | 57 ; PUSH EDI 7 | 87E5 ; xchg ebp,esp 8 | 81c5 10010000 ; add ebp, 110 9 | 0FB6B5 40FFFFFF ; MOVZX ESI,BYTE PTR SS:[EBP-C0] 10 | 0FB685 0FFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-F1] 11 | BF 80000000 ; MOV EDI,80 12 | 21fe ; and esi,edi 13 | 21f8 ; and eax,edi 14 | C1E6 02 ; SHL ESI,2 15 | 0BF0 ; OR ESI,EAX 16 | 0FB68D 15FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-EB] 17 | 0FB685 1CFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-E4] 18 | 03F6 ; ADD ESI,ESI 19 | 21f8 ; and eax,edi 20 | 0FB695 29FFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-D7] 21 | 0BF0 ; OR ESI,EAX 22 | 21fa ; and edx,edi 23 | 0FB685 18FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-E8] 24 | 03F6 ; ADD ESI,ESI 25 | 21f8 ; and eax,edi 26 | 0BF0 ; OR ESI,EAX 27 | 0fb685 C3ffffff ; MOVZX EAX,BYTE PTR SS:[EBP-3D] 28 | 0BC8 ; OR ECX,EAX 29 | 03F6 ; ADD ESI,ESI 30 | 0FB685 1BFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-E5] 31 | 21f9 ; and ecx,edi 32 | 0BF1 ; OR ESI,ECX 33 | 21f8 ; and eax,edi 34 | 0FB68D 19FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-E7] 35 | 03F6 ; ADD ESI,ESI 36 | 0BF0 ; OR ESI,EAX 37 | 0FB685 0CFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-F4] 38 | 21f8 ; and eax,edi 39 | 03F6 ; ADD ESI,ESI 40 | 0BF0 ; OR ESI,EAX 41 | 0FB685 2AFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-D6] 42 | 0BC8 ; OR ECX,EAX 43 | C1E6 07 ; SHL ESI,7 44 | 0FB685 FDFEFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-103] 45 | 21f9 ; and ecx,edi 46 | 0BF1 ; OR ESI,ECX 47 | 21f8 ; and eax,edi 48 | 0FB68D 44FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-BC] 49 | 03F6 ; ADD ESI,ESI 50 | 0BF0 ; OR ESI,EAX 51 | 0FB685 28FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-D8] 52 | D1E8 ; SHR EAX,1 53 | 03F6 ; ADD ESI,ESI 54 | 0BD0 ; OR EDX,EAX 55 | 0FB685 26FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-DA] 56 | 21f8 ; and eax,edi 57 | C1EA 02 ; SHR EDX,2 58 | 0BD0 ; OR EDX,EAX 59 | 0fb685 C4ffffff ; MOVZX EAX,BYTE PTR SS:[EBP-3C] 60 | 0BC8 ; OR ECX,EAX 61 | D1EA ; SHR EDX,1 62 | 0FB685 4CFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-B4] 63 | 21f9 ; and ecx,edi 64 | 0BD1 ; OR EDX,ECX 65 | 0fb68d CCffffff ; MOVZX ECX,BYTE PTR SS:[EBP-34] 66 | 0BC8 ; OR ECX,EAX 67 | D1EA ; SHR EDX,1 68 | 0fb685 C7ffffff ; MOVZX EAX,BYTE PTR SS:[EBP-39] 69 | 21f9 ; and ecx,edi 70 | 0BD1 ; OR EDX,ECX 71 | 0FB68D 47FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-B9] 72 | 0BC8 ; OR ECX,EAX 73 | D1EA ; SHR EDX,1 74 | 0fb685 C9ffffff ; MOVZX EAX,BYTE PTR SS:[EBP-37] 75 | 21f9 ; and ecx,edi 76 | 0BD1 ; OR EDX,ECX 77 | 0FB68D 49FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-B7] 78 | 0BC8 ; OR ECX,EAX 79 | D1EA ; SHR EDX,1 80 | 0FB685 45FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-BB] 81 | 0BF2 ; OR ESI,EDX 82 | 24 80 ; AND AL,80 83 | 21f9 ; and ecx,edi 84 | ; 0FB6C0 ; MOVZX EAX,AL 85 | 0BF1 ; OR ESI,ECX 86 | F7D8 ; NEG EAX 87 | 1BC0 ; SBB EAX,EAX 88 | 25 90000000 ; AND EAX,90 89 | 0BF0 ; OR ESI,EAX 90 | 0FB685 4BFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-B5] 91 | 24 80 ; AND AL,80 92 | ; 0FB6C0 ; MOVZX EAX,AL 93 | F7D8 ; NEG EAX 94 | 1BC0 ; SBB EAX,EAX 95 | 83E0 60 ; AND EAX,60 96 | 0BF0 ; OR ESI,EAX 97 | 0FB685 43FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-BD] 98 | 24 80 ; AND AL,80 99 | ; 0FB6C0 ; MOVZX EAX,AL 100 | F7D8 ; NEG EAX 101 | 1BC0 ; SBB EAX,EAX 102 | 83E0 50 ; AND EAX,50 103 | 0BF0 ; OR ESI,EAX 104 | 0FB685 4DFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-B3] 105 | 81ed 10010000 ; sub ebp, 110 106 | 87E5 ; xchg ebp,esp 107 | 5F ; POP EDI 108 | ) 109 | this._setPatchData( 0x401c8f, 0x401dea, 0x400c00, 0x104, pd ) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /gamedata/gamedata17tr.ahk: -------------------------------------------------------------------------------- 1 | class THKMC_GameData17tr extends THKMC_GameDataBase { 2 | __New( ) { 3 | base.__New("th17tr", "东方鬼形兽 体验版 0.01a", 0x5CCCE6FC, 729600) 4 | pd = 5 | (LTrim Comment 6 | 0FB6B5 40FFFFFF ; MOVZX ESI,BYTE PTR SS:[EBP-C0] 7 | 0FB685 0FFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-F1] 8 | 57 ; PUSH EDI 9 | BF 80000000 ; MOV EDI,80 10 | 21fe ; and esi,edi 11 | 21f8 ; and eax,edi 12 | C1E6 02 ; SHL ESI,2 13 | 0BF0 ; OR ESI,EAX 14 | 0FB68D 15FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-EB] 15 | 0FB685 1CFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-E4] 16 | 03F6 ; ADD ESI,ESI 17 | 21f8 ; and eax,edi 18 | 0FB695 29FFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-D7] 19 | 0BF0 ; OR ESI,EAX 20 | 21fa ; and edx,edi 21 | 0FB685 18FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-E8] 22 | 03F6 ; ADD ESI,ESI 23 | 21f8 ; and eax,edi 24 | 0BF0 ; OR ESI,EAX 25 | 0fb685 C3ffffff ; MOVZX EAX,BYTE PTR SS:[EBP-3D] 26 | 0BC8 ; OR ECX,EAX 27 | 03F6 ; ADD ESI,ESI 28 | 0FB685 1BFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-E5] 29 | 21f9 ; and ecx,edi 30 | 0BF1 ; OR ESI,ECX 31 | 21f8 ; and eax,edi 32 | 0FB68D 19FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-E7] 33 | 03F6 ; ADD ESI,ESI 34 | 0BF0 ; OR ESI,EAX 35 | 0FB685 0CFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-F4] 36 | 21f8 ; and eax,edi 37 | 03F6 ; ADD ESI,ESI 38 | 0BF0 ; OR ESI,EAX 39 | 0FB685 2AFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-D6] 40 | 0BC8 ; OR ECX,EAX 41 | C1E6 07 ; SHL ESI,7 42 | 0FB685 FDFEFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-103] 43 | 21f9 ; and ecx,edi 44 | 0BF1 ; OR ESI,ECX 45 | 21f8 ; and eax,edi 46 | 0FB68D 44FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-BC] 47 | 03F6 ; ADD ESI,ESI 48 | 0BF0 ; OR ESI,EAX 49 | 0FB685 28FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-D8] 50 | D1E8 ; SHR EAX,1 51 | 03F6 ; ADD ESI,ESI 52 | 0BD0 ; OR EDX,EAX 53 | 0FB685 26FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-DA] 54 | 21f8 ; and eax,edi 55 | C1EA 02 ; SHR EDX,2 56 | 0BD0 ; OR EDX,EAX 57 | 0fb685 C4ffffff ; MOVZX EAX,BYTE PTR SS:[EBP-3C] 58 | 0BC8 ; OR ECX,EAX 59 | D1EA ; SHR EDX,1 60 | 0FB685 4CFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-B4] 61 | 21f9 ; and ecx,edi 62 | 0BD1 ; OR EDX,ECX 63 | 0fb68d CCffffff ; MOVZX ECX,BYTE PTR SS:[EBP-34] 64 | 0BC8 ; OR ECX,EAX 65 | D1EA ; SHR EDX,1 66 | 0fb685 C7ffffff ; MOVZX EAX,BYTE PTR SS:[EBP-39] 67 | 21f9 ; and ecx,edi 68 | 0BD1 ; OR EDX,ECX 69 | 0FB68D 47FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-B9] 70 | 0BC8 ; OR ECX,EAX 71 | D1EA ; SHR EDX,1 72 | 0fb685 C9ffffff ; MOVZX EAX,BYTE PTR SS:[EBP-37] 73 | 21f9 ; and ecx,edi 74 | 0BD1 ; OR EDX,ECX 75 | 0FB68D 49FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-B7] 76 | 0BC8 ; OR ECX,EAX 77 | D1EA ; SHR EDX,1 78 | 0FB685 45FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-BB] 79 | 0BF2 ; OR ESI,EDX 80 | 24 80 ; AND AL,80 81 | 21f9 ; and ecx,edi 82 | 0FB6C0 ; MOVZX EAX,AL 83 | 0BF1 ; OR ESI,ECX 84 | F7D8 ; NEG EAX 85 | 1BC0 ; SBB EAX,EAX 86 | 25 90000000 ; AND EAX,90 87 | 0BF0 ; OR ESI,EAX 88 | 0FB685 4BFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-B5] 89 | 24 80 ; AND AL,80 90 | 0FB6C0 ; MOVZX EAX,AL 91 | F7D8 ; NEG EAX 92 | 1BC0 ; SBB EAX,EAX 93 | 83E0 60 ; AND EAX,60 94 | 0BF0 ; OR ESI,EAX 95 | 0FB685 43FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-BD] 96 | 24 80 ; AND AL,80 97 | 0FB6C0 ; MOVZX EAX,AL 98 | F7D8 ; NEG EAX 99 | 1BC0 ; SBB EAX,EAX 100 | 83E0 50 ; AND EAX,50 101 | 0BF0 ; OR ESI,EAX 102 | 0FB685 4DFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-B3] 103 | 5F ; POP EDI 104 | ) 105 | this._setPatchData( 0x401bd6, 0x401d47, 0x400c00, 0x104, pd ) 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /gamedata/gamedata6.ahk: -------------------------------------------------------------------------------- 1 | class THKMC_GameData6 extends THKMC_GameDataBase { 2 | __New( ) { 3 | base.__New("th6", "东方红魔乡 1.02h", 0x3DE9A0FB, 512000) 4 | pd = 5 | (LTrim Comment 6 | 8B85 C0FFFFFF ; MOV EAX,DWORD PTR SS:[EBP-40] 7 | 56 ; push esi 8 | 57 ; push edi 9 | BE FF000000 ; mov esi, 0ff 10 | BF 80000000 ; mov edi, 80 11 | 21f0 ; and eax,esi 12 | 21f8 ; and eax,edi 13 | F7D8 ; NEG EAX 14 | 1BC0 ; SBB EAX,EAX 15 | 83E0 10 ; AND EAX,10 16 | 8B8D F4FEFFFF ; MOV ECX,DWORD PTR SS:[EBP-10C] 17 | 81E1 FFFF0000 ; AND ECX,0FFFF 18 | 0BC8 ; OR ECX,EAX 19 | 66:898D F4FEFFFF ; MOV WORD PTR SS:[EBP-10C],CX 20 | 8B95 C8FFFFFF ; MOV EDX,DWORD PTR SS:[EBP-38] 21 | 21f2 ; and edx,esi 22 | 21fa ; and edx,edi 23 | F7DA ; NEG EDX 24 | 1BD2 ; SBB EDX,EDX 25 | 83E2 20 ; AND EDX,20 26 | 8B85 F4FEFFFF ; MOV EAX,DWORD PTR SS:[EBP-10C] 27 | 25 FFFF0000 ; AND EAX,0FFFF 28 | 0BC2 ; OR EAX,EDX 29 | 66:8985 F4FEFFFF ; MOV WORD PTR SS:[EBP-10C],AX 30 | 8B8D C3FFFFFF ; MOV ECX,DWORD PTR SS:[EBP-3D] 31 | 21f1 ; and ecx,esi 32 | 21f9 ; and ecx,edi 33 | F7D9 ; NEG ECX 34 | 1BC9 ; SBB ECX,ECX 35 | 83E1 40 ; AND ECX,40 36 | 8B95 F4FEFFFF ; MOV EDX,DWORD PTR SS:[EBP-10C] 37 | 81E2 FFFF0000 ; AND EDX,0FFFF 38 | 0BD1 ; OR EDX,ECX 39 | 66:8995 F4FEFFFF ; MOV WORD PTR SS:[EBP-10C],DX 40 | 8B85 C5FFFFFF ; MOV EAX,DWORD PTR SS:[EBP-3B] 41 | 21f0 ; and eax,esi 42 | 21f8 ; and eax,edi 43 | F7D8 ; NEG EAX 44 | 1BC0 ; SBB EAX,EAX 45 | 21f8 ; and eax,edi 46 | 8B8D F4FEFFFF ; MOV ECX,DWORD PTR SS:[EBP-10C] 47 | 81E1 FFFF0000 ; AND ECX,0FFFF 48 | 0BC8 ; OR ECX,EAX 49 | 66:898D F4FEFFFF ; MOV WORD PTR SS:[EBP-10C],CX 50 | 8B95 40FFFFFF ; MOV EDX,DWORD PTR SS:[EBP-C0] 51 | 21f2 ; and edx,esi 52 | 21fa ; and edx,edi 53 | F7DA ; NEG EDX 54 | 1BD2 ; SBB EDX,EDX 55 | 83E2 10 ; AND EDX,10 56 | 8B85 F4FEFFFF ; MOV EAX,DWORD PTR SS:[EBP-10C] 57 | 25 FFFF0000 ; AND EAX,0FFFF 58 | 0BC2 ; OR EAX,EDX 59 | 66:8985 F4FEFFFF ; MOV WORD PTR SS:[EBP-10C],AX 60 | 8B8D 48FFFFFF ; MOV ECX,DWORD PTR SS:[EBP-B8] 61 | 21f1 ; and ecx,esi 62 | 21f9 ; and ecx,edi 63 | F7D9 ; NEG ECX 64 | 1BC9 ; SBB ECX,ECX 65 | 83E1 20 ; AND ECX,20 66 | 8B95 F4FEFFFF ; MOV EDX,DWORD PTR SS:[EBP-10C] 67 | 81E2 FFFF0000 ; AND EDX,0FFFF 68 | 0BD1 ; OR EDX,ECX 69 | 66:8995 F4FEFFFF ; MOV WORD PTR SS:[EBP-10C],DX 70 | 8B85 43FFFFFF ; MOV EAX,DWORD PTR SS:[EBP-BD] 71 | 21f0 ; and eax,esi 72 | 21f8 ; and eax,edi 73 | F7D8 ; NEG EAX 74 | 1BC0 ; SBB EAX,EAX 75 | 83E0 40 ; AND EAX,40 76 | 8B8D F4FEFFFF ; MOV ECX,DWORD PTR SS:[EBP-10C] 77 | 81E1 FFFF0000 ; AND ECX,0FFFF 78 | 0BC8 ; OR ECX,EAX 79 | 66:898D F4FEFFFF ; MOV WORD PTR SS:[EBP-10C],CX 80 | 8B95 45FFFFFF ; MOV EDX,DWORD PTR SS:[EBP-BB] 81 | 21f2 ; and edx,esi 82 | 21fa ; and edx,edi 83 | F7DA ; NEG EDX 84 | 1BD2 ; SBB EDX,EDX 85 | 21fa ; and edx,edi 86 | 8B85 F4FEFFFF ; MOV EAX,DWORD PTR SS:[EBP-10C] 87 | 25 FFFF0000 ; AND EAX,0FFFF 88 | 0BC2 ; OR EAX,EDX 89 | 66:8985 F4FEFFFF ; MOV WORD PTR SS:[EBP-10C],AX 90 | 8B8D 3FFFFFFF ; MOV ECX,DWORD PTR SS:[EBP-C1] 91 | 21f1 ; and ecx,esi 92 | 21f9 ; and ecx,edi 93 | F7D9 ; NEG ECX 94 | 1BC9 ; SBB ECX,ECX 95 | 83E1 50 ; AND ECX,50 96 | 8B95 F4FEFFFF ; MOV EDX,DWORD PTR SS:[EBP-10C] 97 | 81E2 FFFF0000 ; AND EDX,0FFFF 98 | 0BD1 ; OR EDX,ECX 99 | 66:8995 F4FEFFFF ; MOV WORD PTR SS:[EBP-10C],DX 100 | 8B85 41FFFFFF ; MOV EAX,DWORD PTR SS:[EBP-BF] 101 | 21f0 ; and eax,esi 102 | 21f8 ; and eax,edi 103 | F7D8 ; NEG EAX 104 | 1BC0 ; SBB EAX,EAX 105 | 25 90000000 ; AND EAX,90 106 | 8B8D F4FEFFFF ; MOV ECX,DWORD PTR SS:[EBP-10C] 107 | 81E1 FFFF0000 ; AND ECX,0FFFF 108 | 0BC8 ; OR ECX,EAX 109 | 66:898D F4FEFFFF ; MOV WORD PTR SS:[EBP-10C],CX 110 | 8B95 47FFFFFF ; MOV EDX,DWORD PTR SS:[EBP-B9] 111 | 21f2 ; and edx,esi 112 | 21fa ; and edx,edi 113 | F7DA ; NEG EDX 114 | 1BD2 ; SBB EDX,EDX 115 | 83E2 60 ; AND EDX,60 116 | 8B85 F4FEFFFF ; MOV EAX,DWORD PTR SS:[EBP-10C] 117 | 25 FFFF0000 ; AND EAX,0FFFF 118 | 0BC2 ; OR EAX,EDX 119 | 66:8985 F4FEFFFF ; MOV WORD PTR SS:[EBP-10C],AX 120 | 8B8D 49FFFFFF ; MOV ECX,DWORD PTR SS:[EBP-B7] 121 | 21f1 ; and ecx,esi 122 | 21f9 ; and ecx,edi 123 | F7D9 ; NEG ECX 124 | 1BC9 ; SBB ECX,ECX 125 | 81E1 A0000000 ; AND ECX,0A0 126 | 8B95 F4FEFFFF ; MOV EDX,DWORD PTR SS:[EBP-10C] 127 | 81E2 FFFF0000 ; AND EDX,0FFFF 128 | 0BD1 ; OR EDX,ECX 129 | 66:8995 F4FEFFFF ; MOV WORD PTR SS:[EBP-10C],DX 130 | 8B85 BFFFFFFF ; MOV EAX,DWORD PTR SS:[EBP-41] 131 | 21f0 ; and eax,esi 132 | 21f8 ; and eax,edi 133 | F7D8 ; NEG EAX 134 | 1BC0 ; SBB EAX,EAX 135 | 25 00080000 ; AND EAX,800 136 | 8B8D F4FEFFFF ; MOV ECX,DWORD PTR SS:[EBP-10C] 137 | 81E1 FFFF0000 ; AND ECX,0FFFF 138 | 0BC8 ; OR ECX,EAX 139 | 66:898D F4FEFFFF ; MOV WORD PTR SS:[EBP-10C],CX 140 | 8B95 24FFFFFF ; MOV EDX,DWORD PTR SS:[EBP-DC] 141 | 21f2 ; and edx,esi 142 | 21fa ; and edx,edi 143 | F7DA ; NEG EDX 144 | 1BD2 ; SBB EDX,EDX 145 | F7DA ; NEG EDX 146 | 8B85 F4FEFFFF ; MOV EAX,DWORD PTR SS:[EBP-10C] 147 | 25 FFFF0000 ; AND EAX,0FFFF 148 | 0BC2 ; OR EAX,EDX 149 | 66:8985 F4FEFFFF ; MOV WORD PTR SS:[EBP-10C],AX 150 | 8B8D 25FFFFFF ; MOV ECX,DWORD PTR SS:[EBP-DB] 151 | 21f1 ; and ecx,esi 152 | 21f9 ; and ecx,edi 153 | F7D9 ; NEG ECX 154 | 1BC9 ; SBB ECX,ECX 155 | 83E1 02 ; AND ECX,2 156 | 8B95 F4FEFFFF ; MOV EDX,DWORD PTR SS:[EBP-10C] 157 | 81E2 FFFF0000 ; AND EDX,0FFFF 158 | 0BD1 ; OR EDX,ECX 159 | 66:8995 F4FEFFFF ; MOV WORD PTR SS:[EBP-10C],DX 160 | 8B85 22FFFFFF ; MOV EAX,DWORD PTR SS:[EBP-DE] 161 | 21f0 ; and eax,esi 162 | 21f8 ; and eax,edi 163 | F7D8 ; NEG EAX 164 | 1BC0 ; SBB EAX,EAX 165 | 83E0 04 ; AND EAX,4 166 | 8B8D F4FEFFFF ; MOV ECX,DWORD PTR SS:[EBP-10C] 167 | 81E1 FFFF0000 ; AND ECX,0FFFF 168 | 0BC8 ; OR ECX,EAX 169 | 66:898D F4FEFFFF ; MOV WORD PTR SS:[EBP-10C],CX 170 | 8B95 2EFFFFFF ; MOV EDX,DWORD PTR SS:[EBP-D2] 171 | 21f2 ; and edx,esi 172 | 21fa ; and edx,edi 173 | F7DA ; NEG EDX 174 | 1BD2 ; SBB EDX,EDX 175 | 83E2 04 ; AND EDX,4 176 | 8B85 F4FEFFFF ; MOV EAX,DWORD PTR SS:[EBP-10C] 177 | 25 FFFF0000 ; AND EAX,0FFFF 178 | 0BC2 ; OR EAX,EDX 179 | 66:8985 F4FEFFFF ; MOV WORD PTR SS:[EBP-10C],AX 180 | 8B8D F9FEFFFF ; MOV ECX,DWORD PTR SS:[EBP-107] 181 | 21f1 ; and ecx,esi 182 | 21f9 ; and ecx,edi 183 | F7D9 ; NEG ECX 184 | 1BC9 ; SBB ECX,ECX 185 | 83E1 08 ; AND ECX,8 186 | 8B95 F4FEFFFF ; MOV EDX,DWORD PTR SS:[EBP-10C] 187 | 81E2 FFFF0000 ; AND EDX,0FFFF 188 | 0BD1 ; OR EDX,ECX 189 | 66:8995 F4FEFFFF ; MOV WORD PTR SS:[EBP-10C],DX 190 | 8B85 15FFFFFF ; MOV EAX,DWORD PTR SS:[EBP-EB] 191 | 21f0 ; and eax,esi 192 | 21f8 ; and eax,edi 193 | F7D8 ; NEG EAX 194 | 1BC0 ; SBB EAX,EAX 195 | 25 00010000 ; AND EAX,100 196 | 8B8D F4FEFFFF ; MOV ECX,DWORD PTR SS:[EBP-10C] 197 | 81E1 FFFF0000 ; AND ECX,0FFFF 198 | 0BC8 ; OR ECX,EAX 199 | 66:898D F4FEFFFF ; MOV WORD PTR SS:[EBP-10C],CX 200 | 8B95 95FFFFFF ; MOV EDX,DWORD PTR SS:[EBP-6B] 201 | 21f2 ; and edx,esi 202 | 21fa ; and edx,edi 203 | F7DA ; NEG EDX 204 | 1BD2 ; SBB EDX,EDX 205 | 81E2 00010000 ; AND EDX,100 206 | 8B85 F4FEFFFF ; MOV EAX,DWORD PTR SS:[EBP-10C] 207 | 25 FFFF0000 ; AND EAX,0FFFF 208 | 0BC2 ; OR EAX,EDX 209 | 66:8985 F4FEFFFF ; MOV WORD PTR SS:[EBP-10C],AX 210 | 8B8D 08FFFFFF ; MOV ECX,DWORD PTR SS:[EBP-F8] 211 | 21f1 ; and ecx,esi 212 | 21f9 ; and ecx,edi 213 | F7D9 ; NEG ECX 214 | 1BC9 ; SBB ECX,ECX 215 | 81E1 00020000 ; AND ECX,200 216 | 8B95 F4FEFFFF ; MOV EDX,DWORD PTR SS:[EBP-10C] 217 | 81E2 FFFF0000 ; AND EDX,0FFFF 218 | 0BD1 ; OR EDX,ECX 219 | 66:8995 F4FEFFFF ; MOV WORD PTR SS:[EBP-10C],DX 220 | 8B85 17FFFFFF ; MOV EAX,DWORD PTR SS:[EBP-E9] 221 | 21f0 ; and eax,esi 222 | 21f8 ; and eax,edi 223 | F7D8 ; NEG EAX 224 | 1BC0 ; SBB EAX,EAX 225 | 25 00040000 ; AND EAX,400 226 | 8B8D F4FEFFFF ; MOV ECX,DWORD PTR SS:[EBP-10C] 227 | 81E1 FFFF0000 ; AND ECX,0FFFF 228 | 0BC8 ; OR ECX,EAX 229 | 66:898D F4FEFFFF ; MOV WORD PTR SS:[EBP-10C],CX 230 | 8B95 14FFFFFF ; MOV EDX,DWORD PTR SS:[EBP-EC] 231 | 21f2 ; and edx,esi 232 | 21fa ; and edx,edi 233 | F7DA ; NEG EDX 234 | 1BD2 ; SBB EDX,EDX 235 | 81E2 00100000 ; AND EDX,1000 236 | 8B85 F4FEFFFF ; MOV EAX,DWORD PTR SS:[EBP-10C] 237 | 25 FFFF0000 ; AND EAX,0FFFF 238 | 5F ; pop edi 239 | 5E ; pop esi 240 | ) 241 | this._setPatchData( 0x41dc85, 0x41e08c, 0x400000, 0x108, pd ) 242 | this.addrPattern := "i)\b8b[0-9a-f]+\s([0-9a-z]{2}f[ef]ffff)\s" 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /gamedata/gamedata6cn.ahk: -------------------------------------------------------------------------------- 1 | class THKMC_GameData6cn extends THKMC_GameData6 { 2 | __New( ) { 3 | base.__New() 4 | base._setEXEInfo("th6", "东方红魔乡 渔场汉化版", 0x3D6F56A4, 506880) 5 | ; 直接继承 th6 的 patchdata 6 | pd := this.patchdata 7 | this._setPatchData( 0x41d825, 0x41dc2f, 0x400c00, 0x108, pd ) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /gamedata/gamedata7.ahk: -------------------------------------------------------------------------------- 1 | class THKMC_GameData7 extends THKMC_GameDataBase { 2 | __New( ) { 3 | base.__New("th7", "东方妖妖梦 1.00b", 0x3F983F81, 650752) 4 | pd = 5 | (LTrim Comment 6 | 0fb685 C8ffffff ; MOVZX EAX,BYTE PTR SS:[EBP-38] 7 | 57 ; push edi 8 | BF 80000000 ; mov edi, 80 9 | 21f8 ; and eax,edi 10 | F7D8 ; NEG EAX 11 | 1BC0 ; SBB EAX,EAX 12 | 83E0 10 ; AND EAX,10 13 | 0FB78D FCFEFFFF ; MOVZX ECX,WORD PTR SS:[EBP-104] 14 | 0BC8 ; OR ECX,EAX 15 | 66:898D FCFEFFFF ; MOV WORD PTR SS:[EBP-104],CX 16 | 0fb695 D0ffffff ; MOVZX EDX,BYTE PTR SS:[EBP-30] 17 | 21fa ; and edx,edi 18 | F7DA ; NEG EDX 19 | 1BD2 ; SBB EDX,EDX 20 | 83E2 20 ; AND EDX,20 21 | 0FB785 FCFEFFFF ; MOVZX EAX,WORD PTR SS:[EBP-104] 22 | 0BC2 ; OR EAX,EDX 23 | 66:8985 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],AX 24 | 0fb68d CBffffff ; MOVZX ECX,BYTE PTR SS:[EBP-35] 25 | 21f9 ; and ecx,edi 26 | F7D9 ; NEG ECX 27 | 1BC9 ; SBB ECX,ECX 28 | 83E1 40 ; AND ECX,40 29 | 0FB795 FCFEFFFF ; MOVZX EDX,WORD PTR SS:[EBP-104] 30 | 0BD1 ; OR EDX,ECX 31 | 66:8995 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],DX 32 | 0fb685 CDffffff ; MOVZX EAX,BYTE PTR SS:[EBP-33] 33 | 21f8 ; and eax,edi 34 | F7D8 ; NEG EAX 35 | 1BC0 ; SBB EAX,EAX 36 | 21f8 ; and eax,edi 37 | 0FB78D FCFEFFFF ; MOVZX ECX,WORD PTR SS:[EBP-104] 38 | 0BC8 ; OR ECX,EAX 39 | 66:898D FCFEFFFF ; MOV WORD PTR SS:[EBP-104],CX 40 | 0FB695 48FFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-B8] 41 | 21fa ; and edx,edi 42 | F7DA ; NEG EDX 43 | 1BD2 ; SBB EDX,EDX 44 | 83E2 10 ; AND EDX,10 45 | 0FB785 FCFEFFFF ; MOVZX EAX,WORD PTR SS:[EBP-104] 46 | 0BC2 ; OR EAX,EDX 47 | 66:8985 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],AX 48 | 0FB68D 50FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-B0] 49 | 21f9 ; and ecx,edi 50 | F7D9 ; NEG ECX 51 | 1BC9 ; SBB ECX,ECX 52 | 83E1 20 ; AND ECX,20 53 | 0FB795 FCFEFFFF ; MOVZX EDX,WORD PTR SS:[EBP-104] 54 | 0BD1 ; OR EDX,ECX 55 | 66:8995 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],DX 56 | 0FB685 4BFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-B5] 57 | 21f8 ; and eax,edi 58 | F7D8 ; NEG EAX 59 | 1BC0 ; SBB EAX,EAX 60 | 83E0 40 ; AND EAX,40 61 | 0FB78D FCFEFFFF ; MOVZX ECX,WORD PTR SS:[EBP-104] 62 | 0BC8 ; OR ECX,EAX 63 | 66:898D FCFEFFFF ; MOV WORD PTR SS:[EBP-104],CX 64 | 0FB695 4DFFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-B3] 65 | 21fa ; and edx,edi 66 | F7DA ; NEG EDX 67 | 1BD2 ; SBB EDX,EDX 68 | 21fa ; and edx,edi 69 | 0FB785 FCFEFFFF ; MOVZX EAX,WORD PTR SS:[EBP-104] 70 | 0BC2 ; OR EAX,EDX 71 | 66:8985 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],AX 72 | 0FB68D 47FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-B9] 73 | 21f9 ; and ecx,edi 74 | F7D9 ; NEG ECX 75 | 1BC9 ; SBB ECX,ECX 76 | 83E1 50 ; AND ECX,50 77 | 0FB795 FCFEFFFF ; MOVZX EDX,WORD PTR SS:[EBP-104] 78 | 0BD1 ; OR EDX,ECX 79 | 66:8995 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],DX 80 | 0FB685 49FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-B7] 81 | 21f8 ; and eax,edi 82 | F7D8 ; NEG EAX 83 | 1BC0 ; SBB EAX,EAX 84 | 25 90000000 ; AND EAX,90 85 | 0FB78D FCFEFFFF ; MOVZX ECX,WORD PTR SS:[EBP-104] 86 | 0BC8 ; OR ECX,EAX 87 | 66:898D FCFEFFFF ; MOV WORD PTR SS:[EBP-104],CX 88 | 0FB695 4FFFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-B1] 89 | 21fa ; and edx,edi 90 | F7DA ; NEG EDX 91 | 1BD2 ; SBB EDX,EDX 92 | 83E2 60 ; AND EDX,60 93 | 0FB785 FCFEFFFF ; MOVZX EAX,WORD PTR SS:[EBP-104] 94 | 0BC2 ; OR EAX,EDX 95 | 66:8985 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],AX 96 | 0FB68D 51FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-AF] 97 | 21f9 ; and ecx,edi 98 | F7D9 ; NEG ECX 99 | 1BC9 ; SBB ECX,ECX 100 | 81E1 A0000000 ; AND ECX,0A0 101 | 0FB795 FCFEFFFF ; MOVZX EDX,WORD PTR SS:[EBP-104] 102 | 0BD1 ; OR EDX,ECX 103 | 66:8995 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],DX 104 | 0fb685 C7ffffff ; MOVZX EAX,BYTE PTR SS:[EBP-39] 105 | 21f8 ; and eax,edi 106 | F7D8 ; NEG EAX 107 | 1BC0 ; SBB EAX,EAX 108 | 25 00080000 ; AND EAX,800 109 | 0FB78D FCFEFFFF ; MOVZX ECX,WORD PTR SS:[EBP-104] 110 | 0BC8 ; OR ECX,EAX 111 | 66:898D FCFEFFFF ; MOV WORD PTR SS:[EBP-104],CX 112 | 0FB695 20FFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-E0] 113 | 21fa ; and edx,edi 114 | F7DA ; NEG EDX 115 | 1BD2 ; SBB EDX,EDX 116 | 81E2 00200000 ; AND EDX,2000 117 | 0FB785 FCFEFFFF ; MOVZX EAX,WORD PTR SS:[EBP-104] 118 | 0BC2 ; OR EAX,EDX 119 | 66:8985 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],AX 120 | 0FB68D 2CFFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-D4] 121 | 21f9 ; and ecx,edi 122 | F7D9 ; NEG ECX 123 | 1BC9 ; SBB ECX,ECX 124 | F7D9 ; NEG ECX 125 | 0FB795 FCFEFFFF ; MOVZX EDX,WORD PTR SS:[EBP-104] 126 | 0BD1 ; OR EDX,ECX 127 | 66:8995 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],DX 128 | 0FB685 2DFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-D3] 129 | 21f8 ; and eax,edi 130 | F7D8 ; NEG EAX 131 | 1BC0 ; SBB EAX,EAX 132 | 83E0 02 ; AND EAX,2 133 | 0FB78D FCFEFFFF ; MOVZX ECX,WORD PTR SS:[EBP-104] 134 | 0BC8 ; OR ECX,EAX 135 | 66:898D FCFEFFFF ; MOV WORD PTR SS:[EBP-104],CX 136 | 0FB695 2AFFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-D6] 137 | 21fa ; and edx,edi 138 | F7DA ; NEG EDX 139 | 1BD2 ; SBB EDX,EDX 140 | 83E2 04 ; AND EDX,4 141 | 0FB785 FCFEFFFF ; MOVZX EAX,WORD PTR SS:[EBP-104] 142 | 0BC2 ; OR EAX,EDX 143 | 66:8985 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],AX 144 | 0FB68D 36FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-CA] 145 | 21f9 ; and ecx,edi 146 | F7D9 ; NEG ECX 147 | 1BC9 ; SBB ECX,ECX 148 | 83E1 04 ; AND ECX,4 149 | 0FB795 FCFEFFFF ; MOVZX EDX,WORD PTR SS:[EBP-104] 150 | 0BD1 ; OR EDX,ECX 151 | 66:8995 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],DX 152 | 0FB685 01FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-FF] 153 | 21f8 ; and eax,edi 154 | F7D8 ; NEG EAX 155 | 1BC0 ; SBB EAX,EAX 156 | 83E0 08 ; AND EAX,8 157 | 0FB78D FCFEFFFF ; MOVZX ECX,WORD PTR SS:[EBP-104] 158 | 0BC8 ; OR ECX,EAX 159 | 66:898D FCFEFFFF ; MOV WORD PTR SS:[EBP-104],CX 160 | 0FB695 1DFFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-E3] 161 | 21fa ; and edx,edi 162 | F7DA ; NEG EDX 163 | 1BD2 ; SBB EDX,EDX 164 | 81E2 00010000 ; AND EDX,100 165 | 0FB785 FCFEFFFF ; MOVZX EAX,WORD PTR SS:[EBP-104] 166 | 0BC2 ; OR EAX,EDX 167 | 66:8985 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],AX 168 | 0fb68d 9Dffffff ; MOVZX ECX,BYTE PTR SS:[EBP-63] 169 | 21f9 ; and ecx,edi 170 | F7D9 ; NEG ECX 171 | 1BC9 ; SBB ECX,ECX 172 | 81E1 00010000 ; AND ECX,100 173 | 0FB795 FCFEFFFF ; MOVZX EDX,WORD PTR SS:[EBP-104] 174 | 0BD1 ; OR EDX,ECX 175 | 66:8995 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],DX 176 | 0FB685 10FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-F0] 177 | 21f8 ; and eax,edi 178 | F7D8 ; NEG EAX 179 | 1BC0 ; SBB EAX,EAX 180 | 25 00020000 ; AND EAX,200 181 | 0FB78D FCFEFFFF ; MOVZX ECX,WORD PTR SS:[EBP-104] 182 | 0BC8 ; OR ECX,EAX 183 | 66:898D FCFEFFFF ; MOV WORD PTR SS:[EBP-104],CX 184 | 0FB695 1FFFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-E1] 185 | 21fa ; and edx,edi 186 | F7DA ; NEG EDX 187 | 1BD2 ; SBB EDX,EDX 188 | 81E2 00040000 ; AND EDX,400 189 | 0FB785 FCFEFFFF ; MOVZX EAX,WORD PTR SS:[EBP-104] 190 | 0BC2 ; OR EAX,EDX 191 | 66:8985 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],AX 192 | 0FB68D 1CFFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-E4] 193 | 21f9 ; and ecx,edi 194 | F7D9 ; NEG ECX 195 | 1BC9 ; SBB ECX,ECX 196 | 81E1 00100000 ; AND ECX,1000 197 | 0FB795 FCFEFFFF ; MOVZX EDX,WORD PTR SS:[EBP-104] 198 | 0BD1 ; OR EDX,ECX 199 | 66:8995 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],DX 200 | 0FB685 13FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-ED] 201 | 21f8 ; and eax,edi 202 | 5F ; pop edi 203 | ) 204 | this._setPatchData( 0x430f2a, 0x431298, 0x400C00, 0x100, pd ) 205 | } 206 | 207 | doCustomPatch( pExeData ) { 208 | ; 检查是否中文版 209 | result := compareHexToBinary( "EB56909090", pExeData, 0x37A97 ) 210 | if ( result = 0 ) { 211 | AddLog("【提示】这是渔场汉化版") 212 | } 213 | else { 214 | ; 若不是中文版,则去掉自校验 215 | writeHexToBinary( "C705145C570000EE0900C705105C57005C44C5AEC3", pExeData, 0x352c0 ) 216 | } 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /gamedata/gamedata8.ahk: -------------------------------------------------------------------------------- 1 | class THKMC_GameData8 extends THKMC_GameDataBase { 2 | __New( ) { 3 | base.__New("th8", "东方永夜抄 1.00d", 0x414daf4c, 840704) 4 | pd = 5 | (LTrim Comment 6 | 53 ; PUSH EBX 7 | BB 80000000 ; MOV EBX,80 8 | 9 | 0fb695 C8ffffff ; MOVZX EDX,BYTE PTR SS:[EBP-38] 10 | 21da ; and edx,ebx 11 | F7DA ; NEG EDX 12 | 1BD2 ; SBB EDX,EDX 13 | 83E2 10 ; AND EDX,10 14 | 0FB785 FCFEFFFF ; MOVZX EAX,WORD PTR SS:[EBP-104] 15 | 0BC2 ; OR EAX,EDX 16 | 66:8985 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],AX 17 | 0fb68d D0ffffff ; MOVZX ECX,BYTE PTR SS:[EBP-30] 18 | 21d9 ; and ecx,ebx 19 | F7D9 ; NEG ECX 20 | 1BC9 ; SBB ECX,ECX 21 | 83E1 20 ; AND ECX,20 22 | 0FB795 FCFEFFFF ; MOVZX EDX,WORD PTR SS:[EBP-104] 23 | 0BD1 ; OR EDX,ECX 24 | 66:8995 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],DX 25 | 0fb685 CBffffff ; MOVZX EAX,BYTE PTR SS:[EBP-35] 26 | 21d8 ; and eax,ebx 27 | F7D8 ; NEG EAX 28 | 1BC0 ; SBB EAX,EAX 29 | 83E0 40 ; AND EAX,40 30 | 0FB78D FCFEFFFF ; MOVZX ECX,WORD PTR SS:[EBP-104] 31 | 0BC8 ; OR ECX,EAX 32 | 66:898D FCFEFFFF ; MOV WORD PTR SS:[EBP-104],CX 33 | 0fb695 CDffffff ; MOVZX EDX,BYTE PTR SS:[EBP-33] 34 | 21da ; and edx,ebx 35 | F7DA ; NEG EDX 36 | 1BD2 ; SBB EDX,EDX 37 | 21da ; and edx,ebx 38 | 0FB785 FCFEFFFF ; MOVZX EAX,WORD PTR SS:[EBP-104] 39 | 0BC2 ; OR EAX,EDX 40 | 66:8985 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],AX 41 | 0FB68D 48FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-B8] 42 | 21d9 ; and ecx,ebx 43 | F7D9 ; NEG ECX 44 | 1BC9 ; SBB ECX,ECX 45 | 83E1 10 ; AND ECX,10 46 | 0FB795 FCFEFFFF ; MOVZX EDX,WORD PTR SS:[EBP-104] 47 | 0BD1 ; OR EDX,ECX 48 | 66:8995 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],DX 49 | 0FB685 50FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-B0] 50 | 21d8 ; and eax,ebx 51 | F7D8 ; NEG EAX 52 | 1BC0 ; SBB EAX,EAX 53 | 83E0 20 ; AND EAX,20 54 | 0FB78D FCFEFFFF ; MOVZX ECX,WORD PTR SS:[EBP-104] 55 | 0BC8 ; OR ECX,EAX 56 | 66:898D FCFEFFFF ; MOV WORD PTR SS:[EBP-104],CX 57 | 0FB695 4BFFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-B5] 58 | 21da ; and edx,ebx 59 | F7DA ; NEG EDX 60 | 1BD2 ; SBB EDX,EDX 61 | 83E2 40 ; AND EDX,40 62 | 0FB785 FCFEFFFF ; MOVZX EAX,WORD PTR SS:[EBP-104] 63 | 0BC2 ; OR EAX,EDX 64 | 66:8985 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],AX 65 | 0FB68D 4DFFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-B3] 66 | 21d9 ; and ecx,ebx 67 | F7D9 ; NEG ECX 68 | 1BC9 ; SBB ECX,ECX 69 | 21d9 ; and ecx,ebx 70 | 0FB795 FCFEFFFF ; MOVZX EDX,WORD PTR SS:[EBP-104] 71 | 0BD1 ; OR EDX,ECX 72 | 66:8995 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],DX 73 | 0FB685 47FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-B9] 74 | 21d8 ; and eax,ebx 75 | F7D8 ; NEG EAX 76 | 1BC0 ; SBB EAX,EAX 77 | 83E0 50 ; AND EAX,50 78 | 0FB78D FCFEFFFF ; MOVZX ECX,WORD PTR SS:[EBP-104] 79 | 0BC8 ; OR ECX,EAX 80 | 66:898D FCFEFFFF ; MOV WORD PTR SS:[EBP-104],CX 81 | 0FB695 49FFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-B7] 82 | 21da ; and edx,ebx 83 | F7DA ; NEG EDX 84 | 1BD2 ; SBB EDX,EDX 85 | 81E2 90000000 ; AND EDX,90 86 | 0FB785 FCFEFFFF ; MOVZX EAX,WORD PTR SS:[EBP-104] 87 | 0BC2 ; OR EAX,EDX 88 | 66:8985 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],AX 89 | 0FB68D 4FFFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-B1] 90 | 21d9 ; and ecx,ebx 91 | F7D9 ; NEG ECX 92 | 1BC9 ; SBB ECX,ECX 93 | 83E1 60 ; AND ECX,60 94 | 0FB795 FCFEFFFF ; MOVZX EDX,WORD PTR SS:[EBP-104] 95 | 0BD1 ; OR EDX,ECX 96 | 66:8995 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],DX 97 | 0FB685 51FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-AF] 98 | 21d8 ; and eax,ebx 99 | F7D8 ; NEG EAX 100 | 1BC0 ; SBB EAX,EAX 101 | 25 A0000000 ; AND EAX,0A0 102 | 0FB78D FCFEFFFF ; MOVZX ECX,WORD PTR SS:[EBP-104] 103 | 0BC8 ; OR ECX,EAX 104 | 66:898D FCFEFFFF ; MOV WORD PTR SS:[EBP-104],CX 105 | 0fb695 C7ffffff ; MOVZX EDX,BYTE PTR SS:[EBP-39] 106 | 21da ; and edx,ebx 107 | F7DA ; NEG EDX 108 | 1BD2 ; SBB EDX,EDX 109 | 81E2 00080000 ; AND EDX,800 110 | 0FB785 FCFEFFFF ; MOVZX EAX,WORD PTR SS:[EBP-104] 111 | 0BC2 ; OR EAX,EDX 112 | 66:8985 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],AX 113 | 0FB68D 19FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-E7] 114 | 21d9 ; and ecx,ebx 115 | F7D9 ; NEG ECX 116 | 1BC9 ; SBB ECX,ECX 117 | 81E1 00080000 ; AND ECX,800 118 | 0FB795 FCFEFFFF ; MOVZX EDX,WORD PTR SS:[EBP-104] 119 | 0BD1 ; OR EDX,ECX 120 | 66:8995 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],DX 121 | 0FB685 20FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-E0] 122 | 21d8 ; and eax,ebx 123 | F7D8 ; NEG EAX 124 | 1BC0 ; SBB EAX,EAX 125 | 25 00200000 ; AND EAX,2000 126 | 0FB78D FCFEFFFF ; MOVZX ECX,WORD PTR SS:[EBP-104] 127 | 0BC8 ; OR ECX,EAX 128 | 66:898D FCFEFFFF ; MOV WORD PTR SS:[EBP-104],CX 129 | 0FB695 2CFFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-D4] 130 | 21da ; and edx,ebx 131 | F7DA ; NEG EDX 132 | 1BD2 ; SBB EDX,EDX 133 | F7DA ; NEG EDX 134 | 0FB785 FCFEFFFF ; MOVZX EAX,WORD PTR SS:[EBP-104] 135 | 0BC2 ; OR EAX,EDX 136 | 66:8985 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],AX 137 | 0FB68D 2DFFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-D3] 138 | 21d9 ; and ecx,ebx 139 | F7D9 ; NEG ECX 140 | 1BC9 ; SBB ECX,ECX 141 | 83E1 02 ; AND ECX,2 142 | 0FB795 FCFEFFFF ; MOVZX EDX,WORD PTR SS:[EBP-104] 143 | 0BD1 ; OR EDX,ECX 144 | 66:8995 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],DX 145 | 0FB685 2AFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-D6] 146 | 21d8 ; and eax,ebx 147 | F7D8 ; NEG EAX 148 | 1BC0 ; SBB EAX,EAX 149 | 83E0 04 ; AND EAX,4 150 | 0FB78D FCFEFFFF ; MOVZX ECX,WORD PTR SS:[EBP-104] 151 | 0BC8 ; OR ECX,EAX 152 | 66:898D FCFEFFFF ; MOV WORD PTR SS:[EBP-104],CX 153 | 0FB695 36FFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-CA] 154 | 21da ; and edx,ebx 155 | F7DA ; NEG EDX 156 | 1BD2 ; SBB EDX,EDX 157 | 83E2 04 ; AND EDX,4 158 | 0FB785 FCFEFFFF ; MOVZX EAX,WORD PTR SS:[EBP-104] 159 | 0BC2 ; OR EAX,EDX 160 | 66:8985 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],AX 161 | 0FB68D 01FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-FF] 162 | 21d9 ; and ecx,ebx 163 | F7D9 ; NEG ECX 164 | 1BC9 ; SBB ECX,ECX 165 | 83E1 08 ; AND ECX,8 166 | 0FB795 FCFEFFFF ; MOVZX EDX,WORD PTR SS:[EBP-104] 167 | 0BD1 ; OR EDX,ECX 168 | 66:8995 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],DX 169 | 0FB685 1DFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-E3] 170 | 21d8 ; and eax,ebx 171 | F7D8 ; NEG EAX 172 | 1BC0 ; SBB EAX,EAX 173 | 25 00010000 ; AND EAX,100 174 | 0FB78D FCFEFFFF ; MOVZX ECX,WORD PTR SS:[EBP-104] 175 | 0BC8 ; OR ECX,EAX 176 | 66:898D FCFEFFFF ; MOV WORD PTR SS:[EBP-104],CX 177 | 0fb695 9Dffffff ; MOVZX EDX,BYTE PTR SS:[EBP-63] 178 | 21da ; and edx,ebx 179 | F7DA ; NEG EDX 180 | 1BD2 ; SBB EDX,EDX 181 | 81E2 00010000 ; AND EDX,100 182 | 0FB785 FCFEFFFF ; MOVZX EAX,WORD PTR SS:[EBP-104] 183 | 0BC2 ; OR EAX,EDX 184 | 66:8985 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],AX 185 | 0FB68D 10FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-F0] 186 | 21d9 ; and ecx,ebx 187 | F7D9 ; NEG ECX 188 | 1BC9 ; SBB ECX,ECX 189 | 81E1 00020000 ; AND ECX,200 190 | 0FB795 FCFEFFFF ; MOVZX EDX,WORD PTR SS:[EBP-104] 191 | 0BD1 ; OR EDX,ECX 192 | 66:8995 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],DX 193 | 0FB685 1FFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-E1] 194 | 21d8 ; and eax,ebx 195 | F7D8 ; NEG EAX 196 | 1BC0 ; SBB EAX,EAX 197 | 25 00040000 ; AND EAX,400 198 | 0FB78D FCFEFFFF ; MOVZX ECX,WORD PTR SS:[EBP-104] 199 | 0BC8 ; OR ECX,EAX 200 | 66:898D FCFEFFFF ; MOV WORD PTR SS:[EBP-104],CX 201 | 0FB695 1CFFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-E4] 202 | 21da ; and edx,ebx 203 | F7DA ; NEG EDX 204 | 1BD2 ; SBB EDX,EDX 205 | 81E2 00100000 ; AND EDX,1000 206 | 0FB785 FCFEFFFF ; MOVZX EAX,WORD PTR SS:[EBP-104] 207 | 0BC2 ; OR EAX,EDX 208 | 66:8985 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],AX 209 | 0FB68D 13FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-ED] 210 | 211 | 5B ; pop ebx 212 | ) 213 | this._setPatchData( 0x43dd9e, 0x43e12e, 0x400200, 0x100, pd ) 214 | } 215 | 216 | doCustomPatch( pExeData ) { 217 | ; 检查是否中文版 218 | result := compareHexToBinary( "EB5A909090", pExeData, 0x45fd2 ) 219 | if ( result = 0 ) { 220 | AddLog("【提示】这是渔场汉化版") 221 | } 222 | else { 223 | ; 若不是中文版,则去掉自校验 224 | writeHexToBinary( "C785E8FEFFFF B96168A2 C785F0FEFFFF00D40C00", pExeData, 0x435ef ) 225 | } 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /gamedata/gamedata95.ahk: -------------------------------------------------------------------------------- 1 | class THKMC_GameData95 extends THKMC_GameDataBase { 2 | __New( ) { 3 | base.__New("th95", "东方文花帖 1.02a", 0x43c68d3f, 696832) 4 | 5 | 6 | pd = 7 | (LTrim Comment 8 | 9 | 53 ; PUSH EBX 10 | BB 80000000 ; MOV EBX,80 11 | 12 | 0fb68d C8ffffff ; MOVZX ECX,BYTE PTR SS:[EBP-38] 13 | 21d9 ; and ecx,ebx 14 | F7D9 ; NEG ECX 15 | 1BC9 ; SBB ECX,ECX 16 | 83E1 10 ; AND ECX,10 17 | 0FB795 FCFEFFFF ; MOVZX EDX,WORD PTR SS:[EBP-104] 18 | 0BD1 ; OR EDX,ECX 19 | 66:8995 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],DX 20 | 0fb685 D0ffffff ; MOVZX EAX,BYTE PTR SS:[EBP-30] 21 | 21d8 ; and eax,ebx 22 | F7D8 ; NEG EAX 23 | 1BC0 ; SBB EAX,EAX 24 | 83E0 20 ; AND EAX,20 25 | 0FB78D FCFEFFFF ; MOVZX ECX,WORD PTR SS:[EBP-104] 26 | 0BC8 ; OR ECX,EAX 27 | 66:898D FCFEFFFF ; MOV WORD PTR SS:[EBP-104],CX 28 | 0fb695 CBffffff ; MOVZX EDX,BYTE PTR SS:[EBP-35] 29 | 21da ; and edx,ebx 30 | F7DA ; NEG EDX 31 | 1BD2 ; SBB EDX,EDX 32 | 83E2 40 ; AND EDX,40 33 | 0FB785 FCFEFFFF ; MOVZX EAX,WORD PTR SS:[EBP-104] 34 | 0BC2 ; OR EAX,EDX 35 | 66:8985 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],AX 36 | 0fb68d CDffffff ; MOVZX ECX,BYTE PTR SS:[EBP-33] 37 | 21d9 ; and ecx,ebx 38 | F7D9 ; NEG ECX 39 | 1BC9 ; SBB ECX,ECX 40 | 21d9 ; and ecx,ebx 41 | 0FB795 FCFEFFFF ; MOVZX EDX,WORD PTR SS:[EBP-104] 42 | 0BD1 ; OR EDX,ECX 43 | 66:8995 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],DX 44 | 0FB685 2CFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-D4] 45 | 21d8 ; and eax,ebx 46 | F7D8 ; NEG EAX 47 | 1BC0 ; SBB EAX,EAX 48 | 83E0 02 ; AND EAX,2 49 | 0FB78D FCFEFFFF ; MOVZX ECX,WORD PTR SS:[EBP-104] 50 | 0BC8 ; OR ECX,EAX 51 | 66:898D FCFEFFFF ; MOV WORD PTR SS:[EBP-104],CX 52 | 0FB695 2DFFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-D3] 53 | 21da ; and edx,ebx 54 | F7DA ; NEG EDX 55 | 1BD2 ; SBB EDX,EDX 56 | F7DA ; NEG EDX 57 | 0FB785 FCFEFFFF ; MOVZX EAX,WORD PTR SS:[EBP-104] 58 | 0BC2 ; OR EAX,EDX 59 | 66:8985 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],AX 60 | 0FB68D 2AFFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-D6] 61 | 21d9 ; and ecx,ebx 62 | F7D9 ; NEG ECX 63 | 1BC9 ; SBB ECX,ECX 64 | 83E1 05 ; AND ECX,5 65 | 0FB795 FCFEFFFF ; MOVZX EDX,WORD PTR SS:[EBP-104] 66 | 0BD1 ; OR EDX,ECX 67 | 66:8995 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],DX 68 | 0FB685 48FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-B8] 69 | 21d8 ; and eax,ebx 70 | F7D8 ; NEG EAX 71 | 1BC0 ; SBB EAX,EAX 72 | 83E0 10 ; AND EAX,10 73 | 0FB78D FCFEFFFF ; MOVZX ECX,WORD PTR SS:[EBP-104] 74 | 0BC8 ; OR ECX,EAX 75 | 66:898D FCFEFFFF ; MOV WORD PTR SS:[EBP-104],CX 76 | 0FB695 50FFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-B0] 77 | 21da ; and edx,ebx 78 | F7DA ; NEG EDX 79 | 1BD2 ; SBB EDX,EDX 80 | 83E2 20 ; AND EDX,20 81 | 0FB785 FCFEFFFF ; MOVZX EAX,WORD PTR SS:[EBP-104] 82 | 0BC2 ; OR EAX,EDX 83 | 66:8985 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],AX 84 | 0FB68D 4BFFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-B5] 85 | 21d9 ; and ecx,ebx 86 | F7D9 ; NEG ECX 87 | 1BC9 ; SBB ECX,ECX 88 | 83E1 40 ; AND ECX,40 89 | 0FB795 FCFEFFFF ; MOVZX EDX,WORD PTR SS:[EBP-104] 90 | 0BD1 ; OR EDX,ECX 91 | 66:8995 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],DX 92 | 0FB685 4DFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-B3] 93 | 21d8 ; and eax,ebx 94 | F7D8 ; NEG EAX 95 | 1BC0 ; SBB EAX,EAX 96 | 21d8 ; and eax,ebx 97 | 0FB78D FCFEFFFF ; MOVZX ECX,WORD PTR SS:[EBP-104] 98 | 0BC8 ; OR ECX,EAX 99 | 66:898D FCFEFFFF ; MOV WORD PTR SS:[EBP-104],CX 100 | 0FB695 47FFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-B9] 101 | 21da ; and edx,ebx 102 | F7DA ; NEG EDX 103 | 1BD2 ; SBB EDX,EDX 104 | 83E2 50 ; AND EDX,50 105 | 0FB785 FCFEFFFF ; MOVZX EAX,WORD PTR SS:[EBP-104] 106 | 0BC2 ; OR EAX,EDX 107 | 66:8985 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],AX 108 | 0FB68D 49FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-B7] 109 | 21d9 ; and ecx,ebx 110 | F7D9 ; NEG ECX 111 | 1BC9 ; SBB ECX,ECX 112 | 81E1 90000000 ; AND ECX,90 113 | 0FB795 FCFEFFFF ; MOVZX EDX,WORD PTR SS:[EBP-104] 114 | 0BD1 ; OR EDX,ECX 115 | 66:8995 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],DX 116 | 0FB685 4FFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-B1] 117 | 21d8 ; and eax,ebx 118 | F7D8 ; NEG EAX 119 | 1BC0 ; SBB EAX,EAX 120 | 83E0 60 ; AND EAX,60 121 | 0FB78D FCFEFFFF ; MOVZX ECX,WORD PTR SS:[EBP-104] 122 | 0BC8 ; OR ECX,EAX 123 | 66:898D FCFEFFFF ; MOV WORD PTR SS:[EBP-104],CX 124 | 0FB695 51FFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-AF] 125 | 21da ; and edx,ebx 126 | F7DA ; NEG EDX 127 | 1BD2 ; SBB EDX,EDX 128 | 81E2 A0000000 ; AND EDX,0A0 129 | 0FB785 FCFEFFFF ; MOVZX EAX,WORD PTR SS:[EBP-104] 130 | 0BC2 ; OR EAX,EDX 131 | 66:8985 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],AX 132 | 0FB685 1DFFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-E3] 133 | 21d8 ; and eax,ebx 134 | F7D8 ; NEG EAX 135 | 1BC0 ; SBB EAX,EAX 136 | 25 00010000 ; AND EAX,100 137 | 0FB78D FCFEFFFF ; MOVZX ECX,WORD PTR SS:[EBP-104] 138 | 0BC8 ; OR ECX,EAX 139 | 66:898D FCFEFFFF ; MOV WORD PTR SS:[EBP-104],CX 140 | 0FB695 01FFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-FF] 141 | 21da ; and edx,ebx 142 | F7DA ; NEG EDX 143 | 1BD2 ; SBB EDX,EDX 144 | 83E2 08 ; AND EDX,8 145 | 0FB785 FCFEFFFF ; MOVZX EAX,WORD PTR SS:[EBP-104] 146 | 0BC2 ; OR EAX,EDX 147 | 66:8985 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],AX 148 | 0FB68D 1CFFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-E4] 149 | 21d9 ; and ecx,ebx 150 | F7D9 ; NEG ECX 151 | 1BC9 ; SBB ECX,ECX 152 | 81E1 00100000 ; AND ECX,1000 153 | 0FB795 FCFEFFFF ; MOVZX EDX,WORD PTR SS:[EBP-104] 154 | 0BD1 ; OR EDX,ECX 155 | 66:8995 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],DX 156 | 0fb685 C7ffffff ; MOVZX EAX,BYTE PTR SS:[EBP-39] 157 | 21d8 ; and eax,ebx 158 | F7D8 ; NEG EAX 159 | 1BC0 ; SBB EAX,EAX 160 | 25 00080000 ; AND EAX,800 161 | 0FB78D FCFEFFFF ; MOVZX ECX,WORD PTR SS:[EBP-104] 162 | 0BC8 ; OR ECX,EAX 163 | 66:898D FCFEFFFF ; MOV WORD PTR SS:[EBP-104],CX 164 | 0FB695 19FFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-E7] 165 | 21da ; and edx,ebx 166 | F7DA ; NEG EDX 167 | 1BD2 ; SBB EDX,EDX 168 | 81E2 00080000 ; AND EDX,800 169 | 0FB785 FCFEFFFF ; MOVZX EAX,WORD PTR SS:[EBP-104] 170 | 0BC2 ; OR EAX,EDX 171 | 66:8985 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],AX 172 | 0FB68D 20FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-E0] 173 | 21d9 ; and ecx,ebx 174 | F7D9 ; NEG ECX 175 | 1BC9 ; SBB ECX,ECX 176 | 81E1 00200000 ; AND ECX,2000 177 | 0FB795 FCFEFFFF ; MOVZX EDX,WORD PTR SS:[EBP-104] 178 | 0BD1 ; OR EDX,ECX 179 | 66:8995 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],DX 180 | 0FB685 10FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-F0] 181 | 21d8 ; and eax,ebx 182 | F7D8 ; NEG EAX 183 | 1BC0 ; SBB EAX,EAX 184 | 25 00020000 ; AND EAX,200 185 | 0FB78D FCFEFFFF ; MOVZX ECX,WORD PTR SS:[EBP-104] 186 | 0BC8 ; OR ECX,EAX 187 | 66:898D FCFEFFFF ; MOV WORD PTR SS:[EBP-104],CX 188 | 0FB695 1FFFFFFF ; MOVZX EDX,BYTE PTR SS:[EBP-E1] 189 | 21da ; and edx,ebx 190 | F7DA ; NEG EDX 191 | 1BD2 ; SBB EDX,EDX 192 | 81E2 00040000 ; AND EDX,400 193 | 0FB785 FCFEFFFF ; MOVZX EAX,WORD PTR SS:[EBP-104] 194 | 0BC2 ; OR EAX,EDX 195 | 66:8985 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],AX 196 | 0FB68D 13FFFFFF ; MOVZX ECX,BYTE PTR SS:[EBP-ED] 197 | 21d9 ; and ecx,ebx 198 | F7D9 ; NEG ECX 199 | 1BC9 ; SBB ECX,ECX 200 | 81E1 00400000 ; AND ECX,4000 201 | 0FB795 FCFEFFFF ; MOVZX EDX,WORD PTR SS:[EBP-104] 202 | 0BD1 ; OR EDX,ECX 203 | 66:8995 FCFEFFFF ; MOV WORD PTR SS:[EBP-104],DX 204 | 0FB685 26FFFFFF ; MOVZX EAX,BYTE PTR SS:[EBP-DA] 205 | 206 | 5B ; pop ebx 207 | ) 208 | this._setPatchData( 0x419f4e, 0x41a2bd, 0x400C00, 0x100, pd ) 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /gamedata/gamedataalcostg.ahk: -------------------------------------------------------------------------------- 1 | ; 本作很坑爹的按 [ESP+XX] 来访问局部变量... 2 | ; 而且会修改 EBX/BL 3 | 4 | class THKMC_GameDataAlcostg extends THKMC_GameData10 { 5 | __New( ) { 6 | base.__New() 7 | base._setEXEInfo("alcostg", "黄昏酒场 1.00a", 0x47755c66, 472064) 8 | pd = 9 | (LTrim Comment 10 | 11 | 57 ; PUSH EDI 12 | BF 80000000 ; MOV EDI,80 13 | 14 | 81C4 94000000 ; add esp, 94 ; 后面的地址总共 -0x90。使用 KADD 和 KADDForComment 命令 15 | 16 | 33C9 ; XOR ECX,ECX 17 | 8A4C24 A0 ; MOV CL,BYTE PTR SS:[ESP-60] 18 | 33D2 ; XOR EDX,EDX 19 | 8A5424 9C ; MOV DL,BYTE PTR SS:[ESP-64] 20 | 33C0 ; XOR EAX,EAX 21 | 8A4424 93 ; MOV AL,BYTE PTR SS:[ESP-6D] 22 | 33DB ; XOR EBX,EBX 23 | 8A5C24 48 ; MOV BL,BYTE PTR SS:[ESP+48] 24 | 21f9 ; and ecx,edi 25 | 21fa ; and edx,edi 26 | 21f8 ; and eax,edi 27 | D1E0 ; SHL EAX,1 28 | 0BC1 ; OR EAX,ECX 29 | 33C9 ; XOR ECX,ECX 30 | 8A4C24 99 ; MOV CL,BYTE PTR SS:[ESP-67] 31 | D1E0 ; SHL EAX,1 32 | 0BC2 ; OR EAX,EDX 33 | 33D2 ; XOR EDX,EDX 34 | 8a5424 47 ; MOV DL,BYTE PTR SS:[ESP+47] 35 | D1E0 ; SHL EAX,1 36 | 0BCA ; OR ECX,EDX 37 | 21f9 ; and ecx,edi 38 | 0BC1 ; OR EAX,ECX 39 | 33D2 ; XOR EDX,EDX 40 | 8A5424 90 ; MOV DL,BYTE PTR SS:[ESP-70] 41 | 33C9 ; XOR ECX,ECX 42 | 8A4C24 9F ; MOV CL,BYTE PTR SS:[ESP-61] 43 | D1E0 ; SHL EAX,1 44 | 21fa ; and edx,edi 45 | 21f9 ; and ecx,edi 46 | 0BC1 ; OR EAX,ECX 47 | 33C9 ; XOR ECX,ECX 48 | 8A4C24 9D ; MOV CL,BYTE PTR SS:[ESP-63] 49 | D1E0 ; SHL EAX,1 50 | 0BC2 ; OR EAX,EDX 51 | 33D2 ; XOR EDX,EDX 52 | 8A5424 81 ; MOV DL,BYTE PTR SS:[ESP-7F] 53 | C1E0 07 ; SHL EAX,7 54 | 21f9 ; and ecx,edi 55 | 0BC1 ; OR EAX,ECX 56 | 33C9 ; XOR ECX,ECX 57 | 8A4C24 AD ; MOV CL,BYTE PTR SS:[ESP-53] 58 | 21fa ; and edx,edi 59 | D1E0 ; SHL EAX,1 60 | 0BC2 ; OR EAX,EDX 61 | 0FB65424 AC ; MOVZX EDX,BYTE PTR SS:[ESP-54] 62 | D1EA ; SHR EDX,1 63 | 21f9 ; and ecx,edi 64 | 0BCA ; OR ECX,EDX 65 | 33D2 ; XOR EDX,EDX 66 | 8A5424 AE ; MOV DL,BYTE PTR SS:[ESP-52] 67 | D1E9 ; SHR ECX,1 68 | D1E0 ; SHL EAX,1 69 | 21fa ; and edx,edi 70 | 0BCA ; OR ECX,EDX 71 | 33D2 ; XOR EDX,EDX 72 | 8A5424 AA ; MOV DL,BYTE PTR SS:[ESP-56] 73 | D1E9 ; SHR ECX,1 74 | 21fa ; and edx,edi 75 | 0BCA ; OR ECX,EDX 76 | 33D2 ; XOR EDX,EDX 77 | 8A5424 C8 ; MOV DL,BYTE PTR SS:[ESP-38] 78 | D1E9 ; SHR ECX,1 79 | 0BD3 ; OR EDX,EBX 80 | 21fa ; and edx,edi 81 | 0BCA ; OR ECX,EDX 82 | 33D2 ; XOR EDX,EDX 83 | 8a5424 50 ; MOV DL,BYTE PTR SS:[ESP+50] 84 | 33DB ; XOR EBX,EBX 85 | 8A5C24 D0 ; MOV BL,BYTE PTR SS:[ESP-30] 86 | D1E9 ; SHR ECX,1 87 | 0BD3 ; OR EDX,EBX 88 | 21fa ; and edx,edi 89 | 0BCA ; OR ECX,EDX 90 | 33D2 ; XOR EDX,EDX 91 | 8A5424 CB ; MOV DL,BYTE PTR SS:[ESP-35] 92 | 33DB ; XOR EBX,EBX 93 | 8a5c24 4B ; MOV BL,BYTE PTR SS:[ESP+4B] 94 | D1E9 ; SHR ECX,1 95 | 0BD3 ; OR EDX,EBX 96 | 21fa ; and edx,edi 97 | 0BCA ; OR ECX,EDX 98 | D1E9 ; SHR ECX,1 99 | 0BC1 ; OR EAX,ECX 100 | 33C9 ; XOR ECX,ECX 101 | 8A4C24 CD ; MOV CL,BYTE PTR SS:[ESP-33] 102 | 33D2 ; XOR EDX,EDX 103 | 8a5424 4D ; MOV DL,BYTE PTR SS:[ESP+4D] 104 | 0BCA ; OR ECX,EDX 105 | 8A5424 CF ; MOV DL,BYTE PTR SS:[ESP-31] 106 | 21f9 ; and ecx,edi 107 | 0BC1 ; OR EAX,ECX 108 | 8A4C24 C9 ; MOV CL,BYTE PTR SS:[ESP-37] 109 | 80E1 80 ; AND CL,80 110 | F6D9 ; NEG CL 111 | 1BC9 ; SBB ECX,ECX 112 | 81E1 90000000 ; AND ECX,90 113 | 80E2 80 ; AND DL,80 114 | 0BC1 ; OR EAX,ECX 115 | 8A4C24 C7 ; MOV CL,BYTE PTR SS:[ESP-39] 116 | F6DA ; NEG DL 117 | 1BD2 ; SBB EDX,EDX 118 | 83E2 60 ; AND EDX,60 119 | 0BC2 ; OR EAX,EDX 120 | 8A5424 D1 ; MOV DL,BYTE PTR SS:[ESP-2F] 121 | 122 | 123 | 81EC 94000000 ; sub esp, 94 ; 恢复ESP 124 | 5F ; pop edi 125 | ) 126 | this._setPatchData( 0x446a37, 0x446b8e, 0x400c00, 0x80, pd ) 127 | } 128 | } 129 | 130 | 131 | -------------------------------------------------------------------------------- /readme.mak: -------------------------------------------------------------------------------- 1 | # Make readme.html using mingw32-make 2 | 3 | 4 | SHELL=cmd.exe 5 | 6 | TARGETS=README.html 7 | 8 | RM=del 9 | 10 | all: $(TARGETS) 11 | 12 | README.html: README.md 13 | pandoc -f markdown+multiline_tables+table_captions-implicit_figures -s --toc --toc-depth=4 --email-obfuscation=references -V lang=zh --css=docs/style.css --metadata pagetitle="THKMC Readme" -o $@ $< 14 | 15 | 16 | clean: 17 | -$(RM) $(TARGETS) 18 | 19 | 20 | .SUFFIXES: 21 | 22 | .PHONY: clean 23 | -------------------------------------------------------------------------------- /thkmc.ahk: -------------------------------------------------------------------------------- 1 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 2 | ;;; Touhou Keyboard Mapping Changer 3 | ;;; Written by wz520 4 | ;;; Tested AutoHotkey Version: 1.1.30.01 5 | ;;; Download on Github: https://github.com/wz520/thkmc 6 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 7 | 8 | 9 | #NoEnv 10 | #SingleInstance, force 11 | #Persistent 12 | 13 | #include gamedata.ahk 14 | #include KeyNamesDX.ahk 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | title = THKMC 25 | helpfilecmd = https://wz520.github.io/thkmc/help.html 26 | g_bakfilesuffix = .thkmcbackup ; 备份文件后缀 27 | g_inifilepath = thkmc.ini ; 键位配置文件 28 | g_configfilepath = config.ini ; 程序配置文件 29 | 30 | SetWorkingDir, %A_ScriptDir% 31 | 32 | ShowLog := True 33 | 34 | ; #NoTrayIcon ;; 由于要用 TrayTip 显示一些提示,所以不能禁掉 TrayIcon 35 | ; 不过要把TrayTip菜单给删掉(仅编译版) 36 | if ( A_IsCompiled ) { 37 | Menu, Tray, NoStandard 38 | } 39 | 40 | 41 | 42 | doBackup(inifilepath, exefilepath) { 43 | global ddlBackup 44 | global g_bakfilesuffix 45 | Gui, Submit, NoHide 46 | if ( ddlBackup = 1 || ddlBackup = 2 ) { 47 | backupfilepath=%exefilepath%%g_bakfilesuffix% 48 | if ( ddlBackup = 1 ) { 49 | ; 1 = 如果备份文件已存在,则不进行备份 50 | IfExist, %backupfilepath% 51 | { 52 | AddLog("【备份】备份文件已存在,跳过") 53 | backupfilepath= 54 | } 55 | } 56 | 57 | if ( backupfilepath != "" ) { 58 | FileCopy, %exefilepath%, %backupfilepath%, 1 59 | AddLog("【备份】成功!") 60 | } 61 | } 62 | } 63 | 64 | doProcess(patch_processor, filename, bTestMode) { 65 | if ( patch_processor.isEXEThisGame() ) { 66 | if ( bTestMode ) { 67 | AddLog("【提示】“测试模式”已开启。EXE文件不会被修改,备份文件也不会被创建。") 68 | } 69 | AddLog("【目标文件】" . filename) 70 | AddLog("【所属游戏】" . patch_processor._d.gamenameCN) 71 | 72 | patch_processor.doPatch() 73 | patch_processor.updateEXECache() 74 | 75 | if ( bTestMode ) { 76 | return True ; 如果勾选“测试模式”,则在此直接返回 77 | } 78 | else { 79 | doBackup(patch_processor._d.inifilepath, filename) 80 | fileobj := FileOpen(filename, "w") 81 | if ( isObject(fileobj) ) { 82 | ; 打开成功 83 | filesize := patch_processor._exesize 84 | if ( fileobj.RawWrite(patch_processor._pEXEData, filesize) = filesize ) { 85 | fileobj.Close() 86 | return True 87 | } 88 | else 89 | throw Exception("写入文件失败", filename) 90 | } 91 | else 92 | throw Exception("以写入方式打开文件失败", filename) 93 | } 94 | 95 | } 96 | 97 | return False 98 | } 99 | 100 | 101 | 102 | ; throw exception on error 103 | ; outvar: exedata and filesize. 104 | ; if file not found, filesize will be set to -1, and exedata will be 105 | ; set to an empty string 106 | readEXEData(filename, ByRef exedata, ByRef filesize) { 107 | filesize := -1 108 | exedata = 109 | ; NOTE: 110 | ; When inside a try-block, FileOpen() will throw an exception on error. 111 | ; To throw our customized error when the file cannot be opened, we need 112 | ; to add one more try-block here to prevent the exeuction flow from 113 | ; jumping to the outer catch-block. 114 | fileobj = 115 | try { 116 | fileobj := FileOpen(filename, "r") 117 | } 118 | catch { 119 | ; nothing 120 | } 121 | if ( !isObject(fileobj) ) { 122 | ; NOTE: 123 | ; If we don't put a try-block above, and this function is being 124 | ; executed inside a try-block, this line would be NEVER executed. 125 | throw Exception("以读取方式打开文件失败或者文件不存在", filename) 126 | } 127 | 128 | filesize := fileobj.Length 129 | 130 | ;; 目前支持的游戏中还没有这么大的EXE 131 | if ( filesize > 1048576 ) { 132 | throw Exception("这文件太大我不敢看…… > _ <", filename) 133 | } 134 | 135 | ;; FileOpen() 碰到带 BOM 的 UTF-8 或 UTF-16 的文本文件会自动跳过 BOM, 136 | ;; 这会导致下面抛出“读取文件失败”异常 137 | ;; 所以要先手动定位到文件起始处 138 | fileobj.Seek(0) 139 | 140 | ; 分配内存用于存储 EXE 的二进制数据 141 | VarSetCapacity(exedata, filesize, 0) 142 | 143 | ; 读取 144 | if ( fileobj.RawRead( exedata, filesize ) != filesize ) { 145 | throw Exception("读取文件失败", filename) 146 | } 147 | 148 | fileobj.Close() 149 | } 150 | 151 | 152 | askInputFilename() { 153 | Gui, +OwnDialogs 154 | FileSelectFile, files, M3, %A_ScriptDir%, 请选择一个东方 STG 的主程序文件, 应用程序文件 (*.exe) 155 | if ( errorlevel != 0 || files = "" ) { 156 | ; throw Exception("由于你没有选择文件,于是我不知道该修改啥……", "*") 157 | Exit 158 | } 159 | 160 | dir= 161 | filepaths := [] 162 | Loop, parse, files, `n 163 | { 164 | if (A_Index = 1) 165 | dir := A_LoopField 166 | else 167 | filepaths.Push(dir . "\" . A_LoopField) 168 | } 169 | 170 | return filepaths 171 | } 172 | 173 | ; filename 不得为空,否则扔异常 174 | ; 本函数无返回值。若有任何错误,则扔异常 175 | ; NOTE: It's not recommended to call this function directly. Use tryDoWork() instead. 176 | _doWork(filename) { 177 | if ( filename="" ) { 178 | throw Exception("内部错误ERR233:没有文件名") 179 | } 180 | readEXEData( filename, exedata, filesize ) 181 | 182 | succeeded := False 183 | global THKMC_GameIDList, chkTestMode 184 | Gui, 1:Default 185 | Gui, Submit, NoHide 186 | for k, id in THKMC_GameIDList { 187 | gamedata := new THKMC_GameData%id%() 188 | patch_processor := new THKMC_PatchProcessor(&exedata, filesize, gamedata) 189 | if ( doProcess(patch_processor, filename, chkTestMode) ) { 190 | AddLog("`r`n修改成功!`r`n") 191 | succeeded := True 192 | break 193 | } 194 | } 195 | 196 | if ( !succeeded ) { 197 | wocao := [] 198 | wocao.Push("此乃何物?吾不识也。") 199 | wocao.Push("无法识别的文件。") 200 | wocao.Push("啊哈哈,琪露诺看不懂。") 201 | wocao.Push("对不起,我的智商刚刚下线,这是啥我看不懂……") 202 | throw Exception(array_choose(wocao), filename) 203 | } 204 | } 205 | 206 | ; e is an Exception object 207 | addErrorObjectToLog(e, bShow:=False) { 208 | msg := "【错误】" e.message 209 | if ( e.what ) { 210 | msg .= "`r`n【相关文件】" e.what 211 | } 212 | AddLog(msg, True) 213 | 214 | if ( bShow ) { 215 | DoShowLog() 216 | ClearLog() 217 | } 218 | } 219 | 220 | ; filenames 可以接受多个文件(使用数组) 221 | tryDoWork(filenames="") { 222 | global HasError 223 | global chkAlwaysAddToList 224 | 225 | filepaths := !IsObject(filenames) 226 | ? [filenames] 227 | : filenames.Count() = 0 228 | ? [""] 229 | : filenames 230 | 231 | if ( filepaths[1] = "" ) { 232 | filepaths := askInputFilename() 233 | } 234 | 235 | Gui, 1:Default 236 | Gui, Submit, NoHide 237 | for k, f in filepaths { 238 | succeeded := False 239 | if ( k > 1 ) { 240 | AddLog("`r`n`r`n####################### 分割线 #######################`r`n`r`n") 241 | } 242 | 243 | try { 244 | _doWork(f) 245 | succeeded := True 246 | } 247 | catch e { 248 | addErrorObjectToLog(e) 249 | } 250 | 251 | if ( chkAlwaysAddToList || succeeded ) { 252 | addFileToList(f, True) 253 | } 254 | } 255 | DoShowLog() 256 | refreshFileList() 257 | 258 | ; DEBUG 259 | if ( 0 ) { 260 | ExitApp, HasError ? 1 : 0 261 | } 262 | else { 263 | ; 清除log 264 | return ClearLog() 265 | } 266 | } 267 | 268 | 269 | 270 | 271 | ;; ------------ 272 | ;; GUI 相关 273 | ;; ------------ 274 | 275 | assertLVHasSelection() { 276 | Gui, +OwnDialogs 277 | for c, r in LVRow { 278 | } 279 | if ( c <= 0 ) { 280 | global title 281 | MsgBox, 0x40, %title%, 没有高亮的项目 poi 282 | Exit 283 | } 284 | } 285 | 286 | 287 | ; atTop: 添加到最上面 288 | ; return a value returned by LV_Add() or LV_InsertIfNotExist() 289 | addFileToList(filename, atTop:=False) { 290 | global g_ImageListID, g_bakfilesuffix 291 | backupfilepath=%filename%%g_bakfilesuffix% 292 | backupfileexists := FileExist(backupfilepath) ? "√" : "×" 293 | 294 | ; 获取文件属于哪个游戏 295 | gamenameCN := "未知" 296 | 297 | e = ; clear error object 298 | try { 299 | readEXEData(filename, exedata, filesize) 300 | global THKMC_GameIDList 301 | for k, id in THKMC_GameIDList { 302 | gamedata := new THKMC_GameData%id%() 303 | patch_processor := new THKMC_PatchProcessor(&exedata, filesize, gamedata) 304 | if ( patch_processor.isEXEThisGame() ) { 305 | gamenameCN := gamedata.gamenameCN 306 | break 307 | } 308 | } 309 | } 310 | catch e { 311 | gamenameCN := Format("【{:s}】", e.Message) 312 | } 313 | 314 | ; 图标 315 | if ( isObject(e) ) { ; there was an error 316 | iconID := 9999 317 | } 318 | else { 319 | iconID := IL_Add(g_ImageListID, filename, 1) 320 | } 321 | 322 | ; 其他文件属性 323 | try { 324 | FileGetTime, filetime, %filename% 325 | FormatTime, filetimestring, %filetime% R T0 326 | } 327 | catch e { 328 | filetimestring = 无可奉告 329 | } 330 | try { 331 | FileGetAttrib, fileattrib, %filename% 332 | } 333 | catch e { 334 | fileattrib = 布吉岛 335 | } 336 | filesize := DecimalMark(filesize, 0) 337 | 338 | result := atTop 339 | ? LV_InsertIfNotExist(filename, 1, 1, "Icon" . iconID, filename, gamenameCN, filesize, filetimestring, fileattrib, backupfileexists) 340 | : LV_Add("Icon" . iconID, filename, gamenameCN, filesize, filetimestring, fileattrib, backupfileexists) 341 | return result 342 | } 343 | 344 | loadFileList() { 345 | ; 当文件列表很长时可能需要一点时间。所以显示一个提示框 346 | 347 | splash := new WZSplashWindow("Praying" , "正在载入文件列表", "少女祈祷中...") 348 | 349 | splash.Show() 350 | try { 351 | loop, Read, recentfiles.txt 352 | { 353 | l := A_LoopReadLine 354 | ; 忽略空行和包含 *、? 的行 355 | if ( l = "" || RegExMatch(l, "[*?]") ) { 356 | continue 357 | } 358 | 359 | addFileToList(l) 360 | } 361 | } 362 | finally { 363 | LAutoSizeColumn() 364 | splash.Destroy() 365 | } 366 | } 367 | 368 | saveFileList() { 369 | fp := FileOpen("recentfiles.txt", "w`n", "UTF-16") 370 | if ( IsObject(fp) ) { 371 | loop, % LV_GetCount() 372 | { 373 | result := LV_GetText(l, A_Index) 374 | if (result) 375 | fp.WriteLine(l) 376 | } 377 | fp.Close() 378 | } 379 | } 380 | 381 | refreshFileList() { 382 | saveFileList() 383 | LV_Delete() 384 | loadFileList() 385 | } 386 | 387 | 388 | 389 | createINIFile(inifilepath) { 390 | filecontent= 391 | (LTrim 392 | ; 关于本设置文件的完整说明,请参看 thkmc_sample.ini 393 | 394 | [Default] 395 | UP=W 396 | DOWN=S 397 | LEFT=A 398 | RIGHT=D 399 | 400 | Z=j 401 | X=k 402 | C=l 403 | LSHIFT=i 404 | ) 405 | 406 | fileobj := FileOpen(inifilepath, "w`n", "UTF-16") 407 | if ( isObject(fileobj) ) { 408 | if (fileobj.Write(filecontent)) { 409 | fileobj.Close() 410 | } 411 | else 412 | throw Exception("写入文件失败", inifilepath) 413 | } 414 | else 415 | throw Exception("以写入方式打开文件失败", inifilepath) 416 | } 417 | 418 | ensureINIExists() { 419 | global title, g_inifilepath 420 | IfNotExist, %g_inifilepath% 421 | { 422 | createINIFile(g_inifilepath) 423 | Gui, +OwnDialogs 424 | msg=【%g_inifilepath%】文件不存在。`r`n`r`n已自动生成。 425 | MsgBox, 64, %title%, %msg% 426 | } 427 | } 428 | 429 | 430 | confirmApply(filepaths) { 431 | global vCA_OK 432 | IsOK := False 433 | 434 | txtFiles := array_join(filepaths, "`r`n") 435 | 436 | Gui, WndConfirm:New, +LabelCA_ +Owner1 -SysMenu -Resize +LastFound, 确认 - THKMC 437 | 438 | Gui, Color, Fuchsia 439 | 440 | Gui, Add, Edit, w500 r25, 你选择的文件是:`r`n%txtFiles%`r`n`r`n真的要将 INI 文件中的键位映射设置应用到这些程序上吗? 441 | Gui, Add, Button, gCA_OK Default w150 vvCA_OK, 是(&Y) 442 | Gui, Add, Button, gCA_Close x+20 w150 , 否(&H) 443 | GuiControl, Focus, vCA_OK 444 | Gui, Show, Center AutoSize 445 | Gui, 1:+Disabled 446 | 447 | WinWaitClose 448 | 449 | return IsOK 450 | 451 | CA_OK: 452 | IsOK := True 453 | ; no return 454 | CA_Close: 455 | CA_Cancel: 456 | Gui, 1:-Disabled 457 | Gui, WndConfirm:Destroy 458 | return 459 | } 460 | 461 | 462 | LOpenINI() { 463 | global g_inifilepath 464 | ensureINIExists() 465 | Run, %g_inifilepath% 466 | } 467 | 468 | LOpenAndApply() { 469 | assertLVHasSelection() 470 | 471 | filepaths := [] 472 | for c, row in LVRow { 473 | LV_GetText(filename, row) 474 | filepaths.Push(filename) 475 | } 476 | 477 | if (confirmApply(filepaths)) { 478 | tryDoWork(filepaths) 479 | } 480 | } 481 | 482 | 483 | ; ListView 按键处理 484 | LFileLV() { 485 | if ( A_GuiEvent = "DoubleClick" ) { 486 | LRunEXE() 487 | } 488 | else if ( A_GuiEvent = "K" ) { 489 | key := GetKeyName(Format("vk{:x}", A_EventInfo)) 490 | ShortcutkeyToFuncMap := {} 491 | if ( GetKeyState("Ctrl") && !GetKeyState("Alt") ) { 492 | ShortcutkeyToFuncMap["G"] := Func("LOpenAndApply") 493 | ShortcutkeyToFuncMap["D"] := Func("LOpenFolder") 494 | ShortcutkeyToFuncMap["B"] := Func("LRestoreFromBackup") 495 | ShortcutkeyToFuncMap["V"] := Func("LRunVPatch") 496 | ShortcutkeyToFuncMap["C"] := Func("LRunCustom") 497 | ShortcutkeyToFuncMap["Z"] := Func("LRunNyasama") 498 | ShortcutkeyToFuncMap["A"] := Func("LSelectAll") 499 | ShortcutkeyToFuncMap["S"] := Func("LOpenShanghaiAlice") 500 | } 501 | else if ( GetKeyState("Alt") && !GetKeyState("Ctrl") ) { 502 | ShortcutkeyToFuncMap["Enter"] := Func("LOpenFileProperties") 503 | } 504 | else { 505 | ShortcutkeyToFuncMap["NumpadDel"] := Func("LRemove") 506 | ShortcutkeyToFuncMap["F5"] := Func("refreshFileList") 507 | } 508 | if ( ShortcutkeyToFuncMap.HasKey(key) ) { 509 | ShortcutkeyToFuncMap[key].Call() 510 | } 511 | } 512 | } 513 | 514 | LOpenEXE() { 515 | tryDoWork() 516 | } 517 | 518 | LRunEXE() { 519 | assertLVHasSelection() 520 | for c, row in LVRow { 521 | LV_GetText(filename, row) 522 | CriticalRun(filename, "CMDDIR") 523 | } 524 | } 525 | 526 | RunOtherFileInEXEDir(newfilename) { 527 | assertLVHasSelection() 528 | for c, row in LVRow { 529 | LV_GetText(filename, row) 530 | SplitPath, filename, , dir 531 | filename=%dir%\%newfilename% 532 | CriticalRun(filename, "CMDDIR") 533 | } 534 | } 535 | 536 | LRunVPatch() { 537 | RunOtherFileInEXEDir("vpatch.exe") 538 | } 539 | LRunCustom() { 540 | RunOtherFileInEXEDir("custom.exe") 541 | } 542 | 543 | LRunNyasama() { 544 | assertLVHasSelection() 545 | for c, row in LVRow { 546 | LV_GetText(filename, row) 547 | SplitPath, filename, , dir, ext, namenoext 548 | filename=%dir%\%namenoext%c.%ext% 549 | CriticalRun(filename, "CMDDIR") 550 | } 551 | } 552 | 553 | LSelectAll() { 554 | LV_Modify(0, "Select") 555 | } 556 | 557 | ; 返回错误信息,若有错误则扔异常 558 | doRestoreFromBackup( filename, bKeep ) 559 | { 560 | global g_bakfilesuffix 561 | bakfilename = %filename%%g_bakfilesuffix% 562 | if ( !FileExist(bakfilename) ) { 563 | throw Exception("备份文件不存在", filename) 564 | } 565 | if ( bKeep ) { 566 | FileCopy, %bakfilename%, %filename%, 1 567 | } 568 | else { 569 | FileMove, %bakfilename%, %filename%, 1 570 | } 571 | } 572 | 573 | LRestoreFromBackup() { 574 | assertLVHasSelection() 575 | 576 | global title, g_bakfilesuffix 577 | MsgBox, 0x33, %title%, 578 | (LTrim 579 | 恢复后是否要保留备份文件(*%g_bakfilesuffix%)? 580 | 581 | 【是】 :恢复并保留备份文件 582 | 【否】 :恢复并删除备份文件 583 | 【取消】:不做任何事情 584 | ) 585 | 586 | bKeep := True 587 | IfMsgBox, Cancel 588 | { 589 | return 590 | } 591 | else IfMsgBox, No 592 | { 593 | bKeep := False 594 | } 595 | 596 | success_count := 0 597 | for c, row in LVRow { 598 | LV_GetText(filename, row) 599 | try { 600 | doRestoreFromBackup(filename, bKeep) 601 | success_count++ 602 | } 603 | catch e { 604 | e.what := filename 605 | addErrorObjectToLog(e, True) 606 | } 607 | } 608 | 609 | refreshFileList() 610 | TrayTip, %title%, 成功恢复了 %success_count% 个文件。 611 | } 612 | 613 | LDeleteBackup() { 614 | assertLVHasSelection() 615 | 616 | global title, g_bakfilesuffix 617 | MsgBox, 0x134, %title%, 真的要【从磁盘上】删除所有选中项目的备份文件(*%g_bakfilesuffix%)吗? 618 | 619 | IfMsgBox, No 620 | { 621 | return 622 | } 623 | 624 | success_count := 0 625 | for c, row in LVRow { 626 | LV_GetText(filename, row) 627 | try { 628 | bakfile =%filename%%g_bakfilesuffix% 629 | IfExist, %bakfile% 630 | { 631 | FileDelete, %bakfile% 632 | success_count++ 633 | } 634 | } 635 | catch e { 636 | ; ignore errors 637 | } 638 | } 639 | 640 | refreshFileList() 641 | TrayTip, %title%, 成功删除了 %success_count% 个备份文件。 642 | } 643 | 644 | ; return a list 645 | LVGetAllSelection() { 646 | files := [] 647 | for c, row in LVRow { 648 | LV_GetText(filename, row) 649 | files.Push(filename) 650 | } 651 | return files 652 | } 653 | 654 | 655 | LCopyFullPath() { 656 | assertLVHasSelection() 657 | files := LVGetAllSelection() 658 | c := files.MaxIndex() 659 | if ( c ) { 660 | global title 661 | Clipboard := array_join(files) 662 | TrayTip, %title%, 复制%c%个文件的路径到剪贴板成功! 663 | } 664 | } 665 | 666 | LCopyOrCutFile(itemname) { 667 | assertLVHasSelection() 668 | bCopy := InStr(itemname, "复制") ? True : False 669 | files := LVGetAllSelection() 670 | c := files.MaxIndex() 671 | if ( c ) { 672 | global title 673 | verb := bCopy ? "复制" : "剪切" 674 | CopyFilesToClipboard(files, bCopy) 675 | TrayTip, %title%, %verb%%c%个文件到剪贴板成功! 676 | } 677 | } 678 | 679 | LOpenFolder() { 680 | assertLVHasSelection() 681 | for c, row in LVRow { 682 | LV_GetText(filename, row) 683 | 684 | ; NOTE: Run 貌似没用,换成 DllCall() 685 | ; Run, *open explorer.exe /select,%filename% 686 | ; DllCall("shell32.dll\ShellExecute", "ptr", 0, "str", "open", "str", "Explorer.exe", "str", " /select," . filename, "ptr", 0, "int", 10, "int") 687 | 688 | ; NOTE: 不不不,其实 Run 还是有用的,只是我参数没写对。>_< 689 | Run, *open explorer.exe /select`,%filename%, , UseErrorLevel 690 | } 691 | } 692 | 693 | LOpenShanghaiAlice() { 694 | cmd = *open %A_AppData%\ShanghaiAlice 695 | CriticalRun(cmd) 696 | } 697 | 698 | LOpenFileProperties() { 699 | assertLVHasSelection() 700 | for c, row in LVRow { 701 | LV_GetText(filename, row) 702 | 703 | Run, properties %filename%, , UseErrorLevel 704 | if ( ErrorLevel = "ERROR" ) { 705 | global title 706 | TrayTip, %title% - 打开文件属性失败, 文件:%filename%, ,3 707 | } 708 | } 709 | } 710 | 711 | 712 | ;; Load/Save Config 713 | 714 | loadConfig(key, default) { 715 | global g_configfilepath, g_inifilepath 716 | IniRead, chkstate, %g_configfilepath%, thkmc, %key% 717 | 718 | if ( chkstate = "ERROR" ) { 719 | IniRead, chkstate, %g_inifilepath%, thkmc, %key%, %default% 720 | } 721 | 722 | return chkstate 723 | } 724 | saveConfig() { 725 | global ddlBackup, chkTestMode, chkAlwaysAddToList, g_configfilepath 726 | Gui, Submit, NoHide 727 | 728 | IniWrite, %ddlBackup%, %g_configfilepath%, thkmc, DoBackup 729 | IniWrite, %chkTestMode%, %g_configfilepath%, thkmc, TestMode 730 | IniWrite, %chkAlwaysAddToList%, %g_configfilepath%, thkmc, AlwaysAddToList 731 | } 732 | 733 | 734 | 735 | ; return the count of the deleted items 736 | LRemove() { 737 | assertLVHasSelection() 738 | global title 739 | delcount := 0 740 | MsgBox, 0x34, %title%, 真的要清除列表中所有已选中的项目吗? 741 | IfMsgBox, Yes 742 | { 743 | rownum := 0 744 | Loop { 745 | rownum := LV_GetNext(rownum) 746 | if ( rownum ) { 747 | LV_Delete(rownum--) 748 | delcount++ 749 | } else 750 | break 751 | } 752 | } 753 | return delcount 754 | } 755 | 756 | LRemoveAll() { 757 | global title 758 | Gui, +OwnDialogs 759 | MsgBox, 0x34, %title%, 真的要清除列表中的所有项目吗? 760 | IfMsgBox, Yes 761 | { 762 | LV_Delete() 763 | } 764 | } 765 | 766 | GuiSize(GuiHwnd, EventInfo, Width, Height) { 767 | if ( EventInfo != 1 ) { 768 | neww := Width - 20 769 | newh := Height - 120 770 | GuiControl, Move, vFileLV, w%neww% h%newh% 771 | } 772 | } 773 | 774 | ; 拖拽处理 775 | GuiDropFiles(GuiHwnd, FileArray, CtrlHwnd, X, Y) { 776 | _files := [] 777 | 778 | splash= 779 | for k, f in FileArray { 780 | attr := FileExist(f) 781 | if ( InStr(attr, "D") ) { 782 | if ( !IsObject(splash) ) { 783 | splash := new WZSplashWindow("SearchingEXE" , "正在搜索 .exe 文件", "少女祈祷中...") 784 | splash.Show() 785 | } 786 | 787 | loop, Files, %f%\*.exe, R 788 | { 789 | _files.Push(A_LoopFileFullPath) 790 | } 791 | } 792 | else { 793 | _files.Push(f) 794 | } 795 | } 796 | if ( IsObject(splash) ) { 797 | splash.Destroy() 798 | } 799 | 800 | if (confirmApply(_files)) { 801 | tryDoWork(_files) 802 | } 803 | } 804 | 805 | GuiContextMenu(GuiHwnd, CtrlHwnd, EventInfo, IsRightClick, X, Y) { 806 | if (A_GuiControl = "vFileLV") { 807 | Menu, mnuLV, Show, %A_GuiX%, %A_GuiY% 808 | } 809 | } 810 | 811 | 812 | 813 | ; ------------------------ 814 | ; -- ENTRY 815 | ; ------------------------ 816 | 817 | ; 创建 GUI 818 | Gui, +Hwndg_hWnd 819 | Gui, Add, Button, gLOpenINI, 编辑 thkmc.ini(&E) 820 | Gui, Add, Button, gLOpenEXE x+10, 打开程序并应用补丁(&O) 821 | Gui, Add, Button, gLOpenAndApply x+10 hwndhBtnApply, 应用补丁(&A) 822 | Gui, Add, Button, gLRunEXE x+10 hwndhBtnRun +Default, 运行(&R) 823 | Gui, Add, Button, grefreshFileList x+10 y5 hwndhBtnRefresh, 刷新(&E) 824 | Gui, Add, Button, gRunHelpAndExit hwndhBtnHelp x+10 y5, 帮助(&H) 825 | Gui, Add, Custom, ClassSysLink hwndhSysLinkAbout gLSysLinkEvent x+5 w50 r1 y10, 关于... 826 | 827 | Gui, Add, Text, xm+0, 备份到 *%g_bakfilesuffix% ? 828 | cfgvalue := loadConfig("DoBackup", 1) 829 | Gui, Add, DropDownList, AltSubmit vddlBackup x+10 yp-3 w210 Choose%cfgvalue%, 只在备份文件不存在时备份(推荐)|总是备份(若备份已存在则覆盖)|不备份(不推荐) 830 | cfgvalue := loadConfig("TestMode", 0) 831 | Gui, Add, Checkbox, x+10 yp-3 h30 vchkTestMode Checked%cfgvalue%, 测试模式(&T) 832 | cfgvalue := loadConfig("AlwaysAddToList", 0) 833 | Gui, Add, Checkbox, x+10 h30 vchkAlwaysAddToList Checked%cfgvalue%, 总是添加至列表(&P) 834 | 835 | Gui, Add, Text, x10, 查看更新: 836 | Gui, Add, Custom, ClassSysLink hwndhSysLink gLSysLinkEvent x+5 w40 r1, 喵玉殿 837 | Gui, Add, Custom, ClassSysLink hwndhSysLinkGitHub gLSysLinkEvent x+5 w40 r1, GitHub 838 | g_updatelink = http://bbs.nyasama.com/forum.php?mod=viewthread&tid=78904 839 | g_githublink = https://github.com/wz520/thkmc 840 | 841 | Gui, Add, Text, xm CBlue, ※最近打开的文件(双击可运行程序,右击有菜单): 842 | Gui, Add, ListView, vvFileLV xm w740 h400 gLFileLV AltSubmit LV0x4000, 文件|游戏版本|大小|最后修改时间|属性|存在备份 ; LVS_EX_LABELTIP:=0x4000 843 | LV_ModifyCol(3, "Integer") ; For sorting purposes, indicate that column 2 is an integer. 844 | g_ImageListID := IL_Create(10) 845 | LV_SetImageList(g_ImageListID) 846 | Gui, +Resize +OwnDialogs 847 | 848 | ; GUI ToolTip 849 | GuiControlGet, hCtrl, Hwnd, chkTestMode 850 | AttachTipToControl(hCtrl, "模拟打补丁的过程:会显示修改结果,但不会创建/修改EXE文件和备份文件。`r`n【提示】若打补丁成功,仍会将文件路径添加至文件列表。") 851 | GuiControlGet, hCtrl, Hwnd, chkAlwaysAddToList 852 | AttachTipToControl(hCtrl, "即使打补丁失败也会将文件路径添加至文件列表。`r`n若不勾选此项, THKMC 只会在打补丁成功后才将文件路径添加至文件列表。") 853 | AttachTipToControl(hBtnApply, "将 .INI 中的映射配置重新应用到选中的程序上") 854 | AttachTipToControl(hBtnRun, "运行选中的游戏程序") 855 | AttachTipToControl(hBtnRefresh, "刷新“最近打开的文件”列表") 856 | AttachTipToControl(hBtnHelp, helpfilecmd) 857 | AttachTipToControl(hSysLink, g_updatelink) 858 | AttachTipToControl(hSysLinkGitHub, g_githublink) 859 | 860 | Gui, Show, Center, %title% 861 | GuiControl, Focus, vFileLV 862 | 863 | ; 快捷菜单 864 | Menu, mnuLV, Add, 应用改键补丁`tCtrl+G, LOpenAndApply 865 | Menu, mnuLV, Add 866 | Menu, mnuLV, Add, 运行该程序`tEnter, LRunEXE 867 | Menu, mnuLV, Default, 运行该程序`tEnter 868 | Menu, mnuLV, Add, 运行所在目录下的 vpatch.exe`tCtrl+V, LRunVPatch 869 | Menu, mnuLV, Add, 运行所在目录下的 custom.exe`tCtrl+C, LRunCustom 870 | Menu, mnuLV, Add, 运行所在目录下的汉化版游戏程序(th???c.exe)`tCtrl+Z, LRunNyasama 871 | Menu, mnuLV, Add, 打开所在目录`tCtrl+D, LOpenFolder 872 | Menu, mnuLV, Add, 打开 ShanghaiAlice 目录`tCtrl+S, LOpenShanghaiAlice 873 | Menu, mnuLV, Add 874 | Menu, mnuLV, Add, 属性...`tAlt+Enter, LOpenFileProperties 875 | Menu, mnuLV, Add 876 | Menu, mnuLV, Add, 从备份文件恢复`tCtrl+B, LRestoreFromBackup 877 | Menu, mnuLV, Add, 删除备份文件, LDeleteBackup 878 | Menu, mnuLV, Add 879 | Menu, mnuLV, Add, 复制完整路径, LCopyFullPath 880 | Menu, mnuLV, Add, 剪切, LCopyOrCutFile 881 | Menu, mnuLV, Add, 复制, LCopyOrCutFile 882 | Menu, mnuLV, Add, 全选`tCtrl+A, LSelectAll 883 | Menu, mnuLV, Add 884 | Menu, mnuLV, Add, 移除高亮的项目`tDelete, LRemove 885 | Menu, mnuLV, Add, 移除所有项目, LRemoveAll 886 | Menu, mnuLV, Add 887 | Menu, mnuLV, Add, 自动调整表格宽度, LAutoSizeColumn 888 | Menu, mnuLV, Add, 刷新`tF5, refreshFileList 889 | Menu, mnuLV, Add 890 | Menu, mnuLV, Add, 关于..., LAbout 891 | 892 | OnExit("LOnExit") 893 | 894 | loadFileList() 895 | 896 | return 897 | 898 | LOnExit(ExitReason, ExitCode) { 899 | saveFileList() 900 | saveConfig() 901 | } 902 | 903 | GuiClose: 904 | GuiEscape: 905 | ExitApp 906 | 907 | lSysLinkEvent: 908 | if ( A_GuiEvent = "N" ) { 909 | code := NumGet(A_EventInfo, A_PtrSize+4, "Int") ; NMHDR->hwndFrom 910 | if ( code = -2 || code = -4 ) { ; NM_CLICK = -2, NM_RETURN = -4 911 | hwndFrom := NumGet(A_EventInfo, 0, "UPtr") ; NMHDR->hwndFrom 912 | if ( hwndFrom = hSysLink ) { 913 | Run, %g_updatelink% 914 | } 915 | else if (hwndFrom = hSysLinkGitHub) { 916 | Run, %g_githublink% 917 | } 918 | else if (hwndFrom = hSysLinkAbout) { 919 | LAbout() 920 | } 921 | } 922 | } 923 | return 924 | 925 | GetAHKVersionString() { 926 | strIsUnicode := A_IsUnicode ? "Unicode" : "ANSI" 927 | strIs64bit := A_PtrSize = 8 ? "64位" : "32位" 928 | result=%A_AhkVersion% %strIsUnicode% %strIs64bit% 929 | return result 930 | } 931 | 932 | LAbout() { 933 | global title 934 | Gui, +OwnDialogs 935 | 936 | gamelist := THKMC_GetGameNameList() 937 | ahkver := GetAHKVersionString() 938 | 939 | MsgBox, 64, %title%, 940 | (LTrim 941 | THKMC - 东方STG专用键盘键位映射修改工具 1.40 942 | 943 | AutoHotkey 版本:%ahkver% 944 | 945 | Written by wz520 946 | 百度贴吧ID:天使的枷锁 947 | 948 | 支持的游戏: 949 | %gamelist% 950 | ) 951 | } 952 | 953 | LAutoSizeColumn() { 954 | LVAutoSizeHeader("vFileLV") 955 | } 956 | 957 | -------------------------------------------------------------------------------- /thkmc.ahk.ini: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wz520/thkmc/caf7020d652babf3293273c7d40119a8fd79ac74/thkmc.ahk.ini -------------------------------------------------------------------------------- /thkmc.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wz520/thkmc/caf7020d652babf3293273c7d40119a8fd79ac74/thkmc.ico -------------------------------------------------------------------------------- /thkmc_sample.ini: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wz520/thkmc/caf7020d652babf3293273c7d40119a8fd79ac74/thkmc_sample.ini --------------------------------------------------------------------------------