├── .gitignore ├── LICENSE ├── README.md ├── main.c └── main.lua /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Python1320 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # valvecmd 2 | Execute commands on Source Engine textmode windows (and otherwise). 3 | 4 | Uses Valve-provided interfaces meant for hammer interop. Should therefore be fully VAC-compatible. 5 | 6 | ![bQqmAuQ 1](https://user-images.githubusercontent.com/207340/125543878-5b40c556-6816-4091-b45c-dab7377c43cb.png) 7 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char *argv[]) { 7 | if (argc<2) { 8 | fprintf( stderr, "Usage: %s \"say hello world\"",argv[0]); 9 | return 3; 10 | } 11 | HWND wnd = FindWindow("Valve001", NULL); 12 | if (wnd) { 13 | LPCTSTR cmd = argv[1]; 14 | COPYDATASTRUCT cds; 15 | cds.dwData = 0; 16 | cds.cbData = (strlen(cmd) + 1); 17 | cds.lpData = (void*)cmd; 18 | return SendMessageA(wnd, WM_COPYDATA, (WPARAM)wnd, (LPARAM)(LPVOID)&cds)!=1; 19 | } 20 | return 2; 21 | } 22 | -------------------------------------------------------------------------------- /main.lua: -------------------------------------------------------------------------------- 1 | local ffi = require("ffi") 2 | require("win32.sdkddkver") 3 | require("win32.errhandlingapi") 4 | require("win32.wingdi") 5 | require("win32.winuser") 6 | require("win32.winerror") 7 | local C = ffi.C 8 | local user32 = ffi.load"user32" 9 | ffi.cdef("size_t strlen(const char *);") 10 | 11 | function SendValve001Command(msg) 12 | local Valve001 = user32.FindWindowA("Valve001", nil) 13 | local Valve001Num = tonumber(ffi.cast("intptr_t", Valve001)) 14 | if Valve001Num == 0 then return nil, 'Valve001 not found' end 15 | 16 | local copyData = ffi.new("COPYDATASTRUCT[1]", { 17 | { 18 | dwData = 0x0, 19 | cbData = C.strlen(msg) + 1, 20 | lpData = ffi.cast("void*", msg) 21 | } 22 | }) 23 | 24 | local ok = user32.SendMessageA(Valve001, C.WM_COPYDATA, ffi.cast("WPARAM", Valve001), ffi.cast("LPARAM", ffi.cast("LPVOID", copyData))) 25 | if ok == 1 then return true end 26 | 27 | return nil, ok == 0 and "sendmessage failed" or ok 28 | end 29 | 30 | local cmd = ... 31 | 32 | return SendValve001Command(cmd) 33 | --------------------------------------------------------------------------------