Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions ALTERNATIVE_LSOF_APPROACH.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Alternative Approach: Using lsof to Check File Locks

If checking only the main `steam` process isn't sufficient, here's an alternative approach that checks which processes actually have the category storage files open:

## Implementation

```typescript
// In stop-start-steam.ts, replace the Linux stopSteam check with:

} else if (os.type() == "Linux") {
data.commands = {
action: `kill -15 $(pidof -x steam) 2>/dev/null || true`,
check: `
# Find steam directory from common locations
STEAM_DIR=""
if [ -d "$HOME/.steam/steam" ]; then
STEAM_DIR="$HOME/.steam/steam"
elif [ -d "$HOME/.local/share/Steam" ]; then
STEAM_DIR="$HOME/.local/share/Steam"
fi

if [ -z "$STEAM_DIR" ]; then
# Can't find steam dir, just check for steam process
steam_pid=$(pgrep -x '^steam$' 2>/dev/null)
if [ -z "$steam_pid" ]; then echo "True"; else echo "False"; fi
else
# Check if any process has the category files open
# Check cloud storage, localconfig.vdf, or leveldb files
CLOUD_FILES=$(find "$STEAM_DIR/userdata/*/config/cloudstorage" -name "cloud-storage-namespace-*.json" 2>/dev/null)
LOCALCONFIG_FILES=$(find "$STEAM_DIR/userdata/*/config" -name "localconfig.vdf" 2>/dev/null)
LEVELDB_DIR="$STEAM_DIR/config/htmlcache/Local Storage/leveldb"

HAS_LOCKS=0

# Check cloud storage files
for file in $CLOUD_FILES; do
if lsof "$file" 2>/dev/null | grep -q .; then
HAS_LOCKS=1
break
fi
done

# Check localconfig.vdf files
if [ $HAS_LOCKS -eq 0 ]; then
for file in $LOCALCONFIG_FILES; do
if lsof "$file" 2>/dev/null | grep -q .; then
HAS_LOCKS=1
break
fi
done
fi

# Check leveldb directory
if [ $HAS_LOCKS -eq 0 ] && [ -d "$LEVELDB_DIR" ]; then
if lsof +D "$LEVELDB_DIR" 2>/dev/null | grep -q .; then
HAS_LOCKS=1
fi
fi

if [ $HAS_LOCKS -eq 0 ]; then
echo "True"
else
echo "False"
fi
fi
`.trim(),
};
data.shell = "/bin/sh";
}
```

## Pros
- Most accurate - checks actual file locks
- Won't have false positives from unrelated Steam processes
- Works regardless of Steam's internal process architecture

## Cons
- More complex
- Requires `lsof` to be installed (usually is on Linux)
- Slower due to file system checks
- May require permissions to check file locks

## Recommendation

**Start with the simple approach (only checking main steam process)** and only implement this if you find that:
1. Helper processes actually do hold the files open
2. Users report write failures when they shouldn't happen

The simple approach is cleaner, faster, and aligns with the CLAUDE.md documentation which states that only the main `steam` process and `steamwebhelper` hold category files - and we now know `steamwebhelper` is just for web rendering.
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ All notable changes to this project will be documented in this file.
### Added

- Ability to drag/drop to re-arrange parsers in Classic and Deck themes.
- Several UI standardizations: Unified color scheme for Deck theme, moved
logger options to an options panel, minimized all docs by default, minimized error reporter by default.
- Several UI standardizations: Unified color scheme for Deck theme, moved
logger options to an options panel, minimized all docs by default, minimized error reporter by default.

### Fixed

Expand Down
150 changes: 150 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Steam ROM Manager (SRM) is an Electron application built with Angular that manages ROMs and non-Steam games in Steam. It automatically adds games to Steam libraries with artwork, controller templates, and proper categorization.

## Development Commands

### Essential Commands
- `yarn install` - Install dependencies (run after cloning)
- `yarn run build:dist` - Build both main and renderer processes for production
- `yarn run start` - Launch the compiled application
- `yarn run watch:main` - Watch and recompile main Electron process
- `yarn run watch:renderer` - Watch and recompile Angular renderer process
- `yarn run pretty:check` - Check code formatting with Prettier
- `yarn run pretty:write` - Auto-format code with Prettier

### Building for Distribution
- `yarn run build:win` - Build Windows installer/portable
- `yarn run build:linux` - Build Linux deb package and AppImage
- `yarn run build:mac` - Build macOS dmg package

### Development Workflow
1. Run `yarn run watch:main` once (rarely changes)
2. Run `yarn run watch:renderer` for ongoing development
3. Use `yarn run start` to launch the app
4. Refresh with Ctrl+R after renderer changes
5. Restart app after main process changes

