Init, innit?
Victor Bjelkholm 19 hours ago 11 files (+1425, -0)
ADDED
.gitignore
ADDED
.gitignore
@@ -0,0 +1,1 @@+/target
ADDED
Cargo.lock
ADDED
Cargo.lock
@@ -0,0 +1,677 @@+# This file is automatically @generated by Cargo.+# It is not intended for manual editing.+version = 4++[[package]]+name = "aho-corasick"+version = "1.1.3"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"+dependencies = [+ "memchr",+]++[[package]]+name = "anstream"+version = "0.6.18"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"+dependencies = [+ "anstyle",+ "anstyle-parse",+ "anstyle-query",+ "anstyle-wincon",+ "colorchoice",+ "is_terminal_polyfill",+ "utf8parse",+]++[[package]]+name = "anstyle"+version = "1.0.10"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"++[[package]]+name = "anstyle-parse"+version = "0.2.6"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"+dependencies = [+ "utf8parse",+]++[[package]]+name = "anstyle-query"+version = "1.1.2"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"+dependencies = [+ "windows-sys 0.59.0",+]++[[package]]+name = "anstyle-wincon"+version = "3.0.7"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"+dependencies = [+ "anstyle",+ "once_cell",+ "windows-sys 0.59.0",+]++[[package]]+name = "anyhow"+version = "1.0.96"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4"++[[package]]+name = "bitflags"+version = "2.9.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"++[[package]]+name = "byteorder"+version = "1.5.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"++[[package]]+name = "cfg-if"+version = "1.0.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"++[[package]]+name = "cfg_aliases"+version = "0.2.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"++[[package]]+name = "clap"+version = "4.5.31"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767"+dependencies = [+ "clap_builder",+ "clap_derive",+]++[[package]]+name = "clap_builder"+version = "4.5.31"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863"+dependencies = [+ "anstream",+ "anstyle",+ "clap_lex",+ "strsim",+]++[[package]]+name = "clap_derive"+version = "4.5.28"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed"+dependencies = [+ "heck",+ "proc-macro2",+ "quote",+ "syn",+]++[[package]]+name = "clap_lex"+version = "0.7.4"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"++[[package]]+name = "clipboard-win"+version = "5.4.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892"+dependencies = [+ "error-code",+]++[[package]]+name = "colorchoice"+version = "1.0.3"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"++[[package]]+name = "crossbeam-deque"+version = "0.8.6"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"+dependencies = [+ "crossbeam-epoch",+ "crossbeam-utils",+]++[[package]]+name = "crossbeam-epoch"+version = "0.9.18"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"+dependencies = [+ "crossbeam-utils",+]++[[package]]+name = "crossbeam-utils"+version = "0.8.21"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"++[[package]]+name = "either"+version = "1.14.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d"++[[package]]+name = "endian-type"+version = "0.1.2"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"++[[package]]+name = "env_filter"+version = "0.1.3"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0"+dependencies = [+ "log",+ "regex",+]++[[package]]+name = "env_logger"+version = "0.11.6"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0"+dependencies = [+ "anstream",+ "anstyle",+ "env_filter",+ "humantime",+ "log",+]++[[package]]+name = "errno"+version = "0.3.10"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"+dependencies = [+ "libc",+ "windows-sys 0.59.0",+]++[[package]]+name = "error-code"+version = "3.3.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f"++[[package]]+name = "fd-lock"+version = "4.0.2"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947"+dependencies = [+ "cfg-if",+ "rustix",+ "windows-sys 0.52.0",+]++[[package]]+name = "getrandom"+version = "0.3.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"+dependencies = [+ "cfg-if",+ "libc",+ "wasi",+ "windows-targets",+]++[[package]]+name = "heck"+version = "0.5.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"++[[package]]+name = "home"+version = "0.5.11"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"+dependencies = [+ "windows-sys 0.59.0",+]++[[package]]+name = "humantime"+version = "2.1.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"++[[package]]+name = "is_terminal_polyfill"+version = "1.70.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"++[[package]]+name = "libc"+version = "0.2.170"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828"++[[package]]+name = "linux-raw-sys"+version = "0.4.15"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"++[[package]]+name = "log"+version = "0.4.26"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"++[[package]]+name = "memchr"+version = "2.7.4"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"++[[package]]+name = "memmut"+version = "0.1.0"+dependencies = [+ "anyhow",+ "clap",+ "env_logger",+ "libc",+ "log",+ "rand",+ "rayon",+ "rustyline",+]++[[package]]+name = "nibble_vec"+version = "0.1.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43"+dependencies = [+ "smallvec",+]++[[package]]+name = "nix"+version = "0.29.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"+dependencies = [+ "bitflags",+ "cfg-if",+ "cfg_aliases",+ "libc",+]++[[package]]+name = "once_cell"+version = "1.20.3"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"++[[package]]+name = "ppv-lite86"+version = "0.2.20"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"+dependencies = [+ "zerocopy 0.7.35",+]++[[package]]+name = "proc-macro2"+version = "1.0.93"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"+dependencies = [+ "unicode-ident",+]++[[package]]+name = "quote"+version = "1.0.38"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"+dependencies = [+ "proc-macro2",+]++[[package]]+name = "radix_trie"+version = "0.2.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd"+dependencies = [+ "endian-type",+ "nibble_vec",+]++[[package]]+name = "rand"+version = "0.9.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"+dependencies = [+ "rand_chacha",+ "rand_core",+ "zerocopy 0.8.21",+]++[[package]]+name = "rand_chacha"+version = "0.9.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"+dependencies = [+ "ppv-lite86",+ "rand_core",+]++[[package]]+name = "rand_core"+version = "0.9.3"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"+dependencies = [+ "getrandom",+]++[[package]]+name = "rayon"+version = "1.10.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"+dependencies = [+ "either",+ "rayon-core",+]++[[package]]+name = "rayon-core"+version = "1.12.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"+dependencies = [+ "crossbeam-deque",+ "crossbeam-utils",+]++[[package]]+name = "regex"+version = "1.11.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"+dependencies = [+ "aho-corasick",+ "memchr",+ "regex-automata",+ "regex-syntax",+]++[[package]]+name = "regex-automata"+version = "0.4.9"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"+dependencies = [+ "aho-corasick",+ "memchr",+ "regex-syntax",+]++[[package]]+name = "regex-syntax"+version = "0.8.5"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"++[[package]]+name = "rustix"+version = "0.38.44"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"+dependencies = [+ "bitflags",+ "errno",+ "libc",+ "linux-raw-sys",+ "windows-sys 0.59.0",+]++[[package]]+name = "rustyline"+version = "15.0.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "2ee1e066dc922e513bda599c6ccb5f3bb2b0ea5870a579448f2622993f0a9a2f"+dependencies = [+ "bitflags",+ "cfg-if",+ "clipboard-win",+ "fd-lock",+ "home",+ "libc",+ "log",+ "memchr",+ "nix",+ "radix_trie",+ "unicode-segmentation",+ "unicode-width",+ "utf8parse",+ "windows-sys 0.59.0",+]++[[package]]+name = "smallvec"+version = "1.14.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"++[[package]]+name = "strsim"+version = "0.11.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"++[[package]]+name = "syn"+version = "2.0.98"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"+dependencies = [+ "proc-macro2",+ "quote",+ "unicode-ident",+]++[[package]]+name = "unicode-ident"+version = "1.0.17"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe"++[[package]]+name = "unicode-segmentation"+version = "1.12.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"++[[package]]+name = "unicode-width"+version = "0.2.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"++[[package]]+name = "utf8parse"+version = "0.2.2"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"++[[package]]+name = "wasi"+version = "0.13.3+wasi-0.2.2"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"+dependencies = [+ "wit-bindgen-rt",+]++[[package]]+name = "windows-sys"+version = "0.52.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"+dependencies = [+ "windows-targets",+]++[[package]]+name = "windows-sys"+version = "0.59.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"+dependencies = [+ "windows-targets",+]++[[package]]+name = "windows-targets"+version = "0.52.6"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"+dependencies = [+ "windows_aarch64_gnullvm",+ "windows_aarch64_msvc",+ "windows_i686_gnu",+ "windows_i686_gnullvm",+ "windows_i686_msvc",+ "windows_x86_64_gnu",+ "windows_x86_64_gnullvm",+ "windows_x86_64_msvc",+]++[[package]]+name = "windows_aarch64_gnullvm"+version = "0.52.6"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"++[[package]]+name = "windows_aarch64_msvc"+version = "0.52.6"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"++[[package]]+name = "windows_i686_gnu"+version = "0.52.6"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"++[[package]]+name = "windows_i686_gnullvm"+version = "0.52.6"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"++[[package]]+name = "windows_i686_msvc"+version = "0.52.6"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"++[[package]]+name = "windows_x86_64_gnu"+version = "0.52.6"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"++[[package]]+name = "windows_x86_64_gnullvm"+version = "0.52.6"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"++[[package]]+name = "windows_x86_64_msvc"+version = "0.52.6"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"++[[package]]+name = "wit-bindgen-rt"+version = "0.33.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"+dependencies = [+ "bitflags",+]++[[package]]+name = "zerocopy"+version = "0.7.35"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"+dependencies = [+ "byteorder",+ "zerocopy-derive 0.7.35",+]++[[package]]+name = "zerocopy"+version = "0.8.21"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "dcf01143b2dd5d134f11f545cf9f1431b13b749695cb33bcce051e7568f99478"+dependencies = [+ "zerocopy-derive 0.8.21",+]++[[package]]+name = "zerocopy-derive"+version = "0.7.35"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"+dependencies = [+ "proc-macro2",+ "quote",+ "syn",+]++[[package]]+name = "zerocopy-derive"+version = "0.8.21"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "712c8386f4f4299382c9abee219bee7084f78fb939d88b6840fcc1320d5f6da2"+dependencies = [+ "proc-macro2",+ "quote",+ "syn",+]
ADDED
Cargo.toml
ADDED
Cargo.toml
@@ -0,0 +1,14 @@+[package]+name = "memmut"+version = "0.1.0"+edition = "2024"++[dependencies]+anyhow = "1.0.96"+clap = {version = "4.5.31", features = ["derive"]}+env_logger = "0.11.6"+libc = "0.2.170"+log = "0.4.26"+rand = "0.9.0"+rayon = "1.10.0"+rustyline = "15.0.0"
ADDED
examples/pointers.rs
ADDED
examples/pointers.rs
@@ -0,0 +1,129 @@+use rand::Rng;+use std::{+ io::{self, Read, Write},+ sync::mpsc,+ thread,+ time::Duration,+};++// This is a practice program to test if the main programs behaviour works fine+// This program has been validated and confirmed to work as expected with scanmem++// Structure to hold the "real" game state+struct GameState {+ // Internal values that control the actual game state+ values: Vec<i32>,+}++// Structure to manage what's displayed on screen+struct GameDisplay {+ // Pointers to copies of values shown to the player+ display_values: Vec<Box<i32>>,+}++impl GameState {+ fn new(count: usize, rng: &mut impl Rng) -> Self {+ let mut values = Vec::with_capacity(count);+ for _ in 0..count {+ values.push(rng.gen_range(1..10));+ }+ Self { values }+ }++ fn get_value(&self, index: usize) -> i32 {+ self.values[index]+ }++ fn set_value(&mut self, index: usize, value: i32) {+ self.values[index] = value;+ }+}++impl GameDisplay {+ fn new(count: usize) -> Self {+ let mut display_values = Vec::with_capacity(count);+ for _ in 0..count {+ display_values.push(Box::new(0));+ }+ Self { display_values }+ }++ fn update_from_state(&mut self, state: &GameState) {+ for (i, val) in state.values.iter().enumerate() {+ *self.display_values[i] = *val;+ }+ }++ fn get_display_ptr(&self, index: usize) -> *const i32 {+ &*self.display_values[index] as *const i32+ }++ fn get_displayed_value(&self, index: usize) -> i32 {+ *self.display_values[index]+ }+}++fn main() {+ let mut rng = rand::thread_rng();++ const LEN: usize = 10;++ let mut game_state = GameState::new(LEN, &mut rng);+ let mut game_display = GameDisplay::new(LEN);++ game_display.update_from_state(&game_state);++ let (tx, rx) = mpsc::channel();++ thread::spawn(move || {+ let mut buffer = [0; 1];+ loop {+ if let Ok(_) = io::stdin().read_exact(&mut buffer) {+ let _ = tx.send(());+ }+ }+ });++ let mut refresh_counter = 0;++ loop {+ print!("\x1B[2J\x1B[1;1H");++ refresh_counter += 1;+ if refresh_counter >= 3 {+ refresh_counter = 0;+ game_display.update_from_state(&game_state);+ println!("(Display refreshed from game state)");+ }++ println!(+ "Memory Scanner Target (Realistic Game Structure) - Press ENTER for new values, Ctrl+C to quit"+ );+ println!("Displayed values (what you see on screen):");++ for i in 0..LEN {+ let display_value = game_display.get_displayed_value(i);+ let display_ptr = game_display.get_display_ptr(i);+ println!(+ "Value {}: {} (displayed at {:p})",+ i, display_value, display_ptr+ );+ }++ println!("\nInternal game state (would be hidden in a real game):");+ for i in 0..LEN {+ println!("Value {}: {}", i, game_state.get_value(i));+ }++ println!("\nPress ENTER to change all values...");+ io::stdout().flush().unwrap();++ if rx.recv_timeout(Duration::from_secs(1)).is_ok() {+ for i in 0..LEN {+ game_state.set_value(i, rng.gen_range(1..10));+ }+ game_display.update_from_state(&game_state);+ refresh_counter = 0;+ }+ }+}
ADDED
examples/simple.rs
ADDED
examples/simple.rs
@@ -0,0 +1,55 @@+use rand::Rng;+use std::{+ io::{self, Read, Write},+ sync::mpsc,+ thread,+ time::Duration,+};++// This is a practice program to test if the main programs behaviour works fine+// This program has been validated and confirmed to work as expected with scanmem++fn main() {+ let mut rng = rand::rng();++ // Create an array of 10 random numbers+ // const LEN: usize = 10_000_000;+ const LEN: usize = 1000;++ let mut numbers: Vec<i32> = vec![0; LEN];++ for i in 0..LEN {+ numbers[i] = rng.random_range(1..10);+ }++ let (tx, rx) = mpsc::channel();++ thread::spawn(move || {+ let mut buffer = [0; 1];+ loop {+ if let Ok(_) = io::stdin().read_exact(&mut buffer) {+ let _ = tx.send(());+ }+ }+ });++ loop {+ print!("\x1B[2J\x1B[1;1H");++ println!("Memory Scanner Target - Press ENTER for new values, Ctrl+C to quit");+ println!("Current values:");++ for (i, &num) in numbers.iter().take(10).enumerate() {+ println!("Index {}: {}", i, num);+ }++ println!("\nPress ENTER to generate new values...");+ io::stdout().flush().unwrap();++ if rx.recv_timeout(Duration::from_secs(1)).is_ok() {+ for i in 0..LEN {+ numbers[i] = rng.random_range(1..10);+ }+ }+ }+}
ADDED
projectory.toml
ADDED
projectory.toml
@@ -0,0 +1,4 @@+name = "memmut"+description = "scanmem alternative with concurrent memory scanning"+tags = ["cli", "utility"]+langs = ["rust"]
ADDED
src/cli.rs
ADDED
src/cli.rs
@@ -0,0 +1,118 @@+use anyhow::Result;+use rustyline::Editor;+use rustyline::error::ReadlineError;++use crate::process::Process;+use crate::scanner::{MemoryScanner, ScanValueType};++pub fn run_interactive_mode(process_name: &str) -> Result<()> {+ println!(+ "# Scanning for process \"{}\" like how pgrep -f works",+ process_name+ );++ let process = Process::find_by_name(process_name)?;+ let mut scanner = MemoryScanner::new(process)?;++ println!("Please enter current value, or \"help\" for other commands.");++ let mut rl = Editor::<(), rustyline::history::DefaultHistory>::new()?;+ let mut first_scan = true;++ loop {+ let prompt = if first_scan {+ "> ".to_string()+ } else {+ format!("{}> ", scanner.get_matches_count())+ };++ let readline = rl.readline(&prompt);+ match readline {+ Ok(line) => {+ let _ = rl.add_history_entry(&line);+ let line = line.trim();++ if line.is_empty() {+ continue;+ }++ if line == "help" {+ print_help();+ continue;+ }++ if line == "exit" || line == "quit" {+ break;+ }++ if line.starts_with("set ") {+ handle_set_command(&mut scanner, line)?;+ continue;+ }+ match parse_value(line) {+ Ok(value) => {+ if first_scan {+ match scanner.scan_for_value(value) {+ Ok(_) => first_scan = false,+ Err(e) => println!("Error scanning: {}", e),+ }+ } else {+ match scanner.filter_matches(value) {+ Ok(_) => {}+ Err(e) => println!("Error filtering: {}", e),+ }+ }+ }+ Err(e) => println!("Invalid value: {}", e),+ }+ }+ Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => {+ println!("Exiting...");+ break;+ }+ Err(err) => {+ println!("Error: {}", err);+ break;+ }+ }+ }++ Ok(())+}++fn print_help() {+ println!("Available commands:");+ println!(" <value> - Search for value or narrow down existing results");+ println!(" set <idx> <value> - Set value at the memory address at index <idx>");+ println!(" help - Show this help message");+ println!(" exit/quit - Exit the program");+}++fn handle_set_command(scanner: &mut MemoryScanner, command: &str) -> Result<()> {+ let parts: Vec<&str> = command.split_whitespace().collect();+ if parts.len() != 3 {+ anyhow::bail!("Invalid set command format. Use: set <idx> <value>");+ }++ let idx = match parts[1].parse::<usize>() {+ Ok(idx) => idx,+ Err(_) => anyhow::bail!("Invalid index: {}", parts[1]),+ };++ let value = match parse_value(parts[2])? {+ ScanValueType::I32(val) => val,+ _ => anyhow::bail!("Only i32 values are currently supported for setting"),+ };++ scanner.set_value(idx, value)+}++fn parse_value(value_str: &str) -> Result<ScanValueType> {+ if let Ok(val) = value_str.parse::<i32>() {+ return Ok(ScanValueType::I32(val));+ }++ // TODO missing implementation for all the other types++ anyhow::bail!("Could not parse '{}' as a valid number", value_str)+}
ADDED
src/main.rs
ADDED
src/main.rs
@@ -0,0 +1,28 @@+mod cli;+mod memory;+mod process;+mod scanner;++use anyhow::Result;+use clap::Parser;+use log::error;++#[derive(Parser)]+#[command(author, version, about, long_about = None)]+struct Args {+ /// Process name to attach to+ process_name: String,+}++fn main() -> Result<()> {+ env_logger::init();+ let args = Args::parse();++ match cli::run_interactive_mode(&args.process_name) {+ Ok(_) => Ok(()),+ Err(e) => {+ error!("Error: {}", e);+ Err(e)+ }+ }+}
ADDED
src/memory.rs
ADDED
src/memory.rs
@@ -0,0 +1,83 @@+use anyhow::Result;+use libc::{c_void, iovec, process_vm_readv, process_vm_writev};+use log::info;++use crate::process::{MemoryRegion, Process};++pub struct MemoryReader {+ process: Process,+}++impl MemoryReader {+ pub fn new(process: Process) -> Self {+ Self { process }+ }++ pub fn read_memory(&self, address: usize, size: usize) -> Result<Vec<u8>> {+ let mut buffer = vec![0u8; size];++ let local_iov = iovec {+ iov_base: buffer.as_mut_ptr() as *mut c_void,+ iov_len: size,+ };++ let remote_iov = iovec {+ iov_base: address as *mut c_void,+ iov_len: size,+ };++ let result = unsafe {+ process_vm_readv(+ self.process.pid as libc::pid_t,+ &local_iov,+ 1,+ &remote_iov,+ 1,+ 0,+ )+ };++ if result == -1 {+ let err = std::io::Error::last_os_error();+ anyhow::bail!("Failed to read memory at 0x{:x}: {}", address, err);+ }++ Ok(buffer)+ }++ pub fn write_memory(&self, address: usize, data: &[u8]) -> Result<()> {+ let local_iov = iovec {+ iov_base: data.as_ptr() as *mut c_void,+ iov_len: data.len(),+ };++ let remote_iov = iovec {+ iov_base: address as *mut c_void,+ iov_len: data.len(),+ };++ let result = unsafe {+ process_vm_writev(+ self.process.pid as libc::pid_t,+ &local_iov,+ 1,+ &remote_iov,+ 1,+ 0,+ )+ };++ if result == -1 {+ let err = std::io::Error::last_os_error();+ anyhow::bail!("Failed to write memory at 0x{:x}: {}", address, err);+ }++ info!("setting *0x{:x} to {:?}...", address, data);+ Ok(())+ }++ pub fn read_region(&self, region: &MemoryRegion) -> Result<Vec<u8>> {+ let size = region.end_addr - region.start_addr;+ self.read_memory(region.start_addr, size)+ }+}
ADDED
src/process.rs
ADDED
src/process.rs
@@ -0,0 +1,109 @@+use anyhow::Result;+use log::info;+use std::fs;+use std::path::Path;+use std::str::FromStr;++pub struct Process {+ pub pid: i32,+ #[allow(dead_code)]+ pub name: String,+}++#[derive(Debug, Clone)]+pub struct MemoryRegion {+ pub start_addr: usize,+ pub end_addr: usize,+ #[allow(dead_code)]+ pub permissions: String,+ #[allow(dead_code)]+ pub offset: usize,+ #[allow(dead_code)]+ pub pathname: String,+}++impl Process {+ pub fn find_by_name(name: &str) -> Result<Self> {+ // Search for process using pgrep-like functionality+ let proc_dir = Path::new("/proc");+ let mut found_pid = None;++ for entry in fs::read_dir(proc_dir)? {+ let entry = entry?;+ let path = entry.path();++ if !path.is_dir() {+ continue;+ }++ let file_name = path.file_name().unwrap().to_string_lossy();+ if !file_name.chars().all(|c| c.is_digit(10)) {+ continue;+ }++ let pid = i32::from_str(&file_name)?;+ let cmdline_path = path.join("cmdline");++ if !cmdline_path.exists() {+ continue;+ }++ let cmdline = fs::read_to_string(cmdline_path)?;+ if cmdline.contains(name) {+ println!("Process found with PID: {}", pid);+ found_pid = Some(pid);+ break;+ }+ }++ match found_pid {+ Some(pid) => Ok(Process {+ pid,+ name: name.to_string(),+ }),+ None => anyhow::bail!("Process '{}' not found", name),+ }+ }++ pub fn get_memory_maps(&self) -> Result<Vec<MemoryRegion>> {+ let maps_path = format!("/proc/{}/maps", self.pid);+ info!("maps file located at {} opened.", maps_path);++ let maps_content = fs::read_to_string(&maps_path)?;+ let mut regions = Vec::new();++ for line in maps_content.lines() {+ let parts: Vec<&str> = line.split_whitespace().collect();+ if parts.len() < 5 {+ continue;+ }++ let addr_range: Vec<&str> = parts[0].split('-').collect();+ if addr_range.len() != 2 {+ continue;+ }++ let start_addr = usize::from_str_radix(addr_range[0], 16)?;+ let end_addr = usize::from_str_radix(addr_range[1], 16)?;+ let permissions = parts[1].to_string();++ // Only add regions with read permission+ if permissions.contains('r') {+ regions.push(MemoryRegion {+ start_addr,+ end_addr,+ permissions,+ offset: usize::from_str_radix(parts[2], 16)?,+ pathname: if parts.len() > 5 {+ parts[5..].join(" ")+ } else {+ String::new()+ },+ });+ }+ }++ info!("{} suitable regions found.", regions.len());+ Ok(regions)+ }+}
ADDED
src/scanner.rs
ADDED
src/scanner.rs
@@ -0,0 +1,207 @@+use anyhow::Result;+use log::{debug, info};+use rayon::prelude::*;+use std::sync::{Arc, Mutex};++use crate::memory::MemoryReader;+use crate::process::{MemoryRegion, Process};++#[derive(Clone, Debug)]+pub enum ScanValueType {+ #[allow(dead_code)]+ U8(u8),+ #[allow(dead_code)]+ U16(u16),+ #[allow(dead_code)]+ U32(u32),+ #[allow(dead_code)]+ U64(u64),+ #[allow(dead_code)]+ I8(i8),+ #[allow(dead_code)]+ I16(i16),+ I32(i32),+ #[allow(dead_code)]+ I64(i64),+ #[allow(dead_code)]+ F32(f32),+ #[allow(dead_code)]+ F64(f64),+}++#[derive(Clone, Debug)]+pub struct MemoryMatch {+ pub address: usize,+ #[allow(dead_code)]+ pub value_type: ScanValueType,+}++pub struct MemoryScanner {+ reader: MemoryReader,+ regions: Vec<MemoryRegion>,+ matches: Vec<MemoryMatch>,+}++impl MemoryScanner {+ pub fn new(process: Process) -> Result<Self> {+ let regions = process.get_memory_maps()?;+ let reader = MemoryReader::new(process);++ println!(+ "Successfully attached to process. Found {} readable memory regions.",+ regions.len()+ );++ Ok(Self {+ reader,+ regions,+ matches: Vec::new(),+ })+ }++ pub fn scan_for_value(&mut self, value: ScanValueType) -> Result<()> {+ let matches = Arc::new(Mutex::new(Vec::new()));++ self.regions+ .par_iter()+ .enumerate()+ .for_each(|(idx, region)| {+ println!(+ "{:02}/{:03} searching {:x} - {:x}...",+ idx + 1,+ self.regions.len(),+ region.start_addr,+ region.end_addr+ );++ match self.scan_region(region, &value) {+ Ok(region_matches) => {+ let mut matches_guard = matches.lock().unwrap();+ matches_guard.extend(region_matches);+ }+ Err(e) => {+ debug!(+ "Error scanning region {:x}-{:x}: {}",+ region.start_addr, region.end_addr, e+ );+ }+ }++ print!("........ok\n");+ });++ self.matches = Arc::try_unwrap(matches)+ .expect("Failed to unwrap Arc")+ .into_inner()+ .expect("Failed to unlock Mutex");++ info!("we currently have {} matches.", self.matches.len());+ Ok(())+ }++ pub fn get_matches_count(&self) -> usize {+ self.matches.len()+ }++ fn scan_region(+ &self,+ region: &MemoryRegion,+ value: &ScanValueType,+ ) -> Result<Vec<MemoryMatch>> {+ // Basic implementation that scans a single region for matches+ let mut region_matches = Vec::new();++ match self.reader.read_region(region) {+ Ok(memory) => {+ // Simple implementation searching for i32 values+ if let ScanValueType::I32(target) = value {+ let target_bytes = target.to_ne_bytes();++ for i in (0..memory.len().saturating_sub(4)).step_by(4) {+ let mut matches = true;+ for j in 0..4 {+ if memory[i + j] != target_bytes[j] {+ matches = false;+ break;+ }+ }++ if matches {+ region_matches.push(MemoryMatch {+ address: region.start_addr + i,+ value_type: value.clone(),+ });+ }+ }+ }+ // TODO missing handlers for all other types+ }+ Err(e) => {+ println!(+ "Error reading region {:x}-{:x}: {}",+ region.start_addr, region.end_addr, e+ );+ }+ }++ Ok(region_matches)+ }+ pub fn filter_matches(&mut self, value: ScanValueType) -> Result<()> {+ let old_matches = std::mem::take(&mut self.matches);++ for m in old_matches {+ let addr = m.address;++ let data = match &value {+ ScanValueType::I32(_) => {+ match self.reader.read_memory(addr, 4) {+ Ok(data) => data,+ Err(_) => continue, // Skip if we can't read+ }+ }+ _ => continue,+ };++ let matched = match &value {+ ScanValueType::I32(target) => {+ if data.len() >= 4 {+ let value_bytes = target.to_ne_bytes();+ data[0] == value_bytes[0]+ && data[1] == value_bytes[1]+ && data[2] == value_bytes[2]+ && data[3] == value_bytes[3]+ } else {+ false+ }+ }+ _ => false,+ };++ if matched {+ self.matches.push(MemoryMatch {+ address: addr,+ value_type: value.clone(),+ });+ }+ }++ println!("..........ok");+ info!("we currently have {} matches.", self.matches.len());+ Ok(())+ }++ pub fn set_value(&self, index: usize, value: i32) -> Result<()> {+ if index >= self.matches.len() {+ anyhow::bail!(+ "Index {} out of range (have {} matches)",+ index,+ self.matches.len()+ );+ }++ let address = self.matches[index].address;+ let data = value.to_ne_bytes();++ self.reader.write_memory(address, &data)+ }+}