Files
TiagoVal97--cheatspecter/src/ui/ModuleChoice.tsx
T
Tiago Valente 814e5bdfc5 CheatSpecter 0.4.0
- Find: text search, grouped search, undo, went up/down by, where to search, before/after compare
- Debug tab: breakpoints, stepping, registers, stack, threads, what code touches, structure compare, pointer re-check
- Engine tab: Unreal, Unity, Godot, GameMaker and C++ class names; hidden settings and switches
- Code cheats with labels, variables, references and shared code; rule builder; script commands
- Cheats kept on by the PS5 without the browser
- Learn section with twelve lessons and an in-page practice game
- Section (master code) community cheats; SHN and MC4 export
- Groups, dropdowns, hotkeys, first-cheat guide, re-find after update, restore everything
- MCP server: 25 tools
- Quick start, wiki, contributor and security guides, changelog
2026-10-10 12:06:50 -07:00

60 lines
2.4 KiB
TypeScript

// Which loaded module an offset is into: the game's own executable (the
// usual case, and the default) or one of the libraries it loads. Shows
// nothing at all when the game has only the one, so most people never see it.
import { useEffect, useState } from 'react';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { loadedModules } from '../lib/locations.ts';
import type { Location } from '../lib/locations.ts';
import { useWorkbench } from './workbench.tsx';
// Radix selects cannot hold an empty value, so the executable gets a stand-in.
const MAIN = ':main';
type InModule = Extract<Location, { kind: 'module' | 'pointer' }>;
/** For a location editor: the same location with the module set, or cleared for the executable. */
export function ModuleChoice(props: { loc: InModule; onChange: (loc: Location) => void }) {
const { ctx } = useWorkbench();
const value = props.loc.module;
const pick = (module: string | undefined) => {
const { module: _old, ...rest } = props.loc;
props.onChange(module === undefined ? rest : { ...rest, module });
};
const [names, setNames] = useState<string[]>([]);
useEffect(() => {
let canceled = false;
loadedModules(ctx)
.then((list) => !canceled && setNames(list.map((m) => m.module).filter((n): n is string => n !== undefined)))
.catch(() => {});
return () => {
canceled = true;
};
}, [ctx]);
// A module the cheat names but the game has not loaded (yet) must stay selectable, or opening the editor would silently change it.
const all = value !== undefined && !names.includes(value) ? [...names, value] : names;
if (all.length === 0) return null;
return (
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<span className="shrink-0">Inside</span>
<Select value={value ?? MAIN} onValueChange={(v) => pick(v === MAIN ? undefined : v)}>
<SelectTrigger className="min-w-0 flex-1 text-foreground" aria-label="Which module the offset is into">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value={MAIN}>The game itself (main executable)</SelectItem>
{all.map((name) => (
<SelectItem key={name} value={name}>
{name}
{names.includes(name) ? '' : ' (not loaded right now)'}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
);
}