Reads the game's process memory read-only (AOB signature scanning, re-resolved every tick — never a cached pointer) and serves a live death counter as a browser-source overlay plus a status panel. Supports co-op: each player runs the program locally, one acts as hub. The peer link is authenticated (HMAC challenge/response, no token on the wire, constant-time compare) but not yet encrypted — see CLAUDE.md's TODO section for the planned TLS+pinning split. Zero external dependencies — including a hand-rolled WebSocket implementation — so the whole thing ships as one .exe.
91 lines
2.4 KiB
Go
91 lines
2.4 KiB
Go
// looksLikeName decides whether text read from the game's memory looks
|
|
// like a real character name, and not binary garbage decoded as if it
|
|
// were UTF-16. It lives apart from main.go (which is Windows-only)
|
|
// because it's pure string logic: it can be tested without a PC with the
|
|
// game open.
|
|
package main
|
|
|
|
import (
|
|
"unicode"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
// charNameMaxChars: from the Cheat Engine table, "GameDataMan
|
|
// +0C +9C, unicode, length 19".
|
|
const charNameMaxChars = 19
|
|
|
|
// script identifies the writing-system family of a character in the name.
|
|
type script int
|
|
|
|
const (
|
|
scriptNone script = iota
|
|
scriptLatin
|
|
scriptCyrillic
|
|
scriptHangul
|
|
scriptCJK // Han, Hiragana, and Katakana count as a single family
|
|
)
|
|
|
|
func scriptOf(r rune) script {
|
|
switch {
|
|
case unicode.In(r, unicode.Latin):
|
|
return scriptLatin
|
|
case unicode.In(r, unicode.Cyrillic):
|
|
return scriptCyrillic
|
|
case unicode.In(r, unicode.Hangul):
|
|
return scriptHangul
|
|
case unicode.In(r, unicode.Han, unicode.Hiragana, unicode.Katakana):
|
|
return scriptCJK
|
|
default:
|
|
return scriptNone
|
|
}
|
|
}
|
|
|
|
// looksLikeName guards against garbage. "Printable" isn't nearly enough:
|
|
// a 64-bit pointer read as UTF-16 decodes into things like "ᾘ䱷翶", which
|
|
// is printable and would pass a filter that only checked that.
|
|
//
|
|
// This used to require the Latin alphabet, which left out Chinese,
|
|
// Korean, and Japanese names. Instead it requires SCRIPT COHERENCE: the
|
|
// whole name has to come from a single family (Latin, Cyrillic, Hangul,
|
|
// or Han+Hiragana+Katakana as one family, since Japanese mixes all three
|
|
// in one name) plus digits/space/punctuation (which don't belong to any
|
|
// particular family). Mixing families — like Greek + Han in "ᾘ䱷翶" — is
|
|
// still rejected: that's exactly the signature of binary garbage.
|
|
func looksLikeName(s string) bool {
|
|
n := utf8.RuneCountInString(s)
|
|
if n < 2 || n > charNameMaxChars {
|
|
return false
|
|
}
|
|
|
|
sc := scriptNone
|
|
hadLetter := false
|
|
|
|
for i, r := range s {
|
|
if r == utf8.RuneError || !unicode.IsPrint(r) {
|
|
return false
|
|
}
|
|
if unicode.IsDigit(r) {
|
|
continue
|
|
}
|
|
if r == ' ' || r == '-' || r == '_' || r == '.' || r == '\'' {
|
|
if i == 0 {
|
|
return false // a name doesn't start with punctuation
|
|
}
|
|
continue
|
|
}
|
|
|
|
f := scriptOf(r)
|
|
if f == scriptNone {
|
|
return false // script not accepted
|
|
}
|
|
if sc == scriptNone {
|
|
sc = f
|
|
} else if sc != f {
|
|
return false // mixed families: the signature of binary garbage
|
|
}
|
|
hadLetter = true
|
|
}
|
|
|
|
return hadLetter
|
|
}
|