## Architecture

### Main Process (`src/main/`)
- **app.ts** - Electron main process with CLI interface, crash reporting, auto-updater
- Handles window management, IPC communication, and system integration

### Renderer Process (`src/renderer/`)
- **Angular 18** application with TypeScript
- **app.module.ts** - Main Angular module configuration
- **components/** - UI components organized by feature
- **services/** - Data services and business logic
- **templates/** - HTML templates for components
- **styles/** - SCSS stylesheets

### Core Libraries (`src/lib/`)
- **parsers/** - Game/ROM parsers for different platforms (Steam, Epic, GOG, emulators)
- Each parser implements specific platform logic
- Support for both ROM files and digital store libraries
- **vdf-manager.ts** - Steam VDF file manipulation
- **category-manager.ts** - Steam category management
- **controller-manager.ts** - Controller template handling
- **image-provider.ts** - Artwork fetching and caching
- **file-parser.ts** - File system parsing and glob matching

### Parser System
The parser architecture supports multiple game sources:
- **ROM Parsers**: glob, glob-regex, manual
- **Platform Parsers**: Steam, Epic, GOG, EA Desktop, Amazon Games, Ubisoft Connect, etc.
- **Artwork Parsers**: Steam games, non-SRM shortcuts

### Steam Category Storage System

SRM writes category data to three storage locations in priority order (newest to oldest):

1. **Cloud Storage** (Primary/Modern - Steam Deck default):
- `~/.steam/steam/userdata/<userid>/config/cloudstorage/cloud-storage-namespace-*.json`
- Synced across devices
- SRM writes to ONE system only, stopping after first success

2. **localconfig.vdf** (Fallback):
- `~/.steam/steam/userdata/<userid>/config/localconfig.vdf`
- Traditional Steam configuration file

3. **leveldb** (Legacy):
- `~/.steam/steam/config/htmlcache/Local Storage/leveldb/*.ldb`
- Browser storage used by older Steam versions

**Read Priority**: Cloud storage → localconfig.vdf → leveldb
**Write Strategy**: Try cloud storage first; if it fails, fall back to localconfig.vdf, then leveldb as last resort

### Steam Process Management

When auto-kill Steam is enabled, SRM needs to detect when Steam processes are fully stopped:

**Process checked on Linux/Steam Deck**:
- `steam` - Main Steam client process

**Important findings from testing**:
- Steam does NOT keep category files locked - it reads/writes them transiently
- Files can technically be written while Steam is running, BUT:
- Race condition risk if Steam writes at the same moment
- Steam caches category data in memory and won't see changes until restart
- Therefore, killing Steam before writes is the safest approach

**Not checked** (these don't prevent category file operations):
- `steamwebhelper` - Web rendering helper (never holds category files)
- `reaper` - Game process manager (doesn't hold category files)
- `steamos-manager` - System daemon (not part of Steam client)
- `fossilize_replay` - Background shader compiler (doesn't prevent operations)

**Detection uses exact matching** (`pgrep -x '^steam$'`) to avoid matching SRM's own process name.

### Configuration
- **TypeScript**: ES2022 target with Angular decorators
- **Webpack**: Separate configs for main/renderer processes
- **Electron Builder**: Multi-platform distribution
- **Prettier**: Code formatting with auto end-of-line

### Key Technologies
- Electron 32+ with remote module
- Angular 18 with RxJS
- Node.js file system operations
- Steam VDF format parsing
- SQLite for local data storage
- SteamGridDB API integration

## Testing and Quality

The project uses:
- TypeScript strict mode with noImplicitAny
- Prettier for code formatting
- Electron Builder for consistent builds
- Native module recompilation via postinstall

## CLI Interface

SRM includes a full CLI with commands like:
- `list` - Show all parsers and status
- `add` - Add games to Steam
- `remove` - Remove games from Steam
- Multiple parser-specific options and flags

## Important Notes

### Steam Process Detection
- Always use exact process name matching to avoid false positives
- SRM's process name may contain "steam" - ensure checks use `pgrep -x` with regex anchors
- Only check processes that actually hold category storage files open

### Category File Operations
- Priority order: cloud storage → localconfig.vdf → leveldb
- Write to ONE system only (stop after first success)
- Graceful error handling at each level
- Steam must be fully stopped before writing

### Auto-Restart Behavior
- If `autoRestartSteam` is ON: Steam restarts immediately after save
- If `autoRestartSteam` is OFF but Steam was killed: Steam restarts when SRM exits
- Prevents leaving users with Steam permanently closed
Loading