Files
deathwatch/names.go
T
emmatherock e1f7e6f529 Initial commit: DeathWatch, an Elden Ring death counter for OBS
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.
2026-09-17 21:10:22 -03:00

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
}