├── .gitignore ├── common ├── src │ ├── lib.rs │ ├── internal.rs │ └── external.rs └── Cargo.toml ├── .github ├── FUNDING.yml └── workflows │ ├── dev.yml │ └── rust.yml ├── assets ├── cover.png ├── kiwami.ico ├── kiwami2.ico ├── yakuza0.ico └── likeadragon.ico ├── .gitmodules ├── Cargo.toml ├── likeadragon ├── injector │ ├── build.rs │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── lib │ ├── build.rs │ ├── notes.md │ ├── Cargo.toml │ └── src │ │ ├── globals.rs │ │ ├── interceptor.asm │ │ └── lib.rs └── notes.md ├── kiwami ├── build.rs ├── Cargo.toml └── src │ ├── interceptor.asm │ └── main.rs ├── kiwami2 ├── build.rs ├── Cargo.toml ├── notes.md └── src │ ├── interceptor.asm │ └── main.rs ├── yakuza0 ├── build.rs ├── Cargo.toml └── src │ ├── interceptor.asm │ └── main.rs ├── LICENSE ├── README.md └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | releases 3 | */*.vi 4 | -------------------------------------------------------------------------------- /common/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod external; 2 | pub mod internal; 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | ko_fi: etra0 3 | -------------------------------------------------------------------------------- /assets/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etra0/yakuza-freecam/HEAD/assets/cover.png -------------------------------------------------------------------------------- /assets/kiwami.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etra0/yakuza-freecam/HEAD/assets/kiwami.ico -------------------------------------------------------------------------------- /assets/kiwami2.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etra0/yakuza-freecam/HEAD/assets/kiwami2.ico -------------------------------------------------------------------------------- /assets/yakuza0.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etra0/yakuza-freecam/HEAD/assets/yakuza0.ico -------------------------------------------------------------------------------- /assets/likeadragon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etra0/yakuza-freecam/HEAD/assets/likeadragon.ico -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "memory-rs"] 2 | path = memory-rs 3 | url = https://github.com/etra0/memory-rs.git 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "common", 4 | "yakuza0", 5 | "kiwami", 6 | "kiwami2", 7 | "likeadragon/lib", 8 | "likeadragon/injector" 9 | ] 10 | -------------------------------------------------------------------------------- /likeadragon/injector/build.rs: -------------------------------------------------------------------------------- 1 | extern crate winres; 2 | 3 | fn main() { 4 | let mut res = winres::WindowsResource::new(); 5 | 6 | res.set_icon("../../assets/likeadragon.ico") 7 | .set("InternalName", "likeadragon-freecam"); 8 | 9 | res.compile().unwrap(); 10 | } 11 | -------------------------------------------------------------------------------- /kiwami/build.rs: -------------------------------------------------------------------------------- 1 | extern crate winres; 2 | 3 | fn main() { 4 | let mut res = winres::WindowsResource::new(); 5 | 6 | res.set_icon("../assets/kiwami.ico"); 7 | 8 | cc::Build::new() 9 | .file("src/interceptor.asm") 10 | .compile("interceptor"); 11 | println!("cargo:rerun-if-changed=interceptor.asm"); 12 | 13 | res.compile().unwrap(); 14 | } 15 | -------------------------------------------------------------------------------- /kiwami2/build.rs: -------------------------------------------------------------------------------- 1 | extern crate winres; 2 | 3 | fn main() { 4 | let mut res = winres::WindowsResource::new(); 5 | 6 | res.set_icon("../assets/kiwami2.ico"); 7 | 8 | cc::Build::new() 9 | .file("src/interceptor.asm") 10 | .compile("interceptor"); 11 | println!("cargo:rerun-if-changed=interceptor.asm"); 12 | 13 | res.compile().unwrap(); 14 | } 15 | -------------------------------------------------------------------------------- /yakuza0/build.rs: -------------------------------------------------------------------------------- 1 | extern crate winres; 2 | 3 | fn main() { 4 | let mut res = winres::WindowsResource::new(); 5 | 6 | res.set_icon("../assets/yakuza0.ico"); 7 | 8 | cc::Build::new() 9 | .file("src/interceptor.asm") 10 | .compile("interceptor"); 11 | println!("cargo:rerun-if-changed=interceptor.asm"); 12 | 13 | res.compile().unwrap(); 14 | } 15 | -------------------------------------------------------------------------------- /likeadragon/lib/build.rs: -------------------------------------------------------------------------------- 1 | extern crate winres; 2 | 3 | fn main() { 4 | let res = winres::WindowsResource::new(); 5 | 6 | println!("cargo:rerun-if-changed=interceptor.asm"); 7 | println!("cargo:rustc-env=CARGO_CFG_TARGET_FEATURE=fxsr,sse,sse2,avx"); 8 | cc::Build::new() 9 | .file("src/interceptor.asm") 10 | .compile("interceptor"); 11 | 12 | res.compile().unwrap(); 13 | } 14 | -------------------------------------------------------------------------------- /common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "common" 3 | version = "1.0.0" 4 | authors = ["Sebastian"] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | memory-rs = { git = "https://github.com/etra0/memory-rs" } 9 | winapi = { version = "0.3.8", features = ["winuser", "impl-default", "xinput", "impl-debug"] } 10 | anyhow = "1.0" 11 | log = { version = "0.4.11" } 12 | nalgebra-glm = "0.3" 13 | 14 | [build-dependencies] 15 | winres = "0.1" 16 | cc = "1.0" 17 | -------------------------------------------------------------------------------- /kiwami/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kiwami" 3 | version = "1.0.0" 4 | authors = ["Sebastian"] 5 | edition = "2018" 6 | build = "build.rs" 7 | 8 | [dependencies] 9 | common = { path = "../common" } 10 | memory-rs = { git = "https://github.com/etra0/memory-rs"} 11 | winapi = { version = "0.3.8", features = ["winuser", "impl-default"] } 12 | 13 | [build-dependencies] 14 | winres = "0.1" 15 | cc = "1.0" 16 | 17 | [[bin]] 18 | name = "kiwami-freecam" 19 | path = "src/main.rs" 20 | -------------------------------------------------------------------------------- /kiwami2/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kiwami2" 3 | version = "1.0.0" 4 | authors = ["Sebastian"] 5 | edition = "2018" 6 | build = "build.rs" 7 | 8 | [dependencies] 9 | common = { path = "../common" } 10 | memory-rs = { git = "https://github.com/etra0/memory-rs"} 11 | winapi = { version = "0.3.8", features = ["winuser", "impl-default"] } 12 | 13 | [build-dependencies] 14 | winres = "0.1" 15 | cc = "1.0" 16 | 17 | [[bin]] 18 | name = "kiwami2-freecam" 19 | path = "src/main.rs" 20 | -------------------------------------------------------------------------------- /yakuza0/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "yakuza0" 3 | version = "1.0.0" 4 | authors = ["Sebastian"] 5 | edition = "2018" 6 | build = "build.rs" 7 | 8 | [dependencies] 9 | common = { path = "../common" } 10 | memory-rs = { git = "https://github.com/etra0/memory-rs"} 11 | winapi = { version = "0.3.8", features = ["winuser", "impl-default"] } 12 | 13 | [build-dependencies] 14 | winres = "0.1" 15 | cc = "1.0" 16 | 17 | [[bin]] 18 | name = "yakuza0-freecam" 19 | path = "src/main.rs" 20 | -------------------------------------------------------------------------------- /likeadragon/lib/notes.md: -------------------------------------------------------------------------------- 1 | UI Draw: 2 | - YakuzaLikeADragon.exe+2829CB0 3 | - YakuzaLikeADragon.exe+2829CA8 4 | 5 | Maybe relevant to pause the world 6 | YakuzaLikeADragon.exe+170D614 - 41 C6 86 C8000000 00 - mov byte ptr [r14+000000C8],00 { 0 } 7 | YakuzaLikeADragon.exe+170D614 - 41 C6 86 C8000000 00 - mov byte ptr [r14+000000C8],00 { 0 } 8 | 9 | 10 | 11 | // Input speed 12 | YakuzaLikeADragon.exe+3E529E4 13 | 14 | // In world speed 15 | YakuzaLikeADragon.exe+3E529E0 16 | YakuzaLikeADragon.exe+3E529DC 17 | 18 | // Cinematic speed 19 | YakuzaLikeADragon.exe+3E529D8 20 | 21 | 22 | -------------------------------------------------------------------------------- /likeadragon/notes.md: -------------------------------------------------------------------------------- 1 | UI Draw: 2 | - YakuzaLikeADragon.exe+2829CB0 3 | - YakuzaLikeADragon.exe+2829CA8 4 | 5 | Maybe relevant to pause the world 6 | YakuzaLikeADragon.exe+170D614 - 41 C6 86 C8000000 00 - mov byte ptr [r14+000000C8],00 { 0 } 7 | YakuzaLikeADragon.exe+170D614 - 41 C6 86 C8000000 00 - mov byte ptr [r14+000000C8],00 { 0 } 8 | 9 | 10 | 11 | // Input speed 12 | YakuzaLikeADragon.exe+3E529E4 13 | 14 | // In world speed 15 | YakuzaLikeADragon.exe+3E529E0 16 | YakuzaLikeADragon.exe+3E529DC 17 | 18 | // Cinematic speed 19 | YakuzaLikeADragon.exe+3E529D8 20 | 21 | 22 | -------------------------------------------------------------------------------- /likeadragon/injector/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "likeadragon-freecam" 3 | version = "0.1.0" 4 | authors = ["Sebastián Aedo "] 5 | edition = "2018" 6 | build = "build.rs" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | # own libs 12 | memory-rs = { git = "https://github.com/etra0/memory-rs"} 13 | # memory-rs = { path = "../../memory-rs" } 14 | simple_injector = { git = "https://github.com/etra0/simple_injector" } 15 | 16 | [build-dependencies] 17 | winres = "0.1" 18 | cc = "1.0" 19 | -------------------------------------------------------------------------------- /kiwami2/notes.md: -------------------------------------------------------------------------------- 1 | Pause func: 2 | YakuzaKiwami2.exe+DF5E1B - 0FB6 87 88010000 - movzx eax,byte ptr [rdi+00000188] 3 | YakuzaKiwami2.exe+DF5E22 - 84 C0 - test al,al 4 | YakuzaKiwami2.exe+DF5E24 - 74 3E - je YakuzaKiwami2.exe+DF5E64 5 | YakuzaKiwami2.exe+DF5E26 - 44 38 B3 32010000 - cmp [rbx+00000132],r14l 6 | YakuzaKiwami2.exe+DF5E2D - 74 06 - je YakuzaKiwami2.exe+DF5E35 7 | YakuzaKiwami2.exe+DF5E2F - 83 7B 68 19 - cmp dword ptr [rbx+68],19 { 8 | 25 } 9 | YakuzaKiwami2.exe+DF5E33 - 75 2F - jne YakuzaKiwami2.exe+DF5E64 10 | YakuzaKiwami2.exe+DF5E35 - 44 38 35 FC09BF01 - cmp 11 | [YakuzaKiwami2.exe+29E6838],r14l { (0) } 12 | 13 | -------------------------------------------------------------------------------- /likeadragon/injector/src/main.rs: -------------------------------------------------------------------------------- 1 | use memory_rs::external::process::Process; 2 | use simple_injector::inject_dll; 3 | use std::env::current_exe; 4 | 5 | fn main() { 6 | println!("Waiting for the process to start"); 7 | let p = loop { 8 | if let Ok(p) = Process::new("YakuzaLikeADragon.exe") { 9 | break p; 10 | } 11 | 12 | std::thread::sleep(std::time::Duration::from_secs(5)); 13 | }; 14 | println!("Game found"); 15 | 16 | let mut path = current_exe().unwrap(); 17 | path.pop(); 18 | let path_string = path.to_string_lossy(); 19 | 20 | let dll_path = format!("{}/likeadragon.dll", path_string).to_string(); 21 | 22 | inject_dll(&p, &dll_path); 23 | } 24 | -------------------------------------------------------------------------------- /likeadragon/lib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "likeadragon" 3 | version = "0.1.0" 4 | authors = ["Sebastián Aedo "] 5 | edition = "2018" 6 | build = "build.rs" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | common = { path = "../../common" } 12 | 13 | # own libs 14 | memory-rs = { git = "https://github.com/etra0/memory-rs"} 15 | # memory-rs = { path = "../../memory-rs" } 16 | simple_injector = { git = "https://github.com/etra0/simple_injector" } 17 | 18 | winapi = { version = "0.3.8", features = ["winuser", "xinput", "wincon", "consoleapi", "minwindef"] } 19 | 20 | # Loggin dependencies 21 | simplelog = "0.8" 22 | log = "0.4.11" 23 | 24 | anyhow = "1.0" 25 | 26 | nalgebra-glm = "0.3" 27 | 28 | [build-dependencies] 29 | winres = "0.1" 30 | cc = "1.0" 31 | 32 | [features] 33 | non_automatic = [] 34 | 35 | [lib] 36 | crate-type = ["cdylib"] 37 | name = "likeadragon" 38 | 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Sebastián A. 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 | -------------------------------------------------------------------------------- /likeadragon/lib/src/globals.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_upper_case_globals)] 2 | 3 | use memory_rs::scoped_no_mangle; 4 | use std::sync::atomic::AtomicUsize; 5 | 6 | scoped_no_mangle! { 7 | // Pointer to the camera struct (the lookat is at +0x80 offset 8 | g_camera_struct: usize = 0; 9 | 10 | // Boolean that says if the camera is active 11 | g_camera_active: u8 = 0x0; 12 | 13 | // Address to jmp back after the injection 14 | g_get_camera_data: usize = 0x0; 15 | g_get_timestop: usize = 0x0; 16 | g_get_timestop_rip: usize = 0x0; 17 | g_get_timestop_first_offset: usize = 0x0; 18 | g_get_controller: usize = 0x0; 19 | 20 | // Global engine speed to be written by the main dll 21 | g_engine_speed: f32 = 1.; 22 | } 23 | 24 | /// This pointer will contain the function that either steam or 25 | /// ms store version uses, since steam overrides the xinput in order 26 | /// to be able to use more controller options. 27 | pub static controller_input_function: AtomicUsize = AtomicUsize::new(0); 28 | 29 | extern "C" { 30 | pub static asm_get_camera_data: u8; 31 | pub static asm_get_timestop: u8; 32 | pub static asm_get_controller: u8; 33 | } 34 | -------------------------------------------------------------------------------- /yakuza0/src/interceptor.asm: -------------------------------------------------------------------------------- 1 | .data 2 | PUBLIC get_camera_data 3 | PUBLIC get_camera_data_end 4 | 5 | PUBLIC get_controller_input 6 | PUBLIC get_controller_input_end 7 | 8 | get_camera_data PROC 9 | push r11 10 | lea r11,[get_camera_data + 200h]; 11 | pushf 12 | push rax 13 | mov eax, [r11-10h] 14 | test eax, eax 15 | pop rax 16 | je not_zero 17 | movaps xmm4,[r11+40h] 18 | movaps xmm5,[r11] 19 | movaps xmm6,[r11+20h] ; 220h 20 | push rbx 21 | mov rbx,[r11+60h] 22 | mov [rax+0ACh],rbx 23 | pop rbx 24 | 25 | not_zero: 26 | movaps [r11],xmm5 27 | movaps [r11+20h],xmm6 28 | movaps [r11+40h],xmm4 ; camera rotation 29 | 30 | ; load fov 31 | push rbx 32 | mov rbx,[rax+0ACh] 33 | mov [r11+60h],rbx 34 | pop rbx 35 | 36 | popf 37 | pop r11 38 | movaps [rsp+48h],xmm4 ; adjusted offset of stack pointer + 8 39 | ret 40 | get_camera_data_end:: 41 | get_camera_data ENDP 42 | 43 | get_controller_input PROC 44 | push rax 45 | lea rax,[get_controller_input+200h] 46 | mov [rax],rbx 47 | pop rax 48 | 49 | ; original code 50 | mov rbp,r9 51 | mov rsi,r8 52 | ret 53 | get_controller_input_end:: 54 | get_controller_input ENDP 55 | 56 | END 57 | -------------------------------------------------------------------------------- /kiwami/src/interceptor.asm: -------------------------------------------------------------------------------- 1 | .data 2 | 3 | PUBLIC get_camera_data 4 | PUBLIC get_camera_data_end 5 | 6 | PUBLIC get_controller_input 7 | PUBLIC get_controller_input_end 8 | 9 | get_camera_data PROC 10 | push r11 11 | lea r11,[get_camera_data + 200h]; 12 | pushf 13 | push rax 14 | mov eax, [r11-010h] 15 | test eax, eax 16 | pop rax 17 | je not_zero 18 | movaps xmm1,[r11] ; focus 19 | movaps xmm0,[r11+020h] ; position 20 | movaps xmm3,[r11+040h] ; rotation ?? 21 | movaps [r9],xmm3 22 | ; fov stuff 23 | push rax 24 | mov rax,[r11+060h] 25 | mov [rbx+0ACh],rax 26 | pop rax 27 | 28 | 29 | not_zero: 30 | movaps [r11],xmm1 31 | movaps [r11+020h],xmm0 32 | ; load rotation 33 | movaps xmm3,[r9] 34 | movaps [r11+040h],xmm3 ; camera rotation 35 | 36 | ; load fov 37 | push rax 38 | mov rax,[rbx+0ACh] 39 | mov [r11+060h],rax 40 | pop rax 41 | 42 | popf 43 | pop r11 44 | ; original code 45 | movaps [rbp-020h],xmm1 46 | movaps [rbp-030h],xmm0 47 | ; end original code 48 | ret 49 | get_camera_data_end:: 50 | get_camera_data ENDP 51 | 52 | get_controller_input PROC 53 | push rax 54 | lea rax,[get_controller_input + 200h] 55 | mov [rax],rbx 56 | pop rax 57 | 58 | ; original code 59 | mov r14,r9 60 | mov rsi,r8 61 | ret 62 | get_controller_input_end:: 63 | get_controller_input ENDP 64 | END 65 | -------------------------------------------------------------------------------- /kiwami2/src/interceptor.asm: -------------------------------------------------------------------------------- 1 | .data 2 | PUBLIC get_camera_data 3 | PUBLIC get_camera_data_end 4 | 5 | PUBLIC get_pause_value 6 | PUBLIC get_pause_value_end 7 | 8 | PUBLIC get_controller_input 9 | PUBLIC get_controller_input_end 10 | 11 | ;; Function that intercepts the values written into the camera 12 | get_camera_data PROC 13 | push r11 14 | lea r11,[get_camera_data+200h]; 15 | pushf 16 | push rax 17 | mov eax, [r11-10h] 18 | test eax, eax 19 | pop rax 20 | je not_zero 21 | movaps xmm4,[r11+40h] ; rotation 22 | movaps xmm10,[r11] ; focus 23 | movaps xmm12,[r11+20h] ; position 24 | ; FOV 25 | push rax 26 | mov rax,[r11+60h] 27 | mov [rdx+58h],rax 28 | pop rax 29 | 30 | not_zero: 31 | movaps [r11],xmm10 32 | movaps [r11+20h],xmm12 33 | movaps [r11+40h],xmm4 ; camera rotation 34 | push rax 35 | mov rax,[rdx+58h] 36 | mov [r11+60h],rax 37 | pop rax 38 | 39 | popf 40 | pop r11 41 | subps xmm10,xmm12 42 | movq xmm0,rax 43 | ret 44 | get_camera_data_end:: 45 | get_camera_data ENDP 46 | 47 | ;; Get the focus-window value, useful to set that to 48 | ;; 0 to force the game to pause itself. 49 | get_pause_value PROC 50 | push rax 51 | push rbx 52 | lea rax,[rdi+188h] 53 | lea rbx,[get_pause_value + 200h] 54 | mov [rbx],rax 55 | pop rbx 56 | pop rax 57 | 58 | ; original code 59 | movzx eax,byte ptr [rdi+188h] 60 | ret 61 | get_pause_value_end:: 62 | get_pause_value ENDP 63 | 64 | ;; Intercept the controller input when controller is detected 65 | get_controller_input PROC 66 | push rax 67 | mov rax,[rsp+10h] 68 | push rbx 69 | lea rbx,[get_controller_input + 200h] 70 | mov [rbx],rax 71 | pop rbx 72 | pop rax 73 | 74 | ; original code 75 | test eax,eax 76 | mov rax,[rsp+108h+8h] ; adjusted stack offset 77 | ret 78 | get_controller_input_end:: 79 | get_controller_input ENDP 80 | 81 | END 82 | -------------------------------------------------------------------------------- /likeadragon/lib/src/interceptor.asm: -------------------------------------------------------------------------------- 1 | .data 2 | EXTERN g_get_camera_data: qword 3 | 4 | EXTERN g_get_timestop: qword 5 | EXTERN g_get_timestop_rip: qword 6 | EXTERN g_get_timestop_first_offset: qword 7 | EXTERN g_get_controller: qword 8 | 9 | EXTERN xinput_interceptor: qword 10 | 11 | EXTERN g_camera_struct: qword 12 | EXTERN g_camera_active: byte 13 | EXTERN g_engine_speed: dword 14 | 15 | ; Function that intercepts the values written into the camera 16 | .code 17 | asm_get_camera_data PROC 18 | pushf 19 | mov al, g_camera_active 20 | 21 | cmp g_camera_active, 0 22 | je original 23 | 24 | cmp g_camera_struct, 0 25 | jne force_ret 26 | mov g_camera_struct, rcx 27 | 28 | force_ret: 29 | popf 30 | ret 31 | 32 | original: 33 | popf 34 | push rdi 35 | sub rsp, 40h 36 | mov qword ptr [rsp + 20h], 0FFFFFFFFFFFFFFFEh 37 | jmp qword ptr [g_get_camera_data] 38 | 39 | asm_get_camera_data ENDP 40 | 41 | asm_get_timestop PROC 42 | pushf 43 | push rax 44 | mov al, g_camera_active 45 | cmp g_camera_active, 0 46 | je @f 47 | ; vmovss xmm8, g_engine_speed 48 | vmovss xmm6, g_engine_speed 49 | vmovss xmm3, g_engine_speed 50 | 51 | @@: 52 | ; If g_get_timestop_rip is 0 we can't start writing to the 53 | ; right address 54 | cmp g_get_timestop_rip, 0 55 | je @f 56 | cmp g_get_timestop_first_offset, 0 57 | je @f 58 | 59 | mov rax, g_get_timestop_rip 60 | add rax, 8h 61 | add rax, g_get_timestop_first_offset 62 | vmovss dword ptr [rax], xmm8 63 | add rax, 4h 64 | vmovss dword ptr [rax], xmm6 65 | add rax, 0Ch 66 | vmovss dword ptr [rax], xmm3 67 | 68 | @@: 69 | 70 | pop rax 71 | popf 72 | jmp [g_get_timestop] 73 | asm_get_timestop ENDP 74 | 75 | asm_get_controller PROC 76 | lea rdx, [rsp + 20h] 77 | mov rsi, r8 78 | lea rax, xinput_interceptor 79 | call rax 80 | test eax, eax 81 | 82 | jmp [g_get_controller] 83 | asm_get_controller ENDP 84 | 85 | END 86 | -------------------------------------------------------------------------------- /.github/workflows/dev.yml: -------------------------------------------------------------------------------- 1 | name: "build-dev" 2 | 3 | on: 4 | push: 5 | branches: 6 | - "dev" 7 | 8 | jobs: 9 | build-release: 10 | name: "Build Dev Version" 11 | runs-on: "windows-latest" 12 | 13 | steps: 14 | - name: "Checkout source code" 15 | uses: "actions/checkout@v1" 16 | with: 17 | submodules: true 18 | token: ${{ secrets.GITHUB_TOKEN }} 19 | 20 | - name: "Set current version" 21 | run: echo "GIT_VERSION=$(git.exe rev-parse --short HEAD)" >> $env:GITHUB_ENV 22 | 23 | - name: "Build Yakuza 0" 24 | run: | 25 | cargo.exe build -p yakuza0 --release 26 | 27 | - name: "Build Yakuza Kiwami" 28 | run: | 29 | cargo.exe build -p kiwami --release 30 | 31 | - name: "Build Yakuza Kiwami 2" 32 | run: | 33 | cargo.exe build -p kiwami2 --release 34 | 35 | - name: "Build Yakuza Like a Dragon" 36 | run: | 37 | cd likeadragon/lib 38 | cargo.exe build --release --features non_automatic 39 | cd ../../ 40 | cd likeadragon/injector 41 | cargo.exe build --release 42 | cd ../../ 43 | 44 | - name: "Pack Yakuza Like a Dragon build" 45 | run: | 46 | Copy target/release/likeadragon.dll ./likeadragon.dll 47 | Copy target/release/likeadragon-freecam.exe ./likeadragon-freecam.exe 48 | tar.exe -a -c -f likeadragon-freecam.zip ./likeadragon.dll ./likeadragon-freecam.exe 49 | 50 | - name: "Automatic Release" 51 | uses: "marvinpinto/action-automatic-releases@latest" 52 | with: 53 | repo_token: "${{ secrets.GITHUB_TOKEN }}" 54 | automatic_release_tag: "dev_release" 55 | prerelease: true 56 | title: "Latest release" 57 | files: | 58 | target/release/yakuza0-freecam.exe 59 | target/release/kiwami-freecam.exe 60 | target/release/kiwami2-freecam.exe 61 | likeadragon-freecam.zip 62 | 63 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: "build-release" 2 | 3 | on: 4 | push: 5 | paths: 6 | - '**.rs' 7 | - '**.lock' 8 | - '**.toml' 9 | - '**.yml' 10 | branches: 11 | - "master" 12 | 13 | jobs: 14 | build-release: 15 | name: "Build & Release" 16 | runs-on: "windows-latest" 17 | 18 | steps: 19 | - name: "Checkout source code" 20 | uses: "actions/checkout@v1" 21 | with: 22 | submodules: true 23 | token: ${{ secrets.GITHUB_TOKEN }} 24 | 25 | - name: "Set current version" 26 | run: echo "GIT_VERSION=$(git.exe rev-parse --short HEAD)" >> $env:GITHUB_ENV 27 | 28 | - name: "Build Yakuza 0" 29 | run: | 30 | cargo.exe build -p yakuza0 --release 31 | 32 | - name: "Build Yakuza Kiwami" 33 | run: | 34 | cargo.exe build -p kiwami --release 35 | 36 | - name: "Build Yakuza Kiwami 2" 37 | run: | 38 | cargo.exe build -p kiwami2 --release 39 | 40 | - name: "Build Yakuza Like a Dragon" 41 | run: | 42 | cd likeadragon/lib 43 | cargo.exe build --release --features non_automatic 44 | cd ../../ 45 | cd likeadragon/injector 46 | cargo.exe build --release 47 | cd ../../ 48 | 49 | - name: "Pack Yakuza Like a Dragon build" 50 | run: | 51 | Copy target/release/likeadragon.dll ./likeadragon.dll 52 | Copy target/release/likeadragon-freecam.exe ./likeadragon-freecam.exe 53 | tar.exe -a -c -f likeadragon-freecam.zip likeadragon.dll likeadragon-freecam.exe 54 | 55 | - name: "Automatic Release" 56 | uses: "marvinpinto/action-automatic-releases@latest" 57 | with: 58 | repo_token: "${{ secrets.GITHUB_TOKEN }}" 59 | automatic_release_tag: "latest" 60 | prerelease: false 61 | title: "Latest release" 62 | files: | 63 | target/release/yakuza0-freecam.exe 64 | target/release/kiwami-freecam.exe 65 | target/release/kiwami2-freecam.exe 66 | likeadragon-freecam.zip 67 | 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Yakuza 0, Kiwami, Kiwami 2 & Like A Dragon Free Camera Tool 2 | ![build-release](https://github.com/etra0/yakuza-freecam/workflows/build-release/badge.svg) 3 | 4 | Buy Me a Coffee at ko-fi.com 5 | 6 |

7 | 8 |

9 | 10 | This is a free camera tool for Yakuza 0, Kiwami and Kiwami 2. It works in Cutscenes and freeroam. 11 | 12 | [DEMO](https://twitter.com/etra0/status/1264050436031623169) 13 | 14 | # This only works with the Steam version 15 | 16 | ## Features 17 | Yakuza 0 & Kiwami: 18 | - You can release the camera in almost every place 19 | - You can pause the cinematics and move the camera around 20 | 21 | Yakuza Kiwami 2: 22 | - You can release the camera in almost every place 23 | - You can pause in freeroam and in the cinematics (experimental) 24 | 25 | Yakuza Like A Dragon: 26 | - You can release the camera in almost every place 27 | - You can change engine's speed at any time (i.e. pause the game). 28 | - Check the [instructions](#usage-ylad) for this photomode before using it. 29 | 30 | ## Usage 31 | 32 | You should see a Command Prompt window with instructions. If one briefly flashes on the screen, or doesn't appear at all, you may need to open Command Prompt yourself and run it to see what went wrong. 33 | 34 | ## Usage YLAD: 35 | **You have to deactivate Motion Blur if you want to mess with the world paused** 36 | 37 | Currently, you can only use it with a controller (no keyboard support) 38 | - R1 + R3: Photo Mode Activation 39 | - Left/Right arrow: Change engine speed 40 | - L2/R2: Change FoV 41 | 42 | 43 | ## Compilation 44 | Yakuza Zero: 45 | 46 | ``` 47 | cargo build -p yakuza0 --release 48 | ``` 49 | 50 | Yakuza Kiwami: 51 | 52 | ``` 53 | cargo build -p kiwami --release 54 | ``` 55 | 56 | Yakuza Kiwami 2: 57 | 58 | ``` 59 | cargo build -p kiwami2 --release 60 | ``` 61 | 62 | # Thanks 63 | 64 | I want to give some special thanks to: 65 | - [@Olivier__Cesar](https://twitter.com/Olivier__Cesar) for giving me some 66 | special support & sponsorship (seriously, thanks!) 67 | - Every supporter at Ko-Fi. For real, thanks a lot! 68 | - [@Miko_M2](https://twitter.com/Miko_M2) for the support in Ko-Fi and also the icon of the Kiwami 2 freecam. 69 | - [Timo654](https://github.com/Timo654) for the support in Ko-Fi and also the icon of Yakuza 0 freecam. 70 | - Galen#5628 for the icon of the Yakuza Like A Dragon freecam. 71 | - [Yakuza Modding Community](https://discord.gg/7HCGNCYp3V) for being a cool 72 | community and giving lots of support and testing. 73 | -------------------------------------------------------------------------------- /common/src/internal.rs: -------------------------------------------------------------------------------- 1 | use winapi::um::xinput; 2 | 3 | const DEADZONE: i16 = 2000; 4 | const MINIMUM_ENGINE_SPEED: f32 = 1e-3; 5 | 6 | #[derive(Default, Debug)] 7 | pub struct Input { 8 | pub engine_speed: f32, 9 | // Deltas with X and Y 10 | pub delta_pos: (f32, f32), 11 | pub delta_focus: (f32, f32), 12 | pub delta_rotation: f32, 13 | 14 | pub delta_altitude: f32, 15 | 16 | pub change_active: bool, 17 | pub is_active: bool, 18 | 19 | pub fov: f32, 20 | #[cfg(debug_assertions)] 21 | pub deattach: bool, 22 | } 23 | 24 | impl Input { 25 | pub fn new() -> Input { 26 | Self { 27 | fov: 0.92, 28 | engine_speed: MINIMUM_ENGINE_SPEED, 29 | ..Input::default() 30 | } 31 | } 32 | 33 | pub fn reset(&mut self) { 34 | self.delta_pos = (0., 0.); 35 | self.delta_focus = (0., 0.); 36 | self.delta_altitude = 0.; 37 | self.change_active = false; 38 | 39 | #[cfg(debug_assertions)] 40 | { 41 | self.deattach = false; 42 | } 43 | } 44 | 45 | pub fn sanitize(&mut self) { 46 | if self.fov < 1e-3 { 47 | self.fov = 0.01; 48 | } 49 | if self.fov > 3.12 { 50 | self.fov = 3.12; 51 | } 52 | 53 | if self.engine_speed < MINIMUM_ENGINE_SPEED { 54 | self.engine_speed = MINIMUM_ENGINE_SPEED; 55 | } 56 | } 57 | } 58 | 59 | pub fn handle_controller(input: &mut Input, func: fn(u32, &mut xinput::XINPUT_STATE) -> u32) { 60 | let mut xs: xinput::XINPUT_STATE = unsafe { std::mem::zeroed() }; 61 | func(0, &mut xs); 62 | 63 | let gp = xs.Gamepad; 64 | 65 | // check camera activation 66 | if (gp.wButtons & (0x200 | 0x80)) == (0x200 | 0x80) { 67 | input.change_active = true; 68 | } 69 | 70 | #[cfg(debug_assertions)] 71 | if (gp.wButtons & (0x1000 | 0x4000)) == (0x1000 | 0x4000) { 72 | input.deattach = true; 73 | } 74 | 75 | // Update the camera changes only if it's listening 76 | if !input.is_active { 77 | return; 78 | } 79 | 80 | // modify speed 81 | if (gp.wButtons & 0x4) != 0 { 82 | input.engine_speed -= 0.01; 83 | } 84 | if (gp.wButtons & 0x8) != 0 { 85 | input.engine_speed += 0.01; 86 | } 87 | 88 | if (gp.wButtons & (0x200)) != 0 { 89 | input.delta_rotation += 0.01; 90 | } 91 | 92 | if (gp.wButtons & (0x100)) != 0 { 93 | input.delta_rotation -= 0.01; 94 | } 95 | 96 | if (gp.wButtons & (0x200 | 0x100)) == (0x200 | 0x100) { 97 | input.delta_rotation = 0.; 98 | } 99 | 100 | if gp.bLeftTrigger > 150 { 101 | input.fov -= 0.01; 102 | } 103 | 104 | if gp.bRightTrigger > 150 { 105 | input.fov += 0.01; 106 | } 107 | 108 | macro_rules! dead_zone { 109 | ($val:expr) => { 110 | if ($val < DEADZONE) && ($val > -DEADZONE) { 111 | 0 112 | } else { 113 | $val 114 | } 115 | }; 116 | } 117 | 118 | input.delta_pos.0 = -(dead_zone!(gp.sThumbLX) as f32) / ((i16::MAX as f32) * 1e2); 119 | input.delta_pos.1 = (dead_zone!(gp.sThumbLY) as f32) / ((i16::MAX as f32) * 1e2); 120 | 121 | input.delta_focus.0 = (dead_zone!(gp.sThumbRX) as f32) / ((i16::MAX as f32) * 1e2); 122 | input.delta_focus.1 = -(dead_zone!(gp.sThumbRY) as f32) / ((i16::MAX as f32) * 1e2); 123 | 124 | } 125 | -------------------------------------------------------------------------------- /kiwami/src/main.rs: -------------------------------------------------------------------------------- 1 | use common::external::{get_version, Camera, Injection}; 2 | use memory_rs::external::process::Process; 3 | use std::f32; 4 | use std::io::Error; 5 | use std::rc::Rc; 6 | use std::thread; 7 | use std::time::{Duration, Instant}; 8 | use winapi::shared::windef::POINT; 9 | use winapi::um::winuser; 10 | use winapi::um::winuser::{GetAsyncKeyState, GetCursorPos, SetCursorPos}; 11 | 12 | const INITIAL_POS: i32 = 500; 13 | 14 | extern "C" { 15 | static get_camera_data: u8; 16 | static get_camera_data_end: u8; 17 | 18 | static get_controller_input: u8; 19 | static get_controller_input_end: u8; 20 | } 21 | 22 | pub fn main() -> Result<(), Error> { 23 | let mut mouse_pos: POINT = POINT::default(); 24 | 25 | // latest mouse positions 26 | let mut latest_x = 0; 27 | let mut latest_y = 0; 28 | 29 | println!("Yakuza Kiwami Freecam v{} by @etra0", get_version()); 30 | println!( 31 | " 32 | INSTRUCTIONS: 33 | 34 | PAUSE/L2 + X - Activate/Deactivate Free Camera 35 | END/L2 + Square - Pause the cinematic 36 | DEL - Deattach Mouse 37 | 38 | W, A, S, D/Left Stick - Move the camera 39 | Mouse/Right Stick - Point the camera 40 | CTRL, SPACE/TRIANGLE, X - Move UP or DOWN 41 | 42 | PG UP, PG DOWN/DPAD UP, DPAD DOWN - Increase/Decrease speed multiplier 43 | DPAD LEFT, DPAD RIGHT - Increase/Decrease Right Stick Sensitivity 44 | F1, F2/L2, R2 - Increase/Decrease FOV respectively 45 | Q, E/L1, R1 - Rotate the camera 46 | 47 | WARNING: Once you deattach the camera (PAUSE), your mouse will be set in a fixed 48 | position, so in order to attach/deattach the mouse to the camera, you can 49 | press DEL 50 | 51 | WARNING: If you're in freeroam and you stop hearing audio, it's probably 52 | because you have the paused option activated, simply press END to deactivate it. 53 | " 54 | ); 55 | 56 | println!("Waiting for the game to start"); 57 | let yakuza = loop { 58 | if let Ok(p) = Process::new("YakuzaKiwami.exe") { 59 | break Rc::new(p); 60 | }; 61 | 62 | thread::sleep(Duration::from_secs(5)); 63 | }; 64 | println!("Game hooked"); 65 | 66 | let entry_point: usize = 0x30CC33; 67 | let entry_point_size: usize = 8; 68 | let p_shellcode = unsafe { 69 | yakuza.inject_shellcode( 70 | entry_point, 71 | entry_point_size, 72 | &get_camera_data as *const u8, 73 | &get_camera_data_end as *const u8, 74 | ) 75 | }; 76 | 77 | let p_controller = unsafe { 78 | yakuza.inject_shellcode( 79 | 0x18C00B, 80 | 6, 81 | &get_controller_input as *const u8, 82 | &get_controller_input_end as *const u8, 83 | ) 84 | }; 85 | 86 | let mut cam = Camera::new(yakuza.clone(), p_shellcode); 87 | 88 | // function that changes the focal length of the cinematics, when 89 | // active, nop this 90 | cam.injections.push(Injection { 91 | entry_point: 0x187616, 92 | f_orig: vec![0xF3, 0x0F, 0x11, 0x89, 0xAC, 0x00, 0x00, 0x00], 93 | f_rep: vec![0x90; 8], 94 | }); 95 | 96 | // WIP: Pause the cinematics of the world. 97 | let pause_cinematic_f: Vec = vec![0x41, 0x8A, 0x8D, 0xD1, 0x00, 0x00, 0x00]; 98 | let pause_cinematic_rep: Vec = vec![0xB1, 0x01, 0x90, 0x90, 0x90, 0x90, 0x90]; 99 | let pause_cinematic_offset = 0x7BB8C; 100 | let mut pause_world = false; 101 | 102 | let mut active = false; 103 | let mut capture_mouse = false; 104 | 105 | let mut restart_mouse = false; 106 | 107 | loop { 108 | if capture_mouse & restart_mouse { 109 | unsafe { SetCursorPos(INITIAL_POS, INITIAL_POS) }; 110 | restart_mouse = !restart_mouse; 111 | latest_x = INITIAL_POS; 112 | latest_y = INITIAL_POS; 113 | continue; 114 | } 115 | 116 | let start = Instant::now(); 117 | 118 | // poll rate 119 | thread::sleep(Duration::from_millis(10)); 120 | unsafe { GetCursorPos(&mut mouse_pos) }; 121 | let duration = start.elapsed().as_millis() as f32; 122 | 123 | let speed_x = ((mouse_pos.x - latest_x) as f32) / duration / 100.; 124 | let speed_y = ((mouse_pos.y - latest_y) as f32) / duration / 100.; 125 | 126 | let controller_structure_p: usize = yakuza.read_value(p_controller + 0x200, true); 127 | let controller_state = match controller_structure_p { 128 | 0 => 0, 129 | _ => yakuza.read_value::(controller_structure_p, true), 130 | }; 131 | 132 | if active && capture_mouse { 133 | cam.update_position(speed_x, speed_y); 134 | unsafe { cam.handle_keyboard_input() }; 135 | } 136 | 137 | if active && (controller_structure_p != 0) { 138 | let [pos_x, pos_y, pitch, yaw] = 139 | yakuza.read_value::<[f32; 4]>(controller_structure_p + 0x10, true); 140 | 141 | // L1 & R1 check 142 | match controller_state & 0x30 { 143 | 0x20 => cam.update_fov(0.01), 144 | 0x10 => cam.update_fov(-0.01), 145 | _ => (), 146 | }; 147 | 148 | let speed: i8 = match controller_state & 0x3000 { 149 | 0x1000 => 1, 150 | 0x2000 => -1, 151 | _ => 0, 152 | }; 153 | 154 | let dp_up = match controller_state & 0x9 { 155 | 0x8 => 2f32, 156 | 0x1 => -2f32, 157 | _ => 0f32, 158 | }; 159 | 160 | let dir_speed = match controller_state & 0xC000 { 161 | 0x8000 => 1, 162 | 0x4000 => -1, 163 | _ => 0, 164 | }; 165 | 166 | let rotation: i8 = match controller_state & 0xC0 { 167 | 0x40 => 1, 168 | 0x80 => -1, 169 | 0xC0 => 2, 170 | _ => 0, 171 | }; 172 | 173 | cam.update_values(-pos_y, -pos_x, dp_up, speed, dir_speed, rotation); //dp_up, speed, dir_speed, rotation); 174 | cam.update_position(pitch, yaw); 175 | } 176 | 177 | latest_x = mouse_pos.x; 178 | latest_y = mouse_pos.y; 179 | 180 | // to scroll infinitely 181 | restart_mouse = !restart_mouse; 182 | unsafe { 183 | if (controller_state & 0x11 == 0x11) 184 | || (GetAsyncKeyState(winuser::VK_PAUSE) as u32 & 0x8000) != 0 185 | { 186 | active = !active; 187 | if controller_state & 0x11 != 0x11 { 188 | capture_mouse = active; 189 | } 190 | 191 | let c_status = if active { "Deattached" } else { "Attached" }; 192 | println!("status of camera: {}", c_status); 193 | if active { 194 | cam.deattach(); 195 | } else { 196 | cam.attach(); 197 | } 198 | thread::sleep(Duration::from_millis(500)); 199 | } 200 | 201 | if active & (GetAsyncKeyState(winuser::VK_DELETE) as u32 & 0x8000 != 0) { 202 | capture_mouse = !capture_mouse; 203 | let c_status = if !capture_mouse { 204 | "Deattached" 205 | } else { 206 | "Attached" 207 | }; 208 | println!("status of mouse: {}", c_status); 209 | thread::sleep(Duration::from_millis(500)); 210 | } 211 | 212 | if (controller_state & 0x14 == 0x14) 213 | || (GetAsyncKeyState(winuser::VK_END) as u32 & 0x8000) != 0 214 | { 215 | pause_world = !pause_world; 216 | println!("status of pausing: {}", pause_world); 217 | if pause_world { 218 | yakuza.write_aob(pause_cinematic_offset, &pause_cinematic_rep, false); 219 | } else { 220 | yakuza.write_aob(pause_cinematic_offset, &pause_cinematic_f, false); 221 | } 222 | thread::sleep(Duration::from_millis(500)); 223 | } 224 | } 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /yakuza0/src/main.rs: -------------------------------------------------------------------------------- 1 | use common::external::{get_version, Camera, Injection}; 2 | use memory_rs::external::process::Process; 3 | use std::f32; 4 | use std::io::Error; 5 | use std::rc::Rc; 6 | use std::thread; 7 | use std::time::{Duration, Instant}; 8 | use winapi::shared::windef::POINT; 9 | use winapi::um::winuser; 10 | use winapi::um::winuser::{GetAsyncKeyState, GetCursorPos, SetCursorPos}; 11 | 12 | const INITIAL_POS: i32 = 500; 13 | 14 | extern "C" { 15 | static get_camera_data: u8; 16 | static get_camera_data_end: u8; 17 | 18 | static get_controller_input: u8; 19 | static get_controller_input_end: u8; 20 | } 21 | 22 | fn detect_activation_by_controller(value: u64, activation: u64) -> bool { 23 | let result = value & activation; 24 | result == activation 25 | } 26 | 27 | pub fn main() -> Result<(), Error> { 28 | let mut mouse_pos: POINT = POINT::default(); 29 | 30 | // latest mouse positions 31 | let mut latest_x = 0; 32 | let mut latest_y = 0; 33 | 34 | println!("Yakuza 0 Freecam v{} by @etra0", get_version()); 35 | println!( 36 | " 37 | INSTRUCTIONS: 38 | 39 | PAUSE/L2 + X - Activate/Deactivate Free Camera 40 | END/L2 + Square - Pause the cinematic 41 | DEL - Deattach Mouse 42 | 43 | W, A, S, D/Left Stick - Move the camera 44 | Mouse/Right Stick - Point the camera 45 | CTRL, SPACE/TRIANGLE, X - Move UP or DOWN 46 | 47 | PG UP, PG DOWN/DPAD UP, DPAD DOWN - Increase/Decrease speed multiplier 48 | DPAD LEFT, DPAD RIGHT - Increase/Decrease Right Stick Sensitivity 49 | F1, F2/L2, R2 - Increase/Decrease FOV respectively 50 | Q, E/L1, R1 - Rotate the camera 51 | 52 | WARNING: Once you deattach the camera (PAUSE), your mouse will be set in a fixed 53 | position, so in order to attach/deattach the mouse to the camera, you can 54 | press DEL 55 | 56 | WARNING: If you're in freeroam and you stop hearing audio, it's probably 57 | because you have the paused option activated, simply press END to deactivate it. 58 | 59 | " 60 | ); 61 | 62 | println!("Waiting for the game to start"); 63 | let yakuza = loop { 64 | if let Ok(p) = Process::new("Yakuza0.exe") { 65 | break Rc::new(p); 66 | }; 67 | 68 | thread::sleep(Duration::from_secs(5)); 69 | }; 70 | println!("Game hooked"); 71 | 72 | let entry_point: usize = 0x18FD38; 73 | let p_shellcode = unsafe { 74 | yakuza.inject_shellcode( 75 | entry_point, 76 | 5, 77 | &get_camera_data as *const u8, 78 | &get_camera_data_end as *const u8, 79 | ) 80 | }; 81 | 82 | let p_controller = unsafe { 83 | yakuza.inject_shellcode( 84 | 0xEC1F, 85 | 6, 86 | &get_controller_input as *const u8, 87 | &get_controller_input_end as *const u8, 88 | ) 89 | }; 90 | 91 | let mut cam = Camera::new(yakuza.clone(), p_shellcode); 92 | 93 | // function that changes the focal length of the cinematics, when 94 | // active, nop this 95 | 96 | cam.injections.push(Injection { 97 | entry_point: 0x187616, 98 | f_orig: vec![0xF3, 0x0F, 0x11, 0x89, 0xAC, 0x00, 0x00, 0x00], 99 | f_rep: vec![0x90; 8], 100 | }); 101 | 102 | // WIP: Pause the cinematics of the world. 103 | let pause_cinematic_f: Vec = vec![0x41, 0x8A, 0x8E, 0xC9, 0x00, 0x00, 0x00]; 104 | let pause_cinematic_rep: Vec = vec![0xB1, 0x01, 0x90, 0x90, 0x90, 0x90, 0x90]; 105 | let pause_cinematic_offset = 0xB720DE; 106 | let mut pause_world = false; 107 | 108 | let mut active = false; 109 | let mut capture_mouse = false; 110 | 111 | let mut restart_mouse = false; 112 | 113 | loop { 114 | if capture_mouse & restart_mouse { 115 | unsafe { SetCursorPos(INITIAL_POS, INITIAL_POS) }; 116 | restart_mouse = !restart_mouse; 117 | latest_x = INITIAL_POS; 118 | latest_y = INITIAL_POS; 119 | continue; 120 | } 121 | 122 | let start = Instant::now(); 123 | 124 | // poll rate 125 | thread::sleep(Duration::from_millis(10)); 126 | unsafe { GetCursorPos(&mut mouse_pos) }; 127 | let duration = start.elapsed().as_millis() as f32; 128 | 129 | let speed_x = ((mouse_pos.x - latest_x) as f32) / duration; 130 | let speed_y = ((mouse_pos.y - latest_y) as f32) / duration; 131 | 132 | let controller_structure_p: usize = yakuza.read_value(p_controller + 0x200, true); 133 | let controller_state = match controller_structure_p { 134 | 0 => 0, 135 | _ => yakuza.read_value::(controller_structure_p, true), 136 | }; 137 | 138 | if active && capture_mouse { 139 | cam.update_position(speed_x, speed_y); 140 | unsafe { cam.handle_keyboard_input() }; 141 | } 142 | 143 | if active && (controller_structure_p != 0) { 144 | let [pos_x, pos_y, pitch, yaw] = 145 | yakuza.read_value::<[f32; 4]>(controller_structure_p + 0x10, true); 146 | 147 | // L1 & R1 check 148 | match controller_state & 0x30 { 149 | 0x20 => cam.update_fov(0.01), 150 | 0x10 => cam.update_fov(-0.01), 151 | _ => (), 152 | }; 153 | 154 | let speed: i8 = match controller_state & 0x3000 { 155 | 0x1000 => 1, 156 | 0x2000 => -1, 157 | _ => 0, 158 | }; 159 | 160 | let dp_up = match controller_state & 0x9 { 161 | 0x8 => 2f32, 162 | 0x1 => -2f32, 163 | _ => 0f32, 164 | }; 165 | 166 | let dir_speed = match controller_state & 0xC000 { 167 | 0x8000 => 1, 168 | 0x4000 => -1, 169 | _ => 0, 170 | }; 171 | 172 | let rotation: i8 = match controller_state & 0xC0 { 173 | 0x40 => 1, 174 | 0x80 => -1, 175 | 0xC0 => 2, 176 | _ => 0, 177 | }; 178 | 179 | cam.update_values(-pos_y, -pos_x, dp_up, speed, dir_speed, rotation); //dp_up, speed, dir_speed, rotation); 180 | cam.update_position(pitch, yaw); 181 | } 182 | 183 | latest_x = mouse_pos.x; 184 | latest_y = mouse_pos.y; 185 | 186 | // to scroll infinitely 187 | restart_mouse = !restart_mouse; 188 | unsafe { 189 | if detect_activation_by_controller(controller_state, 0x11) 190 | || (GetAsyncKeyState(winuser::VK_PAUSE) as u32 & 0x8000) != 0 191 | { 192 | active = !active; 193 | 194 | if controller_state & 0x11 != 0x11 { 195 | capture_mouse = active; 196 | } 197 | 198 | let c_status = if active { "Deattached" } else { "Attached" }; 199 | println!("status of camera: {}", c_status); 200 | if active { 201 | cam.deattach(); 202 | } else { 203 | cam.attach(); 204 | } 205 | thread::sleep(Duration::from_millis(500)); 206 | } 207 | 208 | if active & (GetAsyncKeyState(winuser::VK_DELETE) as u32 & 0x8000 != 0) { 209 | capture_mouse = !capture_mouse; 210 | let c_status = if !capture_mouse { 211 | "Deattached" 212 | } else { 213 | "Attached" 214 | }; 215 | println!("status of mouse: {}", c_status); 216 | thread::sleep(Duration::from_millis(500)); 217 | } 218 | 219 | if detect_activation_by_controller(controller_state, 0x14) 220 | || (GetAsyncKeyState(winuser::VK_END) as u32 & 0x8000) != 0 221 | { 222 | pause_world = !pause_world; 223 | println!("status of pausing: {}", pause_world); 224 | if pause_world { 225 | yakuza.write_aob(pause_cinematic_offset, &pause_cinematic_rep, false); 226 | } else { 227 | yakuza.write_aob(pause_cinematic_offset, &pause_cinematic_f, false); 228 | } 229 | thread::sleep(Duration::from_millis(500)); 230 | } 231 | } 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /kiwami2/src/main.rs: -------------------------------------------------------------------------------- 1 | use common::external::{get_version, Camera, Injection}; 2 | use memory_rs::external::process::Process; 3 | use std::f32; 4 | use std::io::Error; 5 | use std::rc::Rc; 6 | use std::thread; 7 | use std::time::{Duration, Instant}; 8 | use winapi::shared::windef::POINT; 9 | use winapi::um::winuser; 10 | use winapi::um::winuser::{GetAsyncKeyState, GetCursorPos, SetCursorPos}; 11 | 12 | const INITIAL_POS: i32 = 500; 13 | static mut ORIGINAL_VAL_UI: [u32; 5] = [0; 5]; 14 | 15 | extern "C" { 16 | static get_camera_data: u8; 17 | static get_camera_data_end: u8; 18 | 19 | static get_pause_value: u8; 20 | static get_pause_value_end: u8; 21 | 22 | static get_controller_input: u8; 23 | static get_controller_input_end: u8; 24 | } 25 | 26 | fn detect_activation_by_controller(value: u64) -> bool { 27 | let result = value & 0x11; 28 | result == 0x11 29 | } 30 | 31 | fn trigger_pause(process: &Process, addr: usize) { 32 | if addr == 0x0 { 33 | return; 34 | } 35 | process.write_value::(addr, 0x1, true); 36 | thread::sleep(Duration::from_millis(100)); 37 | process.write_value::(addr, 0x0, true); 38 | } 39 | 40 | fn remove_ui(process: &Process, activate: bool) { 41 | let offsets: Vec = vec![0x291D1DC, 0x291D1D0, 0x291D1EC, 0x291D1E8, 0x291D1E4]; 42 | 43 | unsafe { 44 | if ORIGINAL_VAL_UI[0] == 0 { 45 | for (i, offset) in offsets.iter().enumerate() { 46 | ORIGINAL_VAL_UI[i] = process.read_value::(*offset, false); 47 | } 48 | } 49 | 50 | for (i, offset) in offsets.iter().enumerate() { 51 | if activate { 52 | process.write_value::(*offset, -1, false); 53 | } else { 54 | process.write_value::(*offset, ORIGINAL_VAL_UI[i], false); 55 | } 56 | } 57 | } 58 | } 59 | 60 | pub fn main() -> Result<(), Error> { 61 | let mut mouse_pos: POINT = POINT::default(); 62 | 63 | // latest mouse positions 64 | let mut latest_x = 0; 65 | let mut latest_y = 0; 66 | 67 | println!("Yakuza Kiwami 2 Freecam v{} by @etra0", get_version()); 68 | println!( 69 | " 70 | INSTRUCTIONS: 71 | 72 | PAUSE/L2 + X - Activate/Deactivate Free Camera 73 | DEL - Deattach Mouse 74 | 75 | WASD/Left Stick - Move in the direction you're pointing 76 | Mouse/Right Stick - Point 77 | CTRL, SPACE/TRIANGLE, X - Move UP or DOWN 78 | 79 | PG UP, PG DOWN/DPAD UP, DPAD DOWN - Increase/Decrease speed multiplier 80 | DPAD LEFT, DPAD RIGHT - Increase/Decrease Right Stick Sensitivity 81 | F1, F2/L2, R2 - Increase/Decrease FOV respectively 82 | Q, E/L1, R1 - Rotate the camera 83 | 84 | WARNING: Don't forget to deactivate the freecam before skipping a cutscene 85 | (it may cause a game freeze) 86 | 87 | WARNING: Once you deattach the camera (PAUSE), your mouse will be set in a fixed 88 | position, so in order to attach/deattach the mouse to the camera, you can 89 | press DEL 90 | " 91 | ); 92 | 93 | println!("Waiting for the game to start"); 94 | let yakuza = loop { 95 | if let Ok(p) = Process::new("YakuzaKiwami2.exe") { 96 | break Rc::new(p); 97 | }; 98 | 99 | thread::sleep(Duration::from_secs(5)); 100 | }; 101 | println!("Game hooked"); 102 | 103 | let entry_point: usize = 0x1F0222B; 104 | 105 | let p_shellcode = unsafe { 106 | yakuza.inject_shellcode( 107 | entry_point, 108 | 9, 109 | &get_camera_data as *const u8, 110 | &get_camera_data_end as *const u8, 111 | ) 112 | }; 113 | 114 | let p_controller = unsafe { 115 | yakuza.inject_shellcode( 116 | 0x1B98487, 117 | 8, 118 | &get_controller_input as *const u8, 119 | &get_controller_input_end as *const u8, 120 | ) 121 | }; 122 | 123 | let pause_value_ep: usize = 0xDF5E1B; 124 | let pause_value = unsafe { 125 | yakuza.inject_shellcode( 126 | pause_value_ep, 127 | 7, 128 | &get_pause_value as *const u8, 129 | &get_pause_value_end as *const u8, 130 | ) 131 | }; 132 | 133 | let mut cam = Camera::new(yakuza.clone(), p_shellcode); 134 | 135 | // function that changes the focal length of the cinematics, when 136 | // active, nop this 137 | cam.injections.push(Injection { 138 | entry_point: 0xB78D87, 139 | f_orig: vec![0x89, 0x86, 0xB8, 0x00, 0x00, 0x00], 140 | f_rep: vec![0x90; 6], 141 | }); 142 | 143 | // nop the setcursorpos inside the game 144 | cam.injections.push(Injection { 145 | entry_point: 0x1BA285B, 146 | f_orig: vec![0xFF, 0x15, 0x47, 0x52, 0x4A, 0x00], 147 | f_rep: vec![0x90; 6], 148 | }); 149 | 150 | // WIP: Pause the cinematics of the world. 151 | cam.injections.push(Injection { 152 | entry_point: 0xDF6F86, 153 | f_orig: vec![0x0F, 0x84, 0x5E, 0x02, 0x00, 0x00], 154 | f_rep: vec![0xE9, 0x5F, 0x02, 0x00, 0x00, 0x90], 155 | }); 156 | 157 | // Hide UI stuff 158 | cam.injections.push(Injection { 159 | entry_point: 0x8B2E8C, 160 | f_orig: vec![0x41, 0x0F, 0x29, 0x9E, 0x70, 0x01, 0x00, 0x00], 161 | f_rep: vec![0x45, 0x0F, 0x29, 0x8E, 0x70, 0x01, 0x00, 0x00], 162 | }); 163 | 164 | // flashy health bar 165 | cam.injections.push(Injection { 166 | entry_point: 0x1B71453, 167 | f_orig: vec![0xC6, 0x04, 0x0B, 0x01], 168 | f_rep: vec![0xC6, 0x04, 0x0B, 0x00], 169 | }); 170 | 171 | // Nop UI coords writers 172 | cam.injections.push(Injection { 173 | entry_point: 0x1F0CB72, 174 | f_orig: vec![ 175 | 0x89, 0x05, 0x64, 0x06, 0xA1, 0x00, 0x89, 0x0D, 0x52, 0x06, 0xA1, 0x00, 0x89, 0x05, 176 | 0x68, 0x06, 0xA1, 0x00, 0x89, 0x0D, 0x5E, 0x06, 0xA1, 0x00, 177 | ], 178 | f_rep: vec![0x90; 24], 179 | }); 180 | 181 | let mut active = false; 182 | let mut capture_mouse = false; 183 | 184 | let mut restart_mouse = false; 185 | 186 | loop { 187 | if capture_mouse & restart_mouse { 188 | unsafe { SetCursorPos(INITIAL_POS, INITIAL_POS) }; 189 | restart_mouse = !restart_mouse; 190 | latest_x = INITIAL_POS; 191 | latest_y = INITIAL_POS; 192 | continue; 193 | } 194 | 195 | let start = Instant::now(); 196 | 197 | // poll rate 198 | thread::sleep(Duration::from_millis(10)); 199 | unsafe { GetCursorPos(&mut mouse_pos) }; 200 | let duration = start.elapsed().as_millis() as f32; 201 | 202 | let speed_x = ((mouse_pos.x - latest_x) as f32) / duration; 203 | let speed_y = ((mouse_pos.y - latest_y) as f32) / duration; 204 | 205 | let c_v_a = yakuza.read_value::(pause_value + 0x200, true); 206 | let controller_structure_p: usize = yakuza.read_value(p_controller + 0x200, true); 207 | let controller_state = match controller_structure_p { 208 | 0 => 0, 209 | _ => yakuza.read_value::(controller_structure_p, true), 210 | }; 211 | 212 | if active && capture_mouse { 213 | cam.update_position(speed_x, speed_y); 214 | unsafe { cam.handle_keyboard_input() }; 215 | } 216 | 217 | if active && (controller_structure_p != 0x0) { 218 | let [pos_x, pos_y, pitch, yaw] = 219 | yakuza.read_value::<[f32; 4]>(controller_structure_p + 0x10, true); 220 | 221 | // L2 & R2 check 222 | match controller_state & 0x30 { 223 | 0x20 => cam.update_fov(0.01), 224 | 0x10 => cam.update_fov(-0.01), 225 | _ => (), 226 | }; 227 | 228 | let dp_up = match controller_state & 0x9 { 229 | 0x01 => -2f32, 230 | 0x08 => 2f32, 231 | _ => 0f32, 232 | }; 233 | 234 | let speed: i8 = match controller_state & 0x3000 { 235 | 0x1000 => 1, 236 | 0x2000 => -1, 237 | _ => 0, 238 | }; 239 | 240 | let dir_speed: i8 = match controller_state & 0xC000 { 241 | 0x4000 => -1, 242 | 0x8000 => 1, 243 | _ => 0, 244 | }; 245 | 246 | let rotation: i8 = match controller_state & 0xC0 { 247 | 0x40 => 1, 248 | 0x80 => -1, 249 | 0xC0 => 2, 250 | _ => 0, 251 | }; 252 | 253 | cam.update_values(-pos_y, -pos_x, dp_up, speed, dir_speed, rotation); 254 | cam.update_position(pitch, yaw); 255 | } 256 | 257 | latest_x = mouse_pos.x; 258 | latest_y = mouse_pos.y; 259 | 260 | // to scroll infinitely 261 | restart_mouse = !restart_mouse; 262 | unsafe { 263 | if detect_activation_by_controller(controller_state) 264 | || ((GetAsyncKeyState(winuser::VK_PAUSE) as u32 & 0x8000) != 0) 265 | { 266 | active = !active; 267 | if !detect_activation_by_controller(controller_state) { 268 | capture_mouse = active; 269 | } 270 | 271 | let c_status = if active { "Deattached" } else { "Attached" }; 272 | println!("status of camera: {}", c_status); 273 | 274 | if active { 275 | cam.deattach(); 276 | remove_ui(&yakuza, true); 277 | } else { 278 | cam.attach(); 279 | remove_ui(&yakuza, false); 280 | } 281 | 282 | trigger_pause(&yakuza, c_v_a); 283 | thread::sleep(Duration::from_millis(500)); 284 | } 285 | 286 | if (GetAsyncKeyState(winuser::VK_HOME) as u32 & 0x8000) != 0 { 287 | active = !active; 288 | capture_mouse = active; 289 | 290 | let c_status = if active { "Deattached" } else { "Attached" }; 291 | println!("status of camera: {}", c_status); 292 | 293 | if active { 294 | cam.deattach(); 295 | remove_ui(&yakuza, true); 296 | } else { 297 | remove_ui(&yakuza, false); 298 | cam.attach(); 299 | } 300 | 301 | thread::sleep(Duration::from_millis(500)); 302 | } 303 | 304 | if (GetAsyncKeyState(winuser::VK_END) as u32 & 0x8000) != 0 { 305 | active = !active; 306 | capture_mouse = active; 307 | 308 | let c_status = if active { "Deattached" } else { "Attached" }; 309 | println!("status of camera: {}", c_status); 310 | 311 | if active { 312 | remove_ui(&yakuza, true); 313 | cam.deattach(); 314 | } else { 315 | remove_ui(&yakuza, false); 316 | cam.attach(); 317 | } 318 | thread::sleep(Duration::from_millis(500)); 319 | } 320 | 321 | if active & (GetAsyncKeyState(winuser::VK_DELETE) as u32 & 0x8000 != 0) { 322 | capture_mouse = !capture_mouse; 323 | let c_status = if !capture_mouse { 324 | "Deattached" 325 | } else { 326 | "Attached" 327 | }; 328 | println!("status of mouse: {}", c_status); 329 | thread::sleep(Duration::from_millis(500)); 330 | } 331 | } 332 | } 333 | } 334 | -------------------------------------------------------------------------------- /common/src/external.rs: -------------------------------------------------------------------------------- 1 | use memory_rs::external::process::Process; 2 | use nalgebra_glm as glm; 3 | use std::ffi::CString; 4 | use std::rc::Rc; 5 | use winapi; 6 | use winapi::um::winuser; 7 | 8 | const CARGO_VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION"); 9 | const GIT_VERSION: Option<&'static str> = option_env!("GIT_VERSION"); 10 | 11 | /// Generate current version of the executable from the 12 | /// latest git version and the cargo verison. 13 | pub fn get_version() -> String { 14 | let cargo = CARGO_VERSION.unwrap_or("Unknown"); 15 | let git = GIT_VERSION.unwrap_or("Unknown"); 16 | 17 | return format!("{}.{}", cargo, git); 18 | } 19 | 20 | /// Keys that aren't contained in the VirtualKeys from the Windows API. 21 | #[repr(i32)] 22 | pub enum Keys { 23 | A = 0x41, 24 | D = 0x44, 25 | E = 0x45, 26 | Q = 0x51, 27 | S = 0x53, 28 | W = 0x57, 29 | } 30 | 31 | /// Struct that contains an entry point relative to the executable, 32 | /// the original bytes (`f_orig`) and the bytes to be injected (`f_rep`) 33 | /// 34 | pub struct Injection { 35 | /// Entry point relative to the executable 36 | pub entry_point: usize, 37 | /// Original bytes 38 | pub f_orig: Vec, 39 | /// Bytes to be injected 40 | pub f_rep: Vec, 41 | } 42 | 43 | /// Main struct that will handle the camera behaviour. 44 | pub struct Camera { 45 | process: Rc, 46 | /// Camera position in the lookAt version 47 | p_cam_x: f32, 48 | p_cam_y: f32, 49 | p_cam_z: f32, 50 | 51 | /// Camera foocus on a lookAt version 52 | f_cam_x: f32, 53 | f_cam_y: f32, 54 | f_cam_z: f32, 55 | 56 | /// Position differentials to be added according to user input. 57 | /// (Basically what will move the camera) 58 | dp_forward: f32, 59 | dp_sides: f32, 60 | dp_up: f32, 61 | 62 | speed_scale: f32, 63 | dir_speed_scale: f32, 64 | rotation: f32, 65 | 66 | /// Pointer where the injection was allocated. 67 | data_base_addr: usize, 68 | 69 | fov: f32, 70 | 71 | pub injections: Vec, 72 | } 73 | 74 | impl Camera { 75 | pub fn new(process: Rc, data_base_addr: usize) -> Camera { 76 | Camera { 77 | process, 78 | p_cam_x: 0f32, 79 | p_cam_y: 0f32, 80 | p_cam_z: 0f32, 81 | f_cam_x: 0f32, 82 | f_cam_y: 0f32, 83 | f_cam_z: 0f32, 84 | dp_forward: 0f32, 85 | dp_sides: 0f32, 86 | dp_up: 0f32, 87 | speed_scale: 0.01, 88 | dir_speed_scale: 0.05, 89 | rotation: 0f32, 90 | data_base_addr, 91 | fov: 0f32, 92 | injections: vec![], 93 | } 94 | } 95 | 96 | /// Calculates the new lookAt using spherical coordinates. 97 | pub fn calc_new_focus_point( 98 | cam_x: f32, 99 | cam_z: f32, 100 | cam_y: f32, 101 | speed_x: f32, 102 | speed_y: f32, 103 | ) -> (f32, f32, f32) { 104 | // use spherical coordinates to add speed 105 | let theta = cam_z.atan2(cam_x) + speed_x; 106 | 107 | let phi = (cam_x.powi(2) + cam_z.powi(2)).sqrt().atan2(cam_y) + speed_y; 108 | 109 | let r = (cam_x.powi(2) + cam_y.powi(2) + cam_z.powi(2)).sqrt(); 110 | 111 | let r_cam_x = r * theta.cos() * phi.sin(); 112 | let r_cam_z = r * theta.sin() * phi.sin(); 113 | let r_cam_y = r * phi.cos(); 114 | 115 | (r_cam_x, r_cam_z, r_cam_y) 116 | } 117 | 118 | pub fn calculate_rotation(focus: glm::Vec3, pos: glm::Vec3, rotation: f32) -> [f32; 3] { 119 | let up = glm::vec3(0., 1., 0.); 120 | 121 | let m_look_at = glm::look_at(&focus, &pos, &up); 122 | let direction = { 123 | let row = m_look_at.row(2); 124 | glm::vec3(row[0], row[1], row[2]) 125 | }; 126 | // let axis = glm::vec3(0., 0., 1.); 127 | let m_new = glm::rotate_normalized_axis(&m_look_at, rotation, &direction); 128 | 129 | let result = m_new.row(1); 130 | 131 | [result[0], result[1], result[2]] 132 | } 133 | 134 | pub fn update_fov(&mut self, delta: f32) { 135 | if (delta < 0f32) & (self.fov < 0.1) { 136 | return; 137 | } 138 | if (delta > 0f32) & (self.fov > 3.13) { 139 | return; 140 | } 141 | self.fov += delta; 142 | self.process 143 | .write_value::(self.data_base_addr + 0x260, self.fov, true); 144 | } 145 | 146 | pub unsafe fn handle_keyboard_input(&mut self) { 147 | let mut dp_forward = 0f32; 148 | let mut dp_sides = 0f32; 149 | let mut dp_up = 0f32; 150 | let mut speed_scale: i8 = 0; 151 | let mut dir_speed: i8 = 0; 152 | let mut rotation: i8 = 0; 153 | 154 | /// Handle positive and negative state of keypressing 155 | macro_rules! handle_state { 156 | ([ $key_pos:expr, $key_neg:expr, $var:ident, $val:expr ]; $($tt:tt)*) => { 157 | handle_state!([$key_pos, $key_neg, $var = $val, $var = - $val]; $($tt)*); 158 | }; 159 | 160 | ([ $key_pos:expr, $key_neg:expr, $pos_do:expr, $neg_do:expr ]; $($tt:tt)*) => { 161 | if (winuser::GetAsyncKeyState($key_pos as i32) as u32 & 0x8000) != 0 { 162 | $pos_do; 163 | } 164 | 165 | if (winuser::GetAsyncKeyState($key_neg as i32) as u32 & 0x8000) != 0 { 166 | $neg_do; 167 | } 168 | handle_state!($($tt)*); 169 | }; 170 | 171 | () => {} 172 | } 173 | 174 | handle_state! { 175 | [Keys::W, Keys::S, dp_forward, 1.]; 176 | [Keys::A, Keys::D, dp_sides, 1.]; 177 | [winuser::VK_SPACE, winuser::VK_CONTROL, dp_up, 1.]; 178 | [winuser::VK_F1, winuser::VK_F2, self.update_fov(0.01), self.update_fov(-0.01)]; 179 | [winuser::VK_PRIOR, winuser::VK_NEXT, speed_scale, 1]; 180 | [winuser::VK_F4, winuser::VK_F3, dir_speed, 1]; 181 | [Keys::E, Keys::Q, rotation, 1]; 182 | } 183 | 184 | self.update_values( 185 | dp_forward, 186 | dp_sides, 187 | dp_up, 188 | speed_scale, 189 | dir_speed, 190 | rotation, 191 | ); 192 | } 193 | 194 | pub fn update_values( 195 | &mut self, 196 | dp_forward: f32, 197 | dp_sides: f32, 198 | dp_up: f32, 199 | speed_scale: i8, 200 | dir_speed_scale: i8, 201 | rotation: i8, 202 | ) { 203 | self.dp_forward = dp_forward * self.speed_scale; 204 | self.dp_sides = dp_sides * self.speed_scale; 205 | self.dp_up = dp_up * self.speed_scale; 206 | 207 | match speed_scale { 208 | 1 => { 209 | self.speed_scale += 5e-5; 210 | } 211 | -1 => { 212 | if self.speed_scale > 1e-5 { 213 | self.speed_scale -= 5e-5; 214 | } else { 215 | println!("Speed couldn't decrease"); 216 | } 217 | } 218 | _ => (), 219 | }; 220 | 221 | match dir_speed_scale { 222 | 1 => { 223 | self.dir_speed_scale += 5e-5; 224 | } 225 | -1 => { 226 | if self.dir_speed_scale > 1e-5 { 227 | self.dir_speed_scale -= 5e-5; 228 | } else { 229 | println!("Speed couldn't decrease"); 230 | } 231 | } 232 | _ => (), 233 | }; 234 | 235 | match rotation { 236 | 1 => { 237 | self.rotation -= 0.01; 238 | } 239 | -1 => { 240 | self.rotation += 0.01; 241 | } 242 | 2 => { 243 | self.rotation = 0.; 244 | } 245 | _ => (), 246 | }; 247 | } 248 | 249 | pub fn update_position(&mut self, yaw: f32, pitch: f32) { 250 | self.f_cam_x = self 251 | .process 252 | .read_value::(self.data_base_addr + 0x200, true); 253 | self.f_cam_y = self 254 | .process 255 | .read_value::(self.data_base_addr + 0x204, true); 256 | self.f_cam_z = self 257 | .process 258 | .read_value::(self.data_base_addr + 0x208, true); 259 | 260 | self.p_cam_x = self 261 | .process 262 | .read_value::(self.data_base_addr + 0x220, true); 263 | self.p_cam_y = self 264 | .process 265 | .read_value::(self.data_base_addr + 0x224, true); 266 | self.p_cam_z = self 267 | .process 268 | .read_value::(self.data_base_addr + 0x228, true); 269 | 270 | self.fov = self 271 | .process 272 | .read_value::(self.data_base_addr + 0x260, true); 273 | 274 | let r_cam_x = self.f_cam_x - self.p_cam_x; 275 | let r_cam_y = self.f_cam_y - self.p_cam_y; 276 | let r_cam_z = self.f_cam_z - self.p_cam_z; 277 | 278 | let pitch = pitch * self.dir_speed_scale; 279 | let yaw = yaw * self.dir_speed_scale; 280 | 281 | let (r_cam_x, r_cam_z, r_cam_y) = 282 | Camera::calc_new_focus_point(r_cam_x, r_cam_z, r_cam_y, yaw, pitch); 283 | 284 | let pf = glm::vec3(self.f_cam_x, self.f_cam_y, self.f_cam_z); 285 | let pp = glm::vec3(self.p_cam_x, self.p_cam_y, self.p_cam_z); 286 | 287 | let up_new = Camera::calculate_rotation(pf, pp, self.rotation); 288 | let up_v = up_new; 289 | 290 | self.f_cam_x = self.p_cam_x + r_cam_x + self.dp_forward * r_cam_x + self.dp_sides * r_cam_z; 291 | self.f_cam_z = self.p_cam_z + r_cam_z + self.dp_forward * r_cam_z - self.dp_sides * r_cam_x; 292 | self.f_cam_y = self.p_cam_y + r_cam_y + self.dp_forward * r_cam_y + self.dp_up; 293 | 294 | self.p_cam_x = self.p_cam_x + self.dp_forward * r_cam_x + self.dp_sides * r_cam_z; 295 | self.p_cam_z = self.p_cam_z + self.dp_forward * r_cam_z - self.dp_sides * r_cam_x; 296 | self.p_cam_y = self.p_cam_y + self.dp_forward * r_cam_y + self.dp_up; 297 | 298 | // flush movement 299 | self.dp_forward = 0f32; 300 | self.dp_up = 0f32; 301 | self.dp_sides = 0f32; 302 | 303 | self.process 304 | .write_value::(self.data_base_addr + 0x200, self.f_cam_x, true); 305 | self.process 306 | .write_value::(self.data_base_addr + 0x204, self.f_cam_y, true); 307 | self.process 308 | .write_value::(self.data_base_addr + 0x208, self.f_cam_z, true); 309 | 310 | self.process 311 | .write_value::(self.data_base_addr + 0x220, self.p_cam_x, true); 312 | self.process 313 | .write_value::(self.data_base_addr + 0x224, self.p_cam_y, true); 314 | self.process 315 | .write_value::(self.data_base_addr + 0x228, self.p_cam_z, true); 316 | 317 | self.process 318 | .write_value::<[f32; 3]>(self.data_base_addr + 0x240, up_v, true); 319 | } 320 | 321 | pub fn deattach(&self) { 322 | self.process 323 | .write_value::(self.data_base_addr + 0x1F0, 1, true); 324 | for injection in &self.injections { 325 | self.process 326 | .write_aob(injection.entry_point, &injection.f_rep, false); 327 | } 328 | } 329 | 330 | pub fn attach(&self) { 331 | self.process 332 | .write_value::(self.data_base_addr + 0x1F0, 0, true); 333 | for injection in &self.injections { 334 | self.process 335 | .write_aob(injection.entry_point, &injection.f_orig, false); 336 | } 337 | } 338 | } 339 | 340 | pub fn error_message(message: &str) { 341 | let title = CString::new("Error while patching").unwrap(); 342 | let message = CString::new(message).unwrap(); 343 | 344 | unsafe { 345 | winapi::um::winuser::MessageBoxA( 346 | std::ptr::null_mut(), 347 | message.as_ptr(), 348 | title.as_ptr(), 349 | 0x10, 350 | ); 351 | } 352 | } 353 | 354 | pub fn success_message(message: &str) { 355 | let title = CString::new("Patching").unwrap(); 356 | let message = CString::new(message).unwrap(); 357 | 358 | unsafe { 359 | winapi::um::winuser::MessageBoxA( 360 | std::ptr::null_mut(), 361 | message.as_ptr(), 362 | title.as_ptr(), 363 | 0x40, 364 | ); 365 | } 366 | } 367 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "alga" 7 | version = "0.8.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "2cc836ad7a40dc9d8049574e2a29979f5dc77deeea4d7ebcd29773452f0e9694" 10 | dependencies = [ 11 | "approx", 12 | "libm", 13 | "num-complex", 14 | "num-traits", 15 | ] 16 | 17 | [[package]] 18 | name = "anyhow" 19 | version = "1.0.56" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27" 22 | 23 | [[package]] 24 | name = "approx" 25 | version = "0.3.2" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" 28 | dependencies = [ 29 | "num-traits", 30 | ] 31 | 32 | [[package]] 33 | name = "autocfg" 34 | version = "0.1.8" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" 37 | dependencies = [ 38 | "autocfg 1.1.0", 39 | ] 40 | 41 | [[package]] 42 | name = "autocfg" 43 | version = "1.1.0" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 46 | 47 | [[package]] 48 | name = "bitflags" 49 | version = "1.3.2" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 52 | 53 | [[package]] 54 | name = "cc" 55 | version = "1.0.73" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" 58 | 59 | [[package]] 60 | name = "cfg-if" 61 | version = "1.0.0" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 64 | 65 | [[package]] 66 | name = "chrono" 67 | version = "0.4.19" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" 70 | dependencies = [ 71 | "libc", 72 | "num-integer", 73 | "num-traits", 74 | "time", 75 | "winapi", 76 | ] 77 | 78 | [[package]] 79 | name = "cloudabi" 80 | version = "0.0.3" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" 83 | dependencies = [ 84 | "bitflags", 85 | ] 86 | 87 | [[package]] 88 | name = "common" 89 | version = "1.0.0" 90 | dependencies = [ 91 | "anyhow", 92 | "cc", 93 | "log", 94 | "memory-rs", 95 | "nalgebra-glm", 96 | "winapi", 97 | "winres", 98 | ] 99 | 100 | [[package]] 101 | name = "fuchsia-cprng" 102 | version = "0.1.1" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" 105 | 106 | [[package]] 107 | name = "generic-array" 108 | version = "0.12.4" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" 111 | dependencies = [ 112 | "typenum", 113 | ] 114 | 115 | [[package]] 116 | name = "kiwami" 117 | version = "1.0.0" 118 | dependencies = [ 119 | "cc", 120 | "common", 121 | "memory-rs", 122 | "winapi", 123 | "winres", 124 | ] 125 | 126 | [[package]] 127 | name = "kiwami2" 128 | version = "1.0.0" 129 | dependencies = [ 130 | "cc", 131 | "common", 132 | "memory-rs", 133 | "winapi", 134 | "winres", 135 | ] 136 | 137 | [[package]] 138 | name = "libc" 139 | version = "0.2.123" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | checksum = "cb691a747a7ab48abc15c5b42066eaafde10dc427e3b6ee2a1cf43db04c763bd" 142 | 143 | [[package]] 144 | name = "libm" 145 | version = "0.1.4" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" 148 | 149 | [[package]] 150 | name = "likeadragon" 151 | version = "0.1.0" 152 | dependencies = [ 153 | "anyhow", 154 | "cc", 155 | "common", 156 | "log", 157 | "memory-rs", 158 | "nalgebra-glm", 159 | "simple_injector", 160 | "simplelog", 161 | "winapi", 162 | "winres", 163 | ] 164 | 165 | [[package]] 166 | name = "likeadragon-freecam" 167 | version = "0.1.0" 168 | dependencies = [ 169 | "cc", 170 | "memory-rs", 171 | "simple_injector", 172 | "winres", 173 | ] 174 | 175 | [[package]] 176 | name = "log" 177 | version = "0.4.16" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" 180 | dependencies = [ 181 | "cfg-if", 182 | ] 183 | 184 | [[package]] 185 | name = "matrixmultiply" 186 | version = "0.2.4" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "916806ba0031cd542105d916a97c8572e1fa6dd79c9c51e7eb43a09ec2dd84c1" 189 | dependencies = [ 190 | "rawpointer", 191 | ] 192 | 193 | [[package]] 194 | name = "memory-rs" 195 | version = "0.2.4" 196 | source = "git+https://github.com/etra0/memory-rs#de3cce682e5f6df8d4641df375c4323628e318d6" 197 | dependencies = [ 198 | "anyhow", 199 | "winapi", 200 | ] 201 | 202 | [[package]] 203 | name = "nalgebra" 204 | version = "0.17.3" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "be539bb5e751c248d25c21c850b69105809306f367c35bf1daa8724f0cb786df" 207 | dependencies = [ 208 | "alga", 209 | "approx", 210 | "generic-array", 211 | "matrixmultiply", 212 | "num-complex", 213 | "num-traits", 214 | "rand", 215 | "typenum", 216 | ] 217 | 218 | [[package]] 219 | name = "nalgebra-glm" 220 | version = "0.3.0" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "07a3be33aa9aa21ad8959e80cd7435c23c6fe0cc39d0af98bda9a4e486962df2" 223 | dependencies = [ 224 | "alga", 225 | "approx", 226 | "nalgebra", 227 | "num-traits", 228 | ] 229 | 230 | [[package]] 231 | name = "num-complex" 232 | version = "0.2.4" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" 235 | dependencies = [ 236 | "autocfg 1.1.0", 237 | "num-traits", 238 | ] 239 | 240 | [[package]] 241 | name = "num-integer" 242 | version = "0.1.44" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" 245 | dependencies = [ 246 | "autocfg 1.1.0", 247 | "num-traits", 248 | ] 249 | 250 | [[package]] 251 | name = "num-traits" 252 | version = "0.2.14" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 255 | dependencies = [ 256 | "autocfg 1.1.0", 257 | ] 258 | 259 | [[package]] 260 | name = "rand" 261 | version = "0.6.5" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" 264 | dependencies = [ 265 | "autocfg 0.1.8", 266 | "libc", 267 | "rand_chacha", 268 | "rand_core 0.4.2", 269 | "rand_hc", 270 | "rand_isaac", 271 | "rand_jitter", 272 | "rand_os", 273 | "rand_pcg", 274 | "rand_xorshift", 275 | "winapi", 276 | ] 277 | 278 | [[package]] 279 | name = "rand_chacha" 280 | version = "0.1.1" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" 283 | dependencies = [ 284 | "autocfg 0.1.8", 285 | "rand_core 0.3.1", 286 | ] 287 | 288 | [[package]] 289 | name = "rand_core" 290 | version = "0.3.1" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" 293 | dependencies = [ 294 | "rand_core 0.4.2", 295 | ] 296 | 297 | [[package]] 298 | name = "rand_core" 299 | version = "0.4.2" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" 302 | 303 | [[package]] 304 | name = "rand_hc" 305 | version = "0.1.0" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" 308 | dependencies = [ 309 | "rand_core 0.3.1", 310 | ] 311 | 312 | [[package]] 313 | name = "rand_isaac" 314 | version = "0.1.1" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" 317 | dependencies = [ 318 | "rand_core 0.3.1", 319 | ] 320 | 321 | [[package]] 322 | name = "rand_jitter" 323 | version = "0.1.4" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" 326 | dependencies = [ 327 | "libc", 328 | "rand_core 0.4.2", 329 | "winapi", 330 | ] 331 | 332 | [[package]] 333 | name = "rand_os" 334 | version = "0.1.3" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" 337 | dependencies = [ 338 | "cloudabi", 339 | "fuchsia-cprng", 340 | "libc", 341 | "rand_core 0.4.2", 342 | "rdrand", 343 | "winapi", 344 | ] 345 | 346 | [[package]] 347 | name = "rand_pcg" 348 | version = "0.1.2" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" 351 | dependencies = [ 352 | "autocfg 0.1.8", 353 | "rand_core 0.4.2", 354 | ] 355 | 356 | [[package]] 357 | name = "rand_xorshift" 358 | version = "0.1.1" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" 361 | dependencies = [ 362 | "rand_core 0.3.1", 363 | ] 364 | 365 | [[package]] 366 | name = "rawpointer" 367 | version = "0.2.1" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" 370 | 371 | [[package]] 372 | name = "rdrand" 373 | version = "0.4.0" 374 | source = "registry+https://github.com/rust-lang/crates.io-index" 375 | checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" 376 | dependencies = [ 377 | "rand_core 0.3.1", 378 | ] 379 | 380 | [[package]] 381 | name = "serde" 382 | version = "1.0.136" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" 385 | 386 | [[package]] 387 | name = "simple_injector" 388 | version = "0.1.1" 389 | source = "git+https://github.com/etra0/simple_injector#49b87aeabe61048f58a6a829f48cf4933d7ff742" 390 | dependencies = [ 391 | "memory-rs", 392 | "winapi", 393 | ] 394 | 395 | [[package]] 396 | name = "simplelog" 397 | version = "0.8.0" 398 | source = "registry+https://github.com/rust-lang/crates.io-index" 399 | checksum = "2b2736f58087298a448859961d3f4a0850b832e72619d75adc69da7993c2cd3c" 400 | dependencies = [ 401 | "chrono", 402 | "log", 403 | "termcolor", 404 | ] 405 | 406 | [[package]] 407 | name = "termcolor" 408 | version = "1.1.3" 409 | source = "registry+https://github.com/rust-lang/crates.io-index" 410 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 411 | dependencies = [ 412 | "winapi-util", 413 | ] 414 | 415 | [[package]] 416 | name = "time" 417 | version = "0.1.44" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" 420 | dependencies = [ 421 | "libc", 422 | "wasi", 423 | "winapi", 424 | ] 425 | 426 | [[package]] 427 | name = "toml" 428 | version = "0.5.9" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" 431 | dependencies = [ 432 | "serde", 433 | ] 434 | 435 | [[package]] 436 | name = "typenum" 437 | version = "1.15.0" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" 440 | 441 | [[package]] 442 | name = "wasi" 443 | version = "0.10.0+wasi-snapshot-preview1" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" 446 | 447 | [[package]] 448 | name = "winapi" 449 | version = "0.3.9" 450 | source = "registry+https://github.com/rust-lang/crates.io-index" 451 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 452 | dependencies = [ 453 | "winapi-i686-pc-windows-gnu", 454 | "winapi-x86_64-pc-windows-gnu", 455 | ] 456 | 457 | [[package]] 458 | name = "winapi-i686-pc-windows-gnu" 459 | version = "0.4.0" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 462 | 463 | [[package]] 464 | name = "winapi-util" 465 | version = "0.1.5" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 468 | dependencies = [ 469 | "winapi", 470 | ] 471 | 472 | [[package]] 473 | name = "winapi-x86_64-pc-windows-gnu" 474 | version = "0.4.0" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 477 | 478 | [[package]] 479 | name = "winres" 480 | version = "0.1.12" 481 | source = "registry+https://github.com/rust-lang/crates.io-index" 482 | checksum = "b68db261ef59e9e52806f688020631e987592bd83619edccda9c47d42cde4f6c" 483 | dependencies = [ 484 | "toml", 485 | ] 486 | 487 | [[package]] 488 | name = "yakuza0" 489 | version = "1.0.0" 490 | dependencies = [ 491 | "cc", 492 | "common", 493 | "memory-rs", 494 | "winapi", 495 | "winres", 496 | ] 497 | 498 | [[package]] 499 | name = "yakuza6" 500 | version = "0.1.0" 501 | dependencies = [ 502 | "cc", 503 | "log", 504 | "memory-rs", 505 | "nalgebra-glm", 506 | "simplelog", 507 | "termcolor", 508 | "winapi", 509 | "winres", 510 | ] 511 | 512 | [[package]] 513 | name = "yakuza6-freecam" 514 | version = "0.1.0" 515 | dependencies = [ 516 | "cc", 517 | "memory-rs", 518 | "simple_injector", 519 | "winres", 520 | "yakuza6", 521 | ] 522 | -------------------------------------------------------------------------------- /likeadragon/lib/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::clippy::missing_safety_doc)] 2 | pub mod globals; 3 | 4 | use crate::globals::*; 5 | use anyhow::{Context, Result}; 6 | use common::external::{error_message, Camera}; 7 | use common::internal::{handle_controller, Input}; 8 | use memory_rs::internal::injections::*; 9 | use memory_rs::internal::memory::{resolve_module_path}; 10 | use memory_rs::internal::process_info::ProcessInfo; 11 | use memory_rs::{generate_aob_pattern, try_winapi}; 12 | use std::io::prelude::*; 13 | use std::sync::atomic::Ordering; 14 | use winapi::shared::minwindef::LPVOID; 15 | use winapi::um::winuser::{self, GetAsyncKeyState}; 16 | use winapi::um::xinput; 17 | use nalgebra_glm as glm; 18 | 19 | use log::{error, info}; 20 | use simplelog::*; 21 | 22 | /// Structure parsed from the game. 23 | #[repr(C)] 24 | struct GameCamera { 25 | pos: [f32; 4], 26 | focus: [f32; 4], 27 | rot: [f32; 4], 28 | /// We simply skip 8 values because we don't know what they are. 29 | padding_: [f32; 0x8], 30 | fov: f32, 31 | } 32 | 33 | impl GameCamera { 34 | pub fn consume_input(&mut self, input: &Input) { 35 | let r_cam_x = self.focus[0] - self.pos[0]; 36 | let r_cam_y = self.focus[1] - self.pos[1]; 37 | let r_cam_z = self.focus[2] - self.pos[2]; 38 | 39 | let (r_cam_x, r_cam_z, r_cam_y) = Camera::calc_new_focus_point( 40 | r_cam_x, 41 | r_cam_z, 42 | r_cam_y, 43 | input.delta_focus.0, 44 | input.delta_focus.1, 45 | ); 46 | 47 | self.pos[0] += r_cam_x * input.delta_pos.1 + input.delta_pos.0 * r_cam_z; 48 | self.pos[1] += r_cam_y * input.delta_pos.1; 49 | 50 | self.pos[2] += r_cam_z * input.delta_pos.1 - input.delta_pos.0 * r_cam_x; 51 | 52 | self.focus[0] = self.pos[0] + r_cam_x; 53 | self.focus[1] = self.pos[1] + r_cam_y; 54 | self.focus[2] = self.pos[2] + r_cam_z; 55 | 56 | let focus_ = glm::vec3(self.focus[0], self.focus[1], self.focus[2]); 57 | let pos_ = glm::vec3(self.pos[0], self.pos[1], self.pos[2]); 58 | 59 | let result = Camera::calculate_rotation(focus_, pos_, input.delta_rotation); 60 | self.rot[0] = result[0]; 61 | self.rot[1] = result[1]; 62 | self.rot[2] = result[2]; 63 | 64 | self.fov = input.fov; 65 | } 66 | } 67 | 68 | impl std::fmt::Debug for GameCamera { 69 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 70 | let ptr = self as *const GameCamera as usize; 71 | f.debug_struct("GameCamera") 72 | .field("self", &format_args!("{:x}", ptr)) 73 | .field("pos", &self.pos) 74 | .field("focus", &self.focus) 75 | .field("rot", &self.rot) 76 | .field("fov", &self.fov) 77 | .finish() 78 | } 79 | } 80 | 81 | pub unsafe extern "system" fn wrapper(lib: LPVOID) -> u32 { 82 | // Logging initialization 83 | { 84 | let mut path = resolve_module_path(lib as _).unwrap(); 85 | path.push("ylad.log"); 86 | CombinedLogger::init(vec![ 87 | TermLogger::new( 88 | log::LevelFilter::Info, 89 | Config::default(), 90 | TerminalMode::Mixed, 91 | ), 92 | WriteLogger::new( 93 | log::LevelFilter::Info, 94 | Config::default(), 95 | std::fs::File::create(path).unwrap(), 96 | ), 97 | ]) 98 | .unwrap(); 99 | 100 | match patch(lib) { 101 | Ok(_) => { 102 | info!("Everything executed perfectly"); 103 | } 104 | Err(e) => { 105 | let msg = format!("{}", e); 106 | error!("Error: {}", msg); 107 | { 108 | use winapi::um::wincon::FreeConsole; 109 | (FreeConsole()); 110 | } 111 | error_message(&msg); 112 | } 113 | }; 114 | 115 | info!("Exiting"); 116 | } 117 | 118 | winapi::um::libloaderapi::FreeLibraryAndExitThread( 119 | lib as winapi::shared::minwindef::HMODULE, 120 | 0, 121 | ); 122 | 123 | 0 124 | } 125 | 126 | /// `use_xinput_from_game` is in charge to check if the pointer 127 | /// `controller_input_function` is already setted. If the pointer is different 128 | /// from zero, it will actually use the function, if it doesn't, will return 129 | /// an empty `XINPUT_STATE` struct. 130 | pub fn use_xinput_from_game(index: u32, xs: &mut xinput::XINPUT_STATE) -> u32 { 131 | let xstate: xinput::XINPUT_STATE = unsafe { std::mem::zeroed() }; 132 | let function_pointer = controller_input_function.load(Ordering::Relaxed); 133 | 134 | if function_pointer == 0 { 135 | unsafe { std::ptr::copy_nonoverlapping(&xstate, xs, 1) }; 136 | return 0; 137 | } 138 | 139 | let func: fn(u32, &mut xinput::XINPUT_STATE) -> u32 = 140 | unsafe { std::mem::transmute(function_pointer as *const u8) }; 141 | 142 | func(index, xs) 143 | } 144 | 145 | /// This function will be injected in the game, with the purpose of overriding 146 | /// the input getter. This function will use `use_xinput_from_game` to get 147 | /// the input. It'll also check if the camera is active, in the case it is, 148 | /// it will block all input except the pause button because when you alt-tab 149 | /// in the game, the game will pause. 150 | #[no_mangle] 151 | pub unsafe extern "system" fn xinput_interceptor(index: u32, xs: &mut xinput::XINPUT_STATE) -> u32 { 152 | let result = use_xinput_from_game(index, xs); 153 | 154 | if g_camera_active == 0 { 155 | return result; 156 | } 157 | // check if the pause button was pressed 158 | let buttons = (*xs).Gamepad.wButtons & 0x10; 159 | 160 | let mut gamepad: xinput::XINPUT_GAMEPAD = std::mem::zeroed(); 161 | gamepad.wButtons = buttons; 162 | 163 | if g_camera_active == 1 { 164 | std::ptr::copy_nonoverlapping(&gamepad, &mut (*xs).Gamepad, 1); 165 | } 166 | 167 | result 168 | } 169 | 170 | /// In charge of doing all the `Detour` injections type. 171 | fn inject_detourings(proc_inf: &ProcessInfo) -> Result> { 172 | macro_rules! auto_cast { 173 | ($val:expr) => { 174 | &$val as *const u8 as usize 175 | }; 176 | }; 177 | 178 | let mut detours = vec![]; 179 | 180 | unsafe { 181 | // ---- Camera func ---- 182 | let pat = generate_aob_pattern![ 183 | 0x90, 0xC5, 0xF8, 0x10, 0x07, 0xC5, 0xF8, 0x11, 0x86, 0x80, 0x00, 0x00, 0x00 184 | ]; 185 | 186 | let camera_func = Detour::new_from_aob( 187 | pat, 188 | &proc_inf.region, 189 | auto_cast!(asm_get_camera_data), 190 | Some(&mut g_get_camera_data), 191 | 15, 192 | Some(-0x33) 193 | ).with_context(|| "camera_func failed")?; 194 | 195 | info!("camera_func found: {:x}", camera_func.entry_point); 196 | detours.push(camera_func); 197 | // ---- 198 | 199 | // ---- Timestop ---- 200 | let pat = generate_aob_pattern![ 201 | 0xC4, 0xE1, 0xFA, 0x2C, 0xC0, 0x89, 0x05, _, _, _, _, 0xC5, 0x7A, 0x11, 0x05, _, _, _, 202 | _ 203 | ]; 204 | 205 | let timestop_ptr = proc_inf.region.scan_aob(&pat)?.with_context(|| "timestop issues")? + 0xB; 206 | 207 | 208 | g_get_timestop_rip = timestop_ptr; 209 | g_get_timestop_first_offset = *((timestop_ptr + 0x4) as *const u32) as usize; 210 | info!( 211 | "_get_timestop_first_offset: {:x}", 212 | g_get_timestop_first_offset 213 | ); 214 | 215 | let timestop_func = Detour::new( 216 | timestop_ptr, 217 | 16, 218 | auto_cast!(asm_get_timestop), 219 | Some(&mut g_get_timestop), 220 | ); 221 | 222 | info!("timestop_func found: {:x}", timestop_func.entry_point); 223 | detours.push(timestop_func); 224 | // ---- 225 | 226 | // ---- Controller handler 227 | let pat = generate_aob_pattern![ 228 | 0xE8, _, _, _, _, 0x85, 0xC0, 0x0F, 0x85, _, _, _, _, 0x48, 0x8B, 0x44, 0x24, 0x26, 229 | 0x48, 0x8B, 0x8C, 0x24, 0xD0, 0x00, 0x00, 0x00 230 | ]; 231 | 232 | let controller_blocker = Detour::new_from_aob( 233 | pat, 234 | &proc_inf.region, 235 | auto_cast!(asm_get_controller), 236 | Some(&mut g_get_controller), 237 | 15, 238 | Some(-0x8), 239 | )?; 240 | 241 | let controller_blocker_rip = controller_blocker.entry_point + 0x8; 242 | let controller_blocker_offset = *((controller_blocker_rip + 0x1) as *const u32) as usize; 243 | let function_pointer = controller_blocker_rip + controller_blocker_offset; 244 | controller_input_function.store(function_pointer, Ordering::Relaxed); 245 | 246 | info!( 247 | "controller_blocker found: {:x}", 248 | controller_blocker.entry_point 249 | ); 250 | detours.push(controller_blocker); 251 | } 252 | 253 | detours.iter_mut().inject(); 254 | info!("injections completed succesfully"); 255 | Ok(detours) 256 | } 257 | 258 | /// In charge of making all the `Injection` type of injections. 259 | fn make_injections(proc_inf: &ProcessInfo) -> Result> { 260 | let mut v = vec![]; 261 | 262 | let fov = Injection::new_from_aob( 263 | &proc_inf.region, 264 | vec![0x90; 6], 265 | generate_aob_pattern![ 266 | 0x89, 0x86, 0xD0, 0x00, 0x00, 0x00, 0x8B, 0x47, 0x54, 0x89, 0x86, 0xD4, 0x00, 0x00, 267 | 0x00 268 | ], 269 | ) 270 | .with_context(|| "FoV couldn't be found")?; 271 | info!("FoV was found at {:x}", fov.entry_point); 272 | v.push(fov); 273 | 274 | let no_ui = Injection::new_from_aob( 275 | &proc_inf.region, 276 | vec![0xC3], 277 | generate_aob_pattern![ 278 | 0x40, 0x55, 0x48, 0x83, 0xEC, 0x20, 0x80, 0xBA, 0xD4, 0x01, 0x00, 0x00, 0x00, 0x48, 279 | 0x8B, 0xEA, 0x0F, 0x84, _, _, _, _ 280 | ], 281 | ) 282 | .with_context(|| "no_ui couldn't be found")?; 283 | info!("no_ui was found at {:x}", no_ui.entry_point); 284 | v.push(no_ui); 285 | 286 | Ok(v) 287 | } 288 | 289 | fn write_ui_elements(proc_inf: &ProcessInfo) -> Result> { 290 | let pat = generate_aob_pattern![ 291 | 0xC5, 0xE8, 0x57, 0xD2, 0xC5, 0xF8, 0x57, 0xC0, 0x48, 0x8D, 0x54, 0x24, 0x20, 0xC5, 0xB0, 292 | 0x58, 0x08 293 | ]; 294 | 295 | let ptr = proc_inf.region.scan_aob(&pat)? 296 | .context("Couldn't find UI values")? 297 | + 0x11; 298 | 299 | let offset = (ptr + 0x2) as *const u32; 300 | let offset = unsafe { *offset }; 301 | let rip = ptr + 0x6; 302 | 303 | let base_addr_for_static_numbers = rip + (offset as usize); 304 | info!( 305 | "base_addr_for_static_numbers: {:x}", 306 | base_addr_for_static_numbers 307 | ); 308 | 309 | Ok(vec![ 310 | StaticElement::new(base_addr_for_static_numbers), 311 | StaticElement::new(base_addr_for_static_numbers + 0x4), 312 | StaticElement::new(base_addr_for_static_numbers + 0x24), 313 | ]) 314 | } 315 | 316 | #[allow(unreachable_code)] 317 | fn patch(_: LPVOID) -> Result<()> { 318 | #[cfg(feature = "non_automatic")] 319 | common::external::success_message("The injection was made succesfully"); 320 | 321 | #[cfg(debug_assertions)] 322 | unsafe { 323 | use winapi::um::consoleapi::AllocConsole; 324 | try_winapi!(AllocConsole()); 325 | } 326 | 327 | info!( 328 | "Yakuza Like A Dragon freecam v{} by @etra0", 329 | common::external::get_version() 330 | ); 331 | 332 | let proc_inf = ProcessInfo::new(None)?; 333 | info!("{:x?}", proc_inf); 334 | 335 | let mut active = false; 336 | 337 | let mut detours = inject_detourings(&proc_inf)?; 338 | let mut ui_elements: Vec = write_ui_elements(&proc_inf)?; 339 | let mut injections = make_injections(&proc_inf)?; 340 | 341 | let mut input = Input::new(); 342 | 343 | info!("Starting main loop"); 344 | 345 | loop { 346 | handle_controller(&mut input, use_xinput_from_game); 347 | input.sanitize(); 348 | 349 | #[cfg(debug_assertions)] 350 | if input.deattach || (unsafe { GetAsyncKeyState(winuser::VK_HOME) } as u32 & 0x8000) != 0 { 351 | break; 352 | } 353 | 354 | input.is_active = active; 355 | if input.change_active { 356 | active = !active; 357 | unsafe { 358 | g_camera_active = active as u8; 359 | } 360 | info!("Camera is {}", active); 361 | 362 | input.engine_speed = 1e-3; 363 | if active { 364 | injections.iter_mut().inject(); 365 | } else { 366 | ui_elements.iter_mut().remove_injection(); 367 | injections.iter_mut().remove_injection(); 368 | 369 | // We need to set g_camera_struct to 0 since 370 | // the camera struct can change depending on the scene. 371 | unsafe { 372 | g_camera_struct = 0; 373 | } 374 | } 375 | 376 | input.change_active = false; 377 | std::thread::sleep(std::time::Duration::from_millis(500)); 378 | } 379 | 380 | unsafe { 381 | if (g_camera_struct == 0x0) || !active { 382 | continue; 383 | } 384 | } 385 | 386 | let gc = unsafe { (g_camera_struct + 0x80) as *mut GameCamera }; 387 | 388 | unsafe { 389 | g_engine_speed = input.engine_speed; 390 | } 391 | ui_elements.iter_mut().inject(); 392 | 393 | unsafe { 394 | (*gc).consume_input(&input); 395 | println!("{:?}", input); 396 | } 397 | 398 | input.reset(); 399 | 400 | std::thread::sleep(std::time::Duration::from_millis(10)); 401 | } 402 | 403 | std::io::stdout().flush()?; 404 | 405 | info!("Dropping values"); 406 | detours.clear(); 407 | 408 | std::thread::sleep(std::time::Duration::from_secs(2)); 409 | 410 | #[cfg(debug_assertions)] 411 | unsafe { 412 | info!("Freeing console"); 413 | use winapi::um::wincon::FreeConsole; 414 | try_winapi!(FreeConsole()); 415 | } 416 | 417 | info!("Exiting library"); 418 | 419 | Ok(()) 420 | } 421 | 422 | memory_rs::main_dll!(wrapper); 423 | --------------------------------------------------------------------------------