From f84dee9d9bc90c1fd1365e35e16ae66573536672 Mon Sep 17 00:00:00 2001 From: ACK72 Date: Thu, 14 Sep 2023 20:45:30 +0900 Subject: [PATCH] initial release --- .gitignore | 20 +++++++++ Cargo.toml | 12 +++++ LICENSE.txt | 24 ++++++++++ README.md | 16 +++++++ src/main.rs | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 196 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 LICENSE.txt create mode 100644 README.md create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..097d2d4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +# Created by https://www.toptal.com/developers/gitignore/api/rust +# Edit at https://www.toptal.com/developers/gitignore?templates=rust + +### Rust ### +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +# End of https://www.toptal.com/developers/gitignore/api/rust \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..653dce4 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "PlayBridge" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +image = "*" +screenshots = "*" +spin_sleep = "*" +windows = {version = "*", features = ["Win32_Foundation", "Win32_UI_WindowsAndMessaging"]} \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..faba8e8 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,24 @@ +BSD 2-Clause License + +Copyright (c) 2023, ACK72 + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..c08602c --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# PlayBridge + +Simple ADB emulator for Google Play Games, especially for Arknights & MAA + +## Download + +- You can get latest builds at [release page](https://github.com/ACK72/PlayBridge/releases/latest) + +## Set-Up +![setting](https://github.com/ACK72/PlayBridge/assets/25812442/69f980b6-7c9e-4a93-b1b5-f2a21c1b0680) + +- You don't need to consider about connection address + +## Limitations + +- Currently, only supports in korean windows (You can customize and build your own) diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..9c3f1ef --- /dev/null +++ b/src/main.rs @@ -0,0 +1,124 @@ +#![allow(non_snake_case)] +use std::{env, io::*, time::Duration}; +use image::{codecs::png::PngEncoder, DynamicImage}; +use screenshots::Screen; +use windows::{ + core::*, Win32::Foundation::*, Win32::UI::WindowsAndMessaging::* +}; + +fn main() { + let args: Vec = env::args().collect(); + let command = args.join(" "); + + if command.contains("connect") { + let mut stdout = stdout().lock(); + stdout.write_all(b"connected to Google Play Games").unwrap(); + } + + if command.contains("shell input tap") { + let x = args[6].parse::().unwrap(); + let y = args[7].parse::().unwrap(); + + input_tap(x, y); + } + + if command.contains("shell input swipe") { + let x1 = args[6].parse::().unwrap(); + let y1 = args[7].parse::().unwrap(); + let x2 = args[8].parse::().unwrap(); + let y2 = args[9].parse::().unwrap(); + let dur = args[10].parse::().unwrap(); + + input_swipe(x1, y1, x2, y2, dur); + } + + if command.contains("shell input keyevent 111") { + input_keyevent(0x01); + } + + if command.contains("shell dumpsys window displays") { + let (x, y) = get_screen_size(); + + let mut stdout = stdout().lock(); + stdout.write_all(format!("{x}\n{y}").as_bytes()).unwrap(); + } + + if command.contains("exec-out screencap -p") { + let image = get_screen_capture(); + + let mut stdout = stdout().lock(); + image.write_with_encoder(PngEncoder::new(&mut stdout)).unwrap(); + } +} + +fn get_screen_size() -> (i32, i32) { + let hwnd = unsafe { FindWindowW(PCWSTR::null(), w!("명일방주")) }; + let mut rect = RECT::default(); + let _ = unsafe { GetWindowRect(hwnd, &mut rect) }; + + (rect.right - rect.left, rect.bottom - rect.top) +} + +fn input_tap(x: i32, y: i32) { + let hwnd = unsafe { FindWindowW(PCWSTR::null(), w!("명일방주")) }; + + let pos = (y << 16 | x) as isize; + + unsafe { + let _ = SendMessageA(hwnd, WM_LBUTTONDOWN, WPARAM(1), LPARAM(pos)); + let _ = SendMessageA(hwnd, WM_LBUTTONUP, WPARAM(1), LPARAM(pos)); + } +} + +fn input_swipe(x1: i32, y1: i32, x2: i32, y2: i32, dur: i32) { + let hwnd = unsafe { FindWindowW(PCWSTR::null(), w!("명일방주")) }; + + let polling = 1000 / 250; + let times = dur as f32 / polling as f32; + + let dx = ((x2 - x1) as f32) / times; + let dy = ((y2 - y1) as f32) / times; + + unsafe { + let mut count = 0f32; + loop { + if count >= times { + break; + } + + let pos = ((y1 + (dy * count as f32) as i32) << 16 | (x1 + (dx * count as f32) as i32)) as isize; + let _ = SendMessageA(hwnd, WM_LBUTTONDOWN, WPARAM(1), LPARAM(pos)); + + spin_sleep::sleep(Duration::new(0, polling * 1000000)); + count += 1.0; + } + + let pos = (y2 << 16 | x2) as isize; + let _ = SendMessageA(hwnd, WM_LBUTTONUP, WPARAM(1), LPARAM(pos)); + } +} + +fn input_keyevent(keycode: i32) { + let hwnd = unsafe { FindWindowW(PCWSTR::null(), w!("명일방주")) }; + + let wparam = WPARAM(keycode as usize); + let down = LPARAM((keycode << 16) as isize); + let up = LPARAM((keycode << 16 | 1 << 30 | 1 << 31) as isize); + + unsafe { + let _ = SendMessageA(hwnd, WM_KEYDOWN, wparam, down); + let _ = SendMessageA(hwnd, WM_KEYUP, wparam, up); + } +} + +fn get_screen_capture() -> DynamicImage { + let hwnd = unsafe { FindWindowW(PCWSTR::null(), w!("명일방주")) }; + + let mut rect = RECT::default(); + let _ = unsafe { GetWindowRect(hwnd, &mut rect) }; + + let screen = Screen::from_point((rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2).unwrap(); + let capture = screen.capture().unwrap(); + + image::DynamicImage::ImageRgba8(capture) +} \ No newline at end of file