35d46f532a
- electron/epic.ts: auto-detect Epic Games from Manifests/*.item (skip DLCs) - electron/favorites.ts: persist favorites + recent (last 20) in userData - electron/steam.ts: replace winreg callbacks with reg query execSync + fallback paths - electron: new IPC handlers — favorites:get/toggle, recent:get; launch tracks recent - UI: star button on cards (favorites), sort A→Z/Z→A toggle, skeleton loading, fade-in card animation, Epic/Favorites/Recent filter tabs, SVG placeholder with initials Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
72 lines
1.6 KiB
TypeScript
72 lines
1.6 KiB
TypeScript
import { existsSync, readdirSync, readFileSync } from 'fs'
|
|
import { join } from 'path'
|
|
|
|
export interface EpicGame {
|
|
id: string
|
|
name: string
|
|
appName: string
|
|
source: 'epic'
|
|
image: string
|
|
installed: true
|
|
}
|
|
|
|
interface EpicManifest {
|
|
DisplayName?: string
|
|
AppName?: string
|
|
MainGameAppName?: string
|
|
bIsIncompleteInstall?: boolean
|
|
AppCategories?: string[]
|
|
}
|
|
|
|
const MANIFESTS_DIR = 'C:\\ProgramData\\Epic\\EpicGamesLauncher\\Data\\Manifests'
|
|
|
|
export function detectEpicGames(): EpicGame[] {
|
|
if (process.platform !== 'win32') return []
|
|
if (!existsSync(MANIFESTS_DIR)) return []
|
|
|
|
let entries: string[]
|
|
try {
|
|
entries = readdirSync(MANIFESTS_DIR)
|
|
} catch {
|
|
return []
|
|
}
|
|
|
|
const games: EpicGame[] = []
|
|
|
|
for (const entry of entries) {
|
|
if (!entry.endsWith('.item')) continue
|
|
|
|
try {
|
|
const raw = readFileSync(join(MANIFESTS_DIR, entry), 'utf-8')
|
|
const m: EpicManifest = JSON.parse(raw)
|
|
|
|
const name = m.DisplayName
|
|
const appName = m.AppName
|
|
const mainApp = m.MainGameAppName
|
|
|
|
if (!name || !appName) continue
|
|
if (m.bIsIncompleteInstall) continue
|
|
// Skip DLCs
|
|
if (mainApp && appName !== mainApp) continue
|
|
// Only game categories
|
|
const cats = m.AppCategories ?? []
|
|
const isGame = cats.length === 0 || cats.some((c) => c.startsWith('games'))
|
|
if (!isGame) continue
|
|
|
|
games.push({
|
|
id: `epic_${appName}`,
|
|
name,
|
|
appName,
|
|
source: 'epic',
|
|
image: '',
|
|
installed: true,
|
|
})
|
|
} catch {
|
|
// skip bad manifests
|
|
}
|
|
}
|
|
|
|
games.sort((a, b) => a.name.localeCompare(b.name, 'ru'))
|
|
return games
|
|
}
|