mirror of
https://github.com/k1tbyte/Wand-Enhancer.git
synced 2026-08-28 17:01:04 +00:00
13759b1db6
- reduce ASAR IO overhead with streamed archive reads, buffered copies, faster relative-path handling and placeholder integrity records - fix in-place app.asar.unpacked packing/extraction self-copy cases that caused locked-file failures - tighten JS patch discovery with candidate bundle filters and search hints - require prebuilt remote-panel dist artifacts and clean up embedded bridge/script packaging - add unified build entrypoints for PowerShell, cmd and bash and move native CMake output under .tmp - add release metadata validation, changelog section extraction, pre-commit hook and GitHub Actions validation/release pipelines - make CHANGELOG the source of truth for release notes and document the tag-driven release flow - add updater release notes UI with latest/full changelog loading and localize the new update strings - modularize bridge renderer scripts, add installed apps and game status sync, and support remote launch/stop commands - centralize bridge protocol, IPC and WebSocket constants and improve LAN IP selection for QR pairing - refactor remote panel controls/state enums, persist accent color, polish library/session UI and refresh assets
255 lines
6.8 KiB
JavaScript
255 lines
6.8 KiB
JavaScript
import { TRAINER_LAUNCH_REQUEST_EXPORT_KEY } from "./constants.js"
|
|
import {
|
|
findExportedConstructor,
|
|
findInstanceInContainerGraph,
|
|
isRecord,
|
|
} from "./runtime.js"
|
|
|
|
export function hasMissingOptionalServices(state) {
|
|
return (
|
|
!state.gameLifecycleService ||
|
|
!state.trainerVisibilityService ||
|
|
!state.unavailableTitlesService
|
|
)
|
|
}
|
|
|
|
const OPTIONAL_SERVICE_SPECS = [
|
|
{
|
|
stateKey: "unavailableTitlesService",
|
|
methods: ["getUnavailableTitle", "searchUnavailableTitles", "getUnavailableTitlesByCorrelationIds"],
|
|
label: "Unavailable titles service",
|
|
},
|
|
{
|
|
stateKey: "gameLifecycleService",
|
|
methods: ["onGameLaunched", "onGameEnded", "launch"],
|
|
label: "Game lifecycle service",
|
|
},
|
|
{
|
|
stateKey: "trainerVisibilityService",
|
|
methods: ["onDisplayTrainerChanged", "onVisibleTrainerChanged", "onRunningTrainerChanged"],
|
|
label: "Trainer visibility service",
|
|
},
|
|
{
|
|
stateKey: "trainerApiService",
|
|
methods: ["getLatestLocalTrainerForGame", "getMostCompatibleTrainerForGame", "getTrainerById"],
|
|
label: "Trainer API service",
|
|
},
|
|
{
|
|
stateKey: "trainerService",
|
|
methods: ["launch", "endTrainer", "onNewTrainer", "onTrainerEnded"],
|
|
label: "Trainer service",
|
|
},
|
|
]
|
|
|
|
export function resolveOptionalServices(state, container, webpackRequire) {
|
|
for (const spec of OPTIONAL_SERVICE_SPECS) {
|
|
if (!state[spec.stateKey]) {
|
|
state[spec.stateKey] = resolveOptionalService(state, container, webpackRequire, spec)
|
|
}
|
|
}
|
|
|
|
if (!state.trainerLaunchRequestCtor) {
|
|
state.trainerLaunchRequestCtor = getTrainerLaunchRequestCtor(state, webpackRequire)
|
|
}
|
|
}
|
|
|
|
export function getInstalledAppsService(state, container, webpackRequire) {
|
|
const ctor = findExportedConstructor(webpackRequire, (prototype) => {
|
|
return (
|
|
typeof prototype.refreshApps === "function" &&
|
|
typeof prototype.watchGame === "function"
|
|
)
|
|
})
|
|
|
|
if (!ctor) {
|
|
state.log(
|
|
"warn",
|
|
"Installed apps service constructor not found in webpack cache."
|
|
)
|
|
return null
|
|
}
|
|
|
|
try {
|
|
const service = container.get(ctor)
|
|
const appsCount = isRecord(service.installedApps)
|
|
? Object.keys(service.installedApps).length
|
|
: -1
|
|
const catalogGamesCount = isRecord(service.catalog?.games)
|
|
? Object.keys(service.catalog.games).length
|
|
: -1
|
|
state.log(
|
|
"info",
|
|
"Installed apps service resolved.",
|
|
`ctor=${ctor.name || "<anon>"}, installedApps=${appsCount}, catalogGames=${catalogGamesCount}`
|
|
)
|
|
return service
|
|
} catch (error) {
|
|
state.log(
|
|
"warn",
|
|
"Failed to resolve installed apps service from Aurelia container.",
|
|
error?.stack || String(error)
|
|
)
|
|
return null
|
|
}
|
|
}
|
|
|
|
export function getStoreRef(state, container, webpackRequire) {
|
|
const storeCtor = findExportedConstructor(webpackRequire, (prototype) => {
|
|
return (
|
|
typeof prototype.dispatch === "function" &&
|
|
typeof prototype.registerAction === "function" &&
|
|
typeof prototype.unregisterAction === "function"
|
|
)
|
|
})
|
|
|
|
if (!storeCtor) {
|
|
state.log("warn", "Store constructor not found in webpack cache.")
|
|
return null
|
|
}
|
|
|
|
try {
|
|
const store = container.get(storeCtor)
|
|
const storeState =
|
|
typeof store.state?.getValue === "function"
|
|
? store.state.getValue()
|
|
: null
|
|
const installedAppsCount = isRecord(storeState?.installedApps)
|
|
? Object.keys(storeState.installedApps).length
|
|
: -1
|
|
const catalogGamesCount = isRecord(storeState?.catalog?.games)
|
|
? Object.keys(storeState.catalog.games).length
|
|
: -1
|
|
state.log(
|
|
"info",
|
|
"Store resolved via fallback.",
|
|
`ctor=${storeCtor.name || "<anon>"}, installedApps=${installedAppsCount}, catalogGames=${catalogGamesCount}`
|
|
)
|
|
return store
|
|
} catch (error) {
|
|
state.log(
|
|
"warn",
|
|
"Failed to resolve Store from container.",
|
|
error?.stack || String(error)
|
|
)
|
|
return null
|
|
}
|
|
}
|
|
|
|
function resolveOptionalService(state, container, webpackRequire, spec) {
|
|
const matchesMethods = (target) => hasAllMethods(target, spec.methods)
|
|
const ctor = findExportedConstructor(webpackRequire, matchesMethods)
|
|
if (ctor) {
|
|
return getContainerService(state, container, ctor, spec.stateKey, spec.label)
|
|
}
|
|
|
|
return findFallbackService(
|
|
state,
|
|
container,
|
|
spec.stateKey,
|
|
`${spec.label} constructor not found in webpack cache.`,
|
|
matchesMethods
|
|
)
|
|
}
|
|
|
|
function hasAllMethods(target, methods) {
|
|
if (!target) {
|
|
return false
|
|
}
|
|
|
|
for (const method of methods) {
|
|
if (typeof target[method] !== "function") {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
function getTrainerLaunchRequestCtor(state, webpackRequire) {
|
|
const cache = webpackRequire?.c
|
|
if (!cache || typeof cache !== "object") {
|
|
warnMissingOptionalService(
|
|
state,
|
|
"trainerLaunchRequestCtor",
|
|
"Trainer launch request constructor cache is unavailable."
|
|
)
|
|
return null
|
|
}
|
|
|
|
for (const record of Object.values(cache)) {
|
|
const exports = record?.exports
|
|
if (!isRecord(exports)) {
|
|
continue
|
|
}
|
|
|
|
const candidate = exports[TRAINER_LAUNCH_REQUEST_EXPORT_KEY]
|
|
if (
|
|
typeof candidate === "function" &&
|
|
typeof exports.ZS === "function" &&
|
|
typeof exports.jR === "function" &&
|
|
typeof exports.UY === "function"
|
|
) {
|
|
clearMissingOptionalServiceWarning(state, "trainerLaunchRequestCtor")
|
|
return candidate
|
|
}
|
|
}
|
|
|
|
warnMissingOptionalService(
|
|
state,
|
|
"trainerLaunchRequestCtor",
|
|
"Trainer launch request constructor not found in webpack cache."
|
|
)
|
|
return null
|
|
}
|
|
|
|
function getContainerService(state, container, ctor, warningKey, label) {
|
|
try {
|
|
const service = container.get(ctor)
|
|
clearMissingOptionalServiceWarning(state, warningKey)
|
|
state.log(
|
|
"info",
|
|
`${label} resolved.`,
|
|
`ctor=${ctor.name || "<anon>"}${service?.runningTrainer ? ", running=yes" : ""}`
|
|
)
|
|
return service
|
|
} catch (error) {
|
|
state.log(
|
|
"warn",
|
|
`Failed to resolve ${label.toLowerCase()} from Aurelia container.`,
|
|
error?.stack || String(error)
|
|
)
|
|
return null
|
|
}
|
|
}
|
|
|
|
function findFallbackService(
|
|
state,
|
|
container,
|
|
warningKey,
|
|
missingMessage,
|
|
predicate
|
|
) {
|
|
const fallbackService = findInstanceInContainerGraph(container, predicate)
|
|
if (fallbackService) {
|
|
clearMissingOptionalServiceWarning(state, warningKey)
|
|
state.log("info", `${warningKey} resolved from container graph.`)
|
|
return fallbackService
|
|
}
|
|
|
|
warnMissingOptionalService(state, warningKey, missingMessage)
|
|
return null
|
|
}
|
|
|
|
function warnMissingOptionalService(state, key, message) {
|
|
if (state.missingOptionalServiceWarnings.has(key)) {
|
|
return
|
|
}
|
|
|
|
state.missingOptionalServiceWarnings.add(key)
|
|
state.log("warn", message)
|
|
}
|
|
|
|
function clearMissingOptionalServiceWarning(state, key) {
|
|
state.missingOptionalServiceWarnings.delete(key)
|
|
}
|