A local-first Progressive Web App (PWA) for managing Pokémon from Gen 3 GBA save files. Extract Pokémon from your save files into an offline vault, view detailed stats, and manage your collection entirely in your browser.
- ✅ Import Gen 3 Save Files - Load .sav files from Ruby, Sapphire, Emerald, FireRed, and LeafGreen
- ✅ Safe Extraction - Validates save integrity with checksum verification before extracting
- ✅ Offline Vault - Store extracted Pokémon in IndexedDB for offline access
- ✅ Detailed View - See species, level, OT, TID/SID, PID, moves, IVs, EVs, and more
- ✅ Search & Filter - Find Pokémon by nickname, OT name, or species
- ✅ PWA Support - Install as a standalone app, works offline
- 🔄 Save Injection - Inject Pokémon back into save files (not yet implemented)
- 🔄 Gen 1/2 Support - Support for Game Boy/Color save files (not yet implemented)
- 🔄 Legality Checking - Validate Pokémon data for legitimacy (not yet implemented)
Currently supports Gen 3 GBA games with 128KB (131,072 bytes) save files:
- Pokémon Ruby
- Pokémon Sapphire
- Pokémon Emerald
- Pokémon FireRed
- Pokémon LeafGreen
The app implements comprehensive integrity checks to prevent data corruption:
- Size Check - Verifies file is exactly 128KB
- Active Slot Detection - Determines which of the two save slots is current using save counters
- Section Checksums - Validates all 14 sections of the active save slot
- Signature Verification - Checks for the Gen 3 magic signature (0x08012025)
- pk3 Structure - Validates 80-byte Pokémon data structure
- Decryption - XORs encrypted data with (PID ^ OTID) key
- Unshuffling - Reorders substructures based on PID % 24
- Checksum Verification - Validates pk3 checksum (sum of decrypted 16-bit words)
- ❌ No writes to invalid saves - Won't attempt to modify corrupted data
- ✅ Read-only extraction - Current version only reads, doesn't modify saves
- ✅ Checksum reporting - Shows which Pokémon have invalid checksums
- ✅ Empty slot detection - Skips empty PC boxes automatically
- Node.js 18+
- npm or yarn
# Clone the repository
git clone https://github.com/Motsocool/Poke-Trees_ProfessorsPC.git
cd Poke-Trees_ProfessorsPC
# Install dependencies
npm install
# Start development server
npm run dev- Open the app in your browser (http://localhost:5173)
- Click "Choose .sav File" and select a Gen 3 save file
- Wait for extraction - The app will parse and import Pokémon from PC boxes
- Browse your vault - Click on any Pokémon to see detailed stats
- Search & filter - Use the search box to find specific Pokémon
# Build the app
npm run build
# Preview production build
npm run previewThe built files will be in the dist/ directory and include:
- PWA service worker for offline support
- Web manifest for installability
- Optimized bundles
The codebase is split into three layers:
Pure TypeScript, no DOM dependencies:
utils/bin.ts- Little-endian read/write, bounds checkinggen3/pk3/- pk3 decode/encode, encryption, shuffling, checksumsgen3/save/- Save parsing, section validation, PC extractionspecies/- Species name mapping and Gen3 character encodingdb/- IndexedDB vault operations
Currently integrated into components, could be extracted to dedicated state management
React components:
- SaveImport - File input and import logic
- VaultView - Grid display of all Pokémon
- PokemonDetail - Detailed stats panel
# Run all tests
npm test
# Run tests in watch mode
npm run test
# Run tests once
npm run test:run
# View test UI
npm run test:uiTests cover:
- Binary utilities (read/write, bounds checking)
- pk3 encryption/decryption roundtrips
- pk3 shuffle/unshuffle roundtrips
- Checksum calculations
- Save section parsing
- Active slot detection
- Local-first - All data stays in your browser's IndexedDB
- No server - No data is sent anywhere
- No tracking - No analytics or telemetry
- Offline capable - Works without internet after first load
- 128KB total (131,072 bytes)
- 2 save slots (A and B) for redundancy
- Each slot has 14 sections of 4KB each
- Sections 5-13 contain PC box data
- Each section has a 12-byte footer with ID, checksum, signature, and save counter
- Header (32 bytes): PID, OTID, nickname, language, OT name, checksum
- Data (48 bytes): Encrypted substructures containing:
- Growth: species, item, experience, friendship
- Attacks: 4 moves and their PP
- EVs: HP, Attack, Defense, Speed, Sp.Atk, Sp.Def
- Misc: Pokérus, met location, IVs, ribbons
- XOR data with (PID ^ OTID) key, 16-bit words
- Unshuffle based on PID % 24 (24 possible orders)
- Checksum is sum of all 16-bit words in decrypted data
Contributions welcome! Areas that need work:
- Save injection feature (write Pokémon back to saves)
- Gen 1/2 support
- Complete species name database (currently partial)
- Proper experience curve calculations
- Move name database
- Nature and ability calculations
- Legality checking
ISC
Built with:
- Vite - Build tool
- React - UI framework
- TypeScript - Type safety
- idb - IndexedDB wrapper
- Vitest - Testing framework
Pokémon structure documentation from:
