Upload files to "/"

This commit is contained in:
Bored
2025-08-05 04:54:50 +00:00
commit a5c02cd0f7
28 changed files with 13977 additions and 0 deletions

7
._analysis.json Normal file
View File

@@ -0,0 +1,7 @@
{
"file_path": ".",
"analysis": {},
"errors": [
"File read failed: [Errno 13] Permission denied: '.'"
]
}

BIN
Fallout Shelter.log Normal file

Binary file not shown.

181
README.md Normal file
View File

@@ -0,0 +1,181 @@
# Fallout Shelter Save Editor
A modern, cross-platform save editor for Fallout Shelter built with Electron.js. Features AES decryption support and a beautiful, intuitive interface.
![Fallout Shelter Save Editor](assets/screenshot.png)
## Features
**Modern Interface** - Beautiful, dark-themed UI with smooth animations
🔐 **AES Decryption** - Properly decrypt and encrypt Fallout Shelter save files
💰 **Resource Editing** - Edit caps, food, water, power, stimpaks, and more
🏠 **Vault Management** - Modify vault name, mode, and theme
👥 **Dweller Overview** - View dweller information and stats
📝 **Raw Data Access** - Direct JSON editing for advanced users
💾 **Safe Backups** - Automatic backup creation before modifications
**Cross-Platform** - Works on Windows, macOS, and Linux
## Installation
### Option 1: Download Pre-built Binary
1. Go to the [Releases](https://github.com/your-repo/releases) page
2. Download the appropriate version for your operating system
3. Install and run the application
### Option 2: Build from Source
#### Prerequisites
- Node.js 16 or higher
- npm or yarn
#### Steps
1. Clone the repository:
```bash
git clone https://github.com/your-repo/fallout-shelter-save-editor.git
cd fallout-shelter-save-editor
```
2. Install dependencies:
```bash
npm install
```
3. Run in development mode:
```bash
npm run dev
```
4. Build for production:
```bash
npm run build
```
## Usage
### Loading a Save File
1. Launch the application
2. Click "Get Started - Open Save File" or use File → Open Save File
3. Navigate to your Fallout Shelter save directory:
- **Windows**: `%LOCALAPPDATA%/FalloutShelter/`
- **macOS**: `~/Library/Application Support/FalloutShelter/`
- **Android** (with file access): `/Android/data/com.bethsoft.falloutshelter/files/`
### Editing Resources
1. Switch to the "Resources" tab
2. Modify the values for caps, food, water, power, etc.
3. Click "Apply Changes" to save modifications
4. Use "Max All" for maximum resource values
### Vault Information
1. Go to the "Vault Info" tab
2. Edit vault name, mode (Normal/Survival), and theme
3. Apply changes when ready
### Advanced Editing
1. Use the "Raw Data" tab for direct JSON editing
2. Format JSON for better readability
3. Copy data to clipboard for external editing
### Saving Changes
1. Use Ctrl+S (Cmd+S on Mac) or File → Save
2. Create backups with File → Create Backup
3. Use File → Save As to save to a different location
## Save File Locations
### Windows
```
%LOCALAPPDATA%/FalloutShelter/
```
### macOS
```
~/Library/Application Support/FalloutShelter/
```
### Android (Rooted/File Access)
```
/Android/data/com.bethsoft.falloutshelter/files/
```
## Technical Details
### Encryption
Fallout Shelter save files use AES-CBC encryption with:
- **Key**: `a7c2f3f367a2e2e2f0f2b6b1b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7` (hex)
- **IV**: `7475383967656A693334307438397532` (hex)
- **Mode**: CBC with PKCS7 padding
- **Encoding**: Base64
### Save Structure
The decrypted save file is a JSON object containing:
- `vault.storage.resources` - Resource amounts (caps, food, water, etc.)
- `vault.VaultName` - Vault name
- `vault.VaultMode` - Game mode (Normal/Survival)
- `dwellers.dwellers` - Array of dweller data
- And much more...
## Development
### Project Structure
```
├── main.js # Electron main process
├── renderer.js # Renderer process (UI logic)
├── index.html # Main UI
├── styles.css # Styling
├── package.json # Dependencies and scripts
└── assets/ # Icons and images
```
### Building
```bash
# Development
npm run dev
# Build for current platform
npm run build
# Build for specific platforms
npm run build-win # Windows
npm run build-mac # macOS
npm run build-linux # Linux
```
## Contributing
1. Fork the repository
2. Create a feature branch: `git checkout -b feature-name`
3. Make your changes and test thoroughly
4. Commit your changes: `git commit -am 'Add feature'`
5. Push to the branch: `git push origin feature-name`
6. Submit a pull request
## Safety & Disclaimers
⚠️ **Important Notes:**
- Always backup your save files before editing
- Use at your own risk - save corruption is possible
- This tool is for educational and personal use only
- Not affiliated with Bethesda Game Studios
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## Acknowledgments
- Bethesda Game Studios for Fallout Shelter
- The modding community for reverse engineering the save format
- Electron.js team for the framework
- Font Awesome for icons
## Support
If you encounter issues:
1. Check the [Issues](https://github.com/your-repo/issues) page
2. Create a new issue with detailed information
3. Include your operating system and save file details (without personal data)
---
**Enjoy modifying your vault! 🏠⚡**

1
Vault1.sav Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
Vault1.sav.bkp Normal file

File diff suppressed because one or more lines are too long

19
Vault1.sav_analysis.json Normal file
View File

@@ -0,0 +1,19 @@
{
"file_path": "Vault1.sav",
"analysis": {
"raw_size": 95532,
"raw_preview": "7a434c314678456877355343656e6f776c487276705a48453338676d614d6e5a6c496c6d4d43347a5930504c2f4a505342666443625279533039335073614d5631674377734236624c562b586455792b57517241713759534e4f4b447961477730647467",
"base64_decoded": true,
"decoded_size": 71648,
"decoded_preview": "cc22f5171121c394827a7a30947aefa591c4dfc82668c9d9948966302e336343cbfc93d205f7426d1c92d3ddcfb1a315d600",
"zlib_error": "Error -3 while decompressing data: incorrect header check",
"gzip_error": "Not a gzipped file (b'\\xcc\"')",
"zlib_raw_error": "Error -3 while decompressing data: invalid code lengths set",
"first_4_bytes_le": 401941196,
"first_4_bytes_be": 3424843031,
"possible_json_content": "\"\u0017\u0011!\u00d4zz0z\uf951&h\u0654f0.3cC\u0005Bm\u001c\u03f1\u0015\u0000\u001e-_uLY\n\u00124\u0261`\u001ae\u0007\u001f\u0013j\u000fk\u000b:\u001bT_m;3e\u071e\u0011\u001fNCt\u001cI\u0492&`OD<M\"v\rw\u0148\u0001\u0018g<\u0013pg\u0279 .9\u0017/V`<\u0011&fCQ7\n4ZshwStn?\u0019\u007fXr1U\u001b\r^+B:\u0010+}e|bZ\u007f\u07e2VZ\b\u001d\u0015]\u0440J9\u0016?\u0089\u0004\u007fq0(B\u0016v-\u01a1^)TLk\u001d\u0004H-\u0017;\u001ec*C\u001f\u0748}U\u000e\u0005r\rA[{xzr\nZ\u0015\n-<\u01c1\u0015QD)\u0010a,)Xk$\rpNK\u001d\u001c\u0005s\u0011Y\u0011\u0010r.\u0011#1&\u0015f\"y\u0007Z\u01b03.V\u0016PTYk\u0015$\u05cd>6:k\u01b33&'6\u000e\u033cV{\u001e\\d\u000f->\u007f\u001bG<O>Q=Y\u00b9pQ<<|x'2\u001dL6\u00139\u06e8\\im_\rr *}\u000fW\t[~\u0016$\u001aTo/~;S~&[)\ud1e5;\u0016N074lqk)B&\u000fBQBcZ;[\u0016R>\u0007gly\u0014\u001a\u0001\u007f*\u001f\u033c@F\u025a}:[pt:\u0006\u0701W:w]*\u0010Xa4{5` k\u001c.\u02a2\u0521(D\u001c$<\u0017\u04a8gj\u0017i-||\u0015\u001a-\u0527\u03577@~I\u0003l\u0007)\u0006n{=\t}\u03c8\u007f\u0017(\n>\u0006,2.\u001e)K\u0269\u00af\u0006~\u0007\u0018y\u0014l\u02ae.G+[T<\u001e\u007fYP;!\u0015\u04a9\u0004{B\u02be*U\u0487`\u05ab\"}wy6~\"z.\n1QRXex9}NWv,\"KON\u000e*tbk+jY'\u007f\u0018Q\u0004ko5\u0006\u0001V\u001cXFg0\u036aJA\u0011\u1cef",
"entropy": 7.997523790919208,
"likely_encrypted": true
},
"errors": []
}

BIN
advanced_save_editor.py Normal file

Binary file not shown.

250
analyze_save.py Normal file
View File

@@ -0,0 +1,250 @@
#!/usr/bin/env python3
"""
Fallout Shelter Save File Analyzer
Analyzes the structure and format of Fallout Shelter save files.
"""
import base64
import json
import zlib
import gzip
import struct
import binascii
from typing import Optional, Dict, Any
def analyze_save_file(filepath: str) -> Dict[str, Any]:
"""Comprehensive analysis of a Fallout Shelter save file."""
results = {
"file_path": filepath,
"analysis": {},
"errors": []
}
try:
# Read raw file
with open(filepath, 'rb') as f:
raw_data = f.read()
results["analysis"]["raw_size"] = len(raw_data)
results["analysis"]["raw_preview"] = raw_data[:100].hex()
# Check if it's base64
try:
decoded_data = base64.b64decode(raw_data)
results["analysis"]["base64_decoded"] = True
results["analysis"]["decoded_size"] = len(decoded_data)
results["analysis"]["decoded_preview"] = decoded_data[:50].hex()
# Analyze decoded data
analyze_decoded_data(decoded_data, results)
except Exception as e:
results["errors"].append(f"Base64 decode failed: {e}")
results["analysis"]["base64_decoded"] = False
except Exception as e:
results["errors"].append(f"File read failed: {e}")
return results
def analyze_decoded_data(data: bytes, results: Dict[str, Any]) -> None:
"""Analyze the decoded binary data."""
# Check for common compression signatures
compression_tests = [
("zlib", lambda d: zlib.decompress(d)),
("gzip", lambda d: gzip.decompress(d)),
("zlib_raw", lambda d: zlib.decompress(d, -15)), # Raw deflate
]
for name, decompress_func in compression_tests:
try:
decompressed = decompress_func(data)
results["analysis"][f"{name}_success"] = True
results["analysis"][f"{name}_size"] = len(decompressed)
# Try to parse as JSON
try:
text = decompressed.decode('utf-8')
json_data = json.loads(text)
results["analysis"][f"{name}_json_success"] = True
results["analysis"][f"{name}_json_keys"] = list(json_data.keys()) if isinstance(json_data, dict) else "not_dict"
results["analysis"]["parsed_data"] = json_data
return # Success! Stop here
except Exception as json_e:
results["analysis"][f"{name}_json_error"] = str(json_e)
except Exception as e:
results["analysis"][f"{name}_error"] = str(e)
# If no standard compression worked, try custom analysis
analyze_custom_format(data, results)
def analyze_custom_format(data: bytes, results: Dict[str, Any]) -> None:
"""Analyze for custom Fallout Shelter format."""
# Check for common patterns
if len(data) >= 4:
# Try reading first 4 bytes as various integer formats
results["analysis"]["first_4_bytes_le"] = struct.unpack('<I', data[:4])[0]
results["analysis"]["first_4_bytes_be"] = struct.unpack('>I', data[:4])[0]
# Check if first 4 bytes could be a length field
length_le = struct.unpack('<I', data[:4])[0]
length_be = struct.unpack('>I', data[:4])[0]
if 4 < length_le < len(data):
results["analysis"]["possible_length_field_le"] = length_le
try_decompress_with_header(data[4:], results, "le_header")
if 4 < length_be < len(data):
results["analysis"]["possible_length_field_be"] = length_be
try_decompress_with_header(data[4:], results, "be_header")
# Look for JSON-like patterns in raw data
text_preview = data.decode('utf-8', errors='ignore')[:500]
if '{' in text_preview or '"' in text_preview:
results["analysis"]["possible_json_content"] = text_preview
# Check for encryption patterns (high entropy)
entropy = calculate_entropy(data)
results["analysis"]["entropy"] = entropy
if entropy > 7.5:
results["analysis"]["likely_encrypted"] = True
else:
results["analysis"]["likely_encrypted"] = False
def try_decompress_with_header(data: bytes, results: Dict[str, Any], prefix: str) -> None:
"""Try decompressing data after removing header."""
compression_methods = [
("zlib", lambda d: zlib.decompress(d)),
("gzip", lambda d: gzip.decompress(d)),
("zlib_raw", lambda d: zlib.decompress(d, -15)),
]
for name, decompress_func in compression_methods:
try:
decompressed = decompress_func(data)
results["analysis"][f"{prefix}_{name}_success"] = True
results["analysis"][f"{prefix}_{name}_size"] = len(decompressed)
# Try JSON parse
try:
text = decompressed.decode('utf-8')
json_data = json.loads(text)
results["analysis"][f"{prefix}_{name}_json_success"] = True
results["analysis"]["parsed_data"] = json_data
return
except:
pass
except Exception as e:
results["analysis"][f"{prefix}_{name}_error"] = str(e)
def calculate_entropy(data: bytes) -> float:
"""Calculate Shannon entropy of data."""
if not data:
return 0
import math
# Count byte frequencies
frequencies = [0] * 256
for byte in data:
frequencies[byte] += 1
# Calculate entropy
entropy = 0
data_len = len(data)
for freq in frequencies:
if freq > 0:
p = freq / data_len
entropy -= p * math.log2(p)
return entropy
def print_analysis_results(results: Dict[str, Any]) -> None:
"""Print analysis results in a readable format."""
print(f"Analysis Results for: {results['file_path']}")
print("=" * 50)
if results["errors"]:
print("ERRORS:")
for error in results["errors"]:
print(f" - {error}")
print()
analysis = results["analysis"]
print("FILE INFO:")
print(f" Raw size: {analysis.get('raw_size', 'unknown')} bytes")
print(f" Base64 decoded: {analysis.get('base64_decoded', False)}")
if analysis.get('decoded_size'):
print(f" Decoded size: {analysis['decoded_size']} bytes")
print()
print("COMPRESSION ANALYSIS:")
compression_methods = ['zlib', 'gzip', 'zlib_raw']
for method in compression_methods:
if f"{method}_success" in analysis:
print(f" {method}: SUCCESS (size: {analysis[f'{method}_size']})")
if f"{method}_json_success" in analysis:
print(f" JSON parse: SUCCESS")
if "parsed_data" in analysis:
data = analysis["parsed_data"]
if isinstance(data, dict):
print(f" Keys: {list(data.keys())[:10]}") # First 10 keys
else:
print(f" JSON parse: FAILED")
elif f"{method}_error" in analysis:
print(f" {method}: FAILED ({analysis[f'{method}_error']})")
print()
if "entropy" in analysis:
print(f"DATA CHARACTERISTICS:")
print(f" Entropy: {analysis['entropy']:.2f}")
print(f" Likely encrypted: {analysis.get('likely_encrypted', 'unknown')}")
print()
if "parsed_data" in analysis:
print("PARSED DATA PREVIEW:")
data = analysis["parsed_data"]
if isinstance(data, dict):
for key, value in list(data.items())[:5]: # First 5 items
print(f" {key}: {str(value)[:100]}")
else:
print(f" {str(data)[:200]}")
def main():
"""Main function."""
import sys
if len(sys.argv) != 2:
print("Usage: python analyze_save.py <save_file_path>")
print("Example: python analyze_save.py Vault1.sav")
return
filepath = sys.argv[1]
results = analyze_save_file(filepath)
print_analysis_results(results)
# Save detailed results to JSON
output_file = f"{filepath}_analysis.json"
with open(output_file, 'w') as f:
json.dump(results, f, indent=2, default=str)
print(f"\nDetailed analysis saved to: {output_file}")
if __name__ == "__main__":
main()

5001
decrypted_save.json Normal file
View File

@@ -0,0 +1,5001 @@
{
"timeMgr": {
"gameTime": 5081.7,
"questTime": 0.0,
"time": 5260.19,
"timeSaveDate": 638895299353878112,
"timeGameBegin": 638895246749920421
},
"localNotificationMgr": {
"UniqueIDS": []
},
"taskMgr": {
"id": 1007,
"time": 5260.19,
"tasks": [
{
"startTime": 1788.73,
"endTime": 88188.73,
"id": 391,
"paused": false,
"rescheduleToOldest": true
},
{
"startTime": 0.0,
"endTime": 86400.0,
"id": 7,
"paused": false,
"rescheduleToOldest": true
},
{
"startTime": 0.0,
"endTime": 86400.0,
"id": 8,
"paused": false,
"rescheduleToOldest": true
},
{
"startTime": 4459.49,
"endTime": 15259.49,
"id": 843,
"paused": false,
"rescheduleToOldest": true
},
{
"startTime": 4309.49,
"endTime": 15109.49,
"id": 828,
"paused": false,
"rescheduleToOldest": true
},
{
"startTime": 4115.39,
"endTime": 14915.39,
"id": 800,
"paused": false,
"rescheduleToOldest": true
},
{
"startTime": 4054.49,
"endTime": 14854.49,
"id": 791,
"paused": false,
"rescheduleToOldest": true
},
{
"startTime": 3563.79,
"endTime": 14363.79,
"id": 658,
"paused": false,
"rescheduleToOldest": true
},
{
"startTime": 3503.79,
"endTime": 14303.79,
"id": 652,
"paused": false,
"rescheduleToOldest": true
},
{
"startTime": 992.8,
"endTime": 9770.85,
"id": 219,
"paused": false,
"rescheduleToOldest": false
},
{
"startTime": 4365.14,
"endTime": 7965.14,
"id": 835,
"paused": false,
"rescheduleToOldest": true
},
{
"startTime": 4235.23,
"endTime": 7624.43,
"id": 217,
"paused": false,
"rescheduleToOldest": false
},
{
"startTime": 3600.0,
"endTime": 7200.0,
"id": 6,
"paused": false,
"rescheduleToOldest": true
},
{
"startTime": 3033.77,
"endTime": 6669.49,
"id": 585,
"paused": false,
"rescheduleToOldest": true
},
{
"startTime": 4592.8,
"endTime": 6392.8,
"id": 213,
"paused": false,
"rescheduleToOldest": false
},
{
"startTime": 4870.0,
"endTime": 6162.4,
"id": 221,
"paused": false,
"rescheduleToOldest": false
},
{
"startTime": 4870.0,
"endTime": 6162.4,
"id": 220,
"paused": false,
"rescheduleToOldest": false
},
{
"startTime": 3566.7,
"endTime": 5966.7,
"id": 659,
"paused": false,
"rescheduleToOldest": true
},
{
"startTime": 4592.8,
"endTime": 5492.8,
"id": 215,
"paused": false,
"rescheduleToOldest": false
},
{
"startTime": 5252.8,
"endTime": 5312.8,
"id": 214,
"paused": false,
"rescheduleToOldest": false
},
{
"startTime": 5252.8,
"endTime": 5312.8,
"id": 216,
"paused": false,
"rescheduleToOldest": false
},
{
"startTime": 5250.62,
"endTime": 5304.27,
"id": 1,
"paused": false,
"rescheduleToOldest": true
},
{
"startTime": 5258.43,
"endTime": 5273.43,
"id": 1006,
"paused": false,
"rescheduleToOldest": true
},
{
"startTime": 5258.43,
"endTime": 5273.43,
"id": 1005,
"paused": false,
"rescheduleToOldest": true
},
{
"startTime": 5258.43,
"endTime": 5273.43,
"id": 1004,
"paused": false,
"rescheduleToOldest": true
},
{
"startTime": 5260.0,
"endTime": 5270.0,
"id": 3,
"paused": false,
"rescheduleToOldest": true
},
{
"startTime": 5260.0,
"endTime": 5270.0,
"id": 4,
"paused": false,
"rescheduleToOldest": true
},
{
"startTime": 5260.0,
"endTime": 5270.0,
"id": 5,
"paused": false,
"rescheduleToOldest": true
},
{
"startTime": 4365.14,
"endTime": 5265.14,
"id": 834,
"paused": false,
"rescheduleToOldest": true
},
{
"startTime": 5252.8,
"endTime": 5264.8,
"id": 218,
"paused": false,
"rescheduleToOldest": false
},
{
"startTime": 5256.0,
"endTime": 5264.0,
"id": 2,
"paused": false,
"rescheduleToOldest": true
},
{
"startTime": 5256.0,
"endTime": 5261.0,
"id": 1007,
"paused": false,
"rescheduleToOldest": true
},
{
"startTime": 5259.99,
"endTime": 5260.24,
"id": 1002,
"paused": false,
"rescheduleToOldest": true
}
],
"pausedTasks": []
},
"ratingMgr": {
"sampleId": 6,
"dayRatingId": 7,
"currentSamples": [
100
],
"ratings": [],
"ratingsLast": [],
"hasWeekRating": false,
"hasLastWeekRating": false,
"weekRating": 0,
"lastWeekRating": 0
},
"specialTheme": {
"themeByRoomType": {},
"eventsThemes": {
"Cafeteria_Xmas": {
"id": "Cafeteria_Xmas",
"type": "Theme",
"hasBeenAssigned": false,
"hasRandonWeaponBeenAssigned": false,
"extraData": {
"partsCollectedCount": 10,
"IsCraftingInProgress": false,
"IsCrafted": true,
"IsClaimed": true,
"IsClaimedInCraftingRoom": true,
"IsNew": true
}
},
"Cafeteria_Halloween": {
"id": "Cafeteria_Halloween",
"type": "Theme",
"hasBeenAssigned": false,
"hasRandonWeaponBeenAssigned": false,
"extraData": {
"partsCollectedCount": 10,
"IsCraftingInProgress": false,
"IsCrafted": true,
"IsClaimed": true,
"IsClaimedInCraftingRoom": true,
"IsNew": true
}
},
"Cafeteria_ThanksGiving": {
"id": "Cafeteria_ThanksGiving",
"type": "Theme",
"hasBeenAssigned": false,
"hasRandonWeaponBeenAssigned": false,
"extraData": {
"partsCollectedCount": 10,
"IsCraftingInProgress": false,
"IsCrafted": true,
"IsClaimed": true,
"IsClaimedInCraftingRoom": true,
"IsNew": true
}
},
"LivingQuarters_Xmas": {
"id": "LivingQuarters_Xmas",
"type": "Theme",
"hasBeenAssigned": false,
"hasRandonWeaponBeenAssigned": false,
"extraData": {
"partsCollectedCount": 10,
"IsCraftingInProgress": false,
"IsCrafted": true,
"IsClaimed": true,
"IsClaimedInCraftingRoom": true,
"IsNew": true
}
},
"LivingQuarters_Halloween": {
"id": "LivingQuarters_Halloween",
"type": "Theme",
"hasBeenAssigned": false,
"hasRandonWeaponBeenAssigned": false,
"extraData": {
"partsCollectedCount": 10,
"IsCraftingInProgress": false,
"IsCrafted": true,
"IsClaimed": true,
"IsClaimedInCraftingRoom": true,
"IsNew": true
}
},
"LivingQuarters_ThanksGiving": {
"id": "LivingQuarters_ThanksGiving",
"type": "Theme",
"hasBeenAssigned": false,
"hasRandonWeaponBeenAssigned": false,
"extraData": {
"partsCollectedCount": 10,
"IsCraftingInProgress": false,
"IsCrafted": true,
"IsClaimed": true,
"IsClaimedInCraftingRoom": true,
"IsNew": true
}
}
},
"lastOverallTheme": "None"
},
"dwellers": {
"dwellers": [
{
"serializeId": 3,
"name": "Veronique",
"lastName": "Perry",
"happiness": {
"happinessValue": 100.0
},
"health": {
"healthValue": 644.0,
"radiationValue": 0.0,
"permaDeath": false,
"lastLevelUpdated": 50,
"maxHealth": 644.0
},
"experience": {
"experienceValue": 2916000.0,
"currentLevel": 50,
"storage": 0,
"accum": 0,
"needLvUp": false,
"wastelandExperience": 0
},
"relations": {
"relations": [],
"partner": -1,
"lastPartner": -1,
"ascendants": [
-1,
-1,
-1,
-1,
-1,
-1
]
},
"gender": 1,
"stats": {
"stats": [
{
"value": 1,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 2,
"exp": 3600.0
},
{
"value": 99,
"mod": 3,
"exp": 3600.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 3600.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 14400.0
}
]
},
"pregnant": true,
"babyReady": false,
"assigned": false,
"sawIncident": false,
"WillGoToWasteland": false,
"WillBeEvicted": false,
"IsEvictedWaitingForFollowers": false,
"skinColor": 4285552709,
"hairColor": 4292039478,
"outfitColor": 4294967295,
"pendingExperienceReward": 0,
"hair": "06",
"equipedOutfit": {
"id": "RadiationSuit_Advanced",
"type": "Outfit",
"hasBeenAssigned": false,
"hasRandonWeaponBeenAssigned": false
},
"equipedWeapon": {
"id": "PlasmaPistol",
"type": "Weapon",
"hasBeenAssigned": false,
"hasRandonWeaponBeenAssigned": false
},
"savedRoom": 76,
"lastChildBorn": -1,
"rarity": "Normal",
"deathTime": -1
},
{
"serializeId": 10,
"name": "Theresa",
"lastName": "Boyd",
"happiness": {
"happinessValue": 100.0
},
"health": {
"healthValue": 644.0,
"radiationValue": 0.0,
"permaDeath": false,
"lastLevelUpdated": 50,
"maxHealth": 644.0
},
"experience": {
"experienceValue": 2916000.0,
"currentLevel": 50,
"storage": 0,
"accum": 0,
"needLvUp": false,
"wastelandExperience": 0
},
"relations": {
"relations": [],
"partner": -1,
"lastPartner": -1,
"ascendants": [
-1,
-1,
-1,
-1,
-1,
-1
]
},
"gender": 1,
"stats": {
"stats": [
{
"value": 1,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 3600.0
},
{
"value": 99,
"mod": 0,
"exp": 3600.0
},
{
"value": 99,
"mod": 0,
"exp": 3600.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 14400.0
}
]
},
"pregnant": true,
"babyReady": false,
"assigned": false,
"sawIncident": false,
"WillGoToWasteland": false,
"WillBeEvicted": false,
"IsEvictedWaitingForFollowers": false,
"skinColor": 4294963174,
"hairColor": 4294967122,
"outfitColor": 4294967295,
"pendingExperienceReward": 0,
"hair": "22",
"equipedOutfit": {
"id": "jumpsuit",
"type": "Outfit",
"hasBeenAssigned": false,
"hasRandonWeaponBeenAssigned": false
},
"equipedWeapon": {
"id": "HuntingRifle_Enhanced",
"type": "Weapon",
"hasBeenAssigned": false,
"hasRandonWeaponBeenAssigned": false
},
"savedRoom": 76,
"lastChildBorn": -1,
"rarity": "Normal",
"deathTime": -1
},
{
"serializeId": 2,
"name": "Nancy",
"lastName": "Young",
"happiness": {
"happinessValue": 100.0
},
"health": {
"healthValue": 644.0,
"radiationValue": 0.0,
"permaDeath": false,
"lastLevelUpdated": 50,
"maxHealth": 644.0
},
"experience": {
"experienceValue": 2916000.0,
"currentLevel": 50,
"storage": 0,
"accum": 0,
"needLvUp": false,
"wastelandExperience": 0
},
"relations": {
"relations": [],
"partner": -1,
"lastPartner": -1,
"ascendants": [
-1,
-1,
-1,
-1,
-1,
-1
]
},
"gender": 1,
"stats": {
"stats": [
{
"value": 1,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 3600.0
},
{
"value": 99,
"mod": 0,
"exp": 3600.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 3600.0
},
{
"value": 99,
"mod": 0,
"exp": 14400.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
}
]
},
"pregnant": true,
"babyReady": false,
"assigned": false,
"sawIncident": false,
"WillGoToWasteland": false,
"WillBeEvicted": false,
"IsEvictedWaitingForFollowers": false,
"skinColor": 4287918423,
"hairColor": 4293054406,
"outfitColor": 4294967295,
"pendingExperienceReward": 0,
"hair": "14",
"equipedOutfit": {
"id": "jumpsuit",
"type": "Outfit",
"hasBeenAssigned": false,
"hasRandonWeaponBeenAssigned": false
},
"equipedWeapon": {
"id": "Fist",
"type": "Weapon",
"hasBeenAssigned": false,
"hasRandonWeaponBeenAssigned": false
},
"savedRoom": 64,
"lastChildBorn": -1,
"rarity": "Normal",
"deathTime": -1
},
{
"serializeId": 8,
"name": "Ashley",
"lastName": "Brooks",
"happiness": {
"happinessValue": 100.0
},
"health": {
"healthValue": 644.0,
"radiationValue": 0.0,
"permaDeath": false,
"lastLevelUpdated": 50,
"maxHealth": 644.0
},
"experience": {
"experienceValue": 1.0,
"currentLevel": 50,
"storage": 0,
"accum": 0,
"needLvUp": false,
"wastelandExperience": 0
},
"relations": {
"relations": [],
"partner": -1,
"lastPartner": -1,
"ascendants": [
-1,
-1,
-1,
-1,
-1,
-1
]
},
"gender": 1,
"stats": {
"stats": [
{
"value": 1,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 2,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 3600.0
},
{
"value": 99,
"mod": 1,
"exp": 3600.0
},
{
"value": 99,
"mod": 0,
"exp": 14400.0
},
{
"value": 99,
"mod": 0,
"exp": 3600.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
}
]
},
"pregnant": false,
"babyReady": false,
"assigned": false,
"sawIncident": false,
"WillGoToWasteland": false,
"WillBeEvicted": false,
"IsEvictedWaitingForFollowers": false,
"skinColor": 4294965454,
"hairColor": 4291535471,
"outfitColor": 4294967295,
"pendingExperienceReward": 0,
"hair": "22",
"equipedOutfit": {
"id": "BattleArmor",
"type": "Outfit",
"hasBeenAssigned": false,
"hasRandonWeaponBeenAssigned": false
},
"equipedWeapon": {
"id": "Fist",
"type": "Weapon",
"hasBeenAssigned": false,
"hasRandonWeaponBeenAssigned": false
},
"savedRoom": -1,
"lastChildBorn": -1,
"rarity": "Normal",
"deathTime": -1,
"daysOnWasteland": 0,
"hoursOnWasteland": 0
},
{
"serializeId": 4,
"name": "Nicole",
"lastName": "Robinson",
"happiness": {
"happinessValue": 100.0
},
"health": {
"healthValue": 644.0,
"radiationValue": 0.0,
"permaDeath": false,
"lastLevelUpdated": 50,
"maxHealth": 644.0
},
"experience": {
"experienceValue": 1.0,
"currentLevel": 50,
"storage": 0,
"accum": 0,
"needLvUp": false,
"wastelandExperience": 0
},
"relations": {
"relations": [],
"partner": -1,
"lastPartner": -1,
"ascendants": [
-1,
-1,
-1,
-1,
-1,
-1
]
},
"gender": 1,
"stats": {
"stats": [
{
"value": 1,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 3600.0
},
{
"value": 99,
"mod": 0,
"exp": 14400.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 3600.0
},
{
"value": 99,
"mod": 0,
"exp": 3600.0
}
]
},
"pregnant": false,
"babyReady": false,
"assigned": false,
"sawIncident": false,
"WillGoToWasteland": false,
"WillBeEvicted": false,
"IsEvictedWaitingForFollowers": false,
"skinColor": 4285552709,
"hairColor": 4289023019,
"outfitColor": 4294967295,
"pendingExperienceReward": 0,
"hair": "12",
"equipedOutfit": {
"id": "jumpsuit",
"type": "Outfit",
"hasBeenAssigned": false,
"hasRandonWeaponBeenAssigned": false
},
"equipedWeapon": {
"id": "Fist",
"type": "Weapon",
"hasBeenAssigned": false,
"hasRandonWeaponBeenAssigned": false
},
"savedRoom": 20,
"lastChildBorn": -1,
"rarity": "Normal",
"deathTime": -1
},
{
"serializeId": 1,
"name": "Ethan",
"lastName": "Snow",
"happiness": {
"happinessValue": 100.0
},
"health": {
"healthValue": 644.0,
"radiationValue": 0.0,
"permaDeath": false,
"lastLevelUpdated": 50,
"maxHealth": 644.0
},
"experience": {
"experienceValue": 1.0,
"currentLevel": 50,
"storage": 0,
"accum": 0,
"needLvUp": false,
"wastelandExperience": 0
},
"relations": {
"relations": [],
"partner": -1,
"lastPartner": -1,
"ascendants": [
-1,
-1,
-1,
-1,
-1,
-1
]
},
"gender": 2,
"stats": {
"stats": [
{
"value": 1,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 72000.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 2,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 1,
"exp": 3600.0
},
{
"value": 99,
"mod": 0,
"exp": 3600.0
}
]
},
"pregnant": false,
"babyReady": false,
"assigned": false,
"sawIncident": false,
"WillGoToWasteland": false,
"WillBeEvicted": false,
"IsEvictedWaitingForFollowers": false,
"skinColor": 4294896868,
"hairColor": 4293440262,
"outfitColor": 4294197569,
"pendingExperienceReward": 0,
"hair": "14",
"equipedOutfit": {
"id": "ScribeRobe_Initiate",
"type": "Outfit",
"hasBeenAssigned": false,
"hasRandonWeaponBeenAssigned": false
},
"equipedWeapon": {
"id": "AlienBlaster_Amplified",
"type": "Weapon",
"hasBeenAssigned": false,
"hasRandonWeaponBeenAssigned": false
},
"savedRoom": 14,
"lastChildBorn": -1,
"rarity": "Common",
"deathTime": -1
},
{
"serializeId": 9,
"name": "Dennis",
"lastName": "Johnson",
"happiness": {
"happinessValue": 100.0
},
"health": {
"healthValue": 644.0,
"radiationValue": 0.0,
"permaDeath": false,
"lastLevelUpdated": 50,
"maxHealth": 644.0
},
"experience": {
"experienceValue": 2916000.0,
"currentLevel": 50,
"storage": 0,
"accum": 0,
"needLvUp": false,
"wastelandExperience": -2147483438
},
"relations": {
"relations": [],
"partner": -1,
"lastPartner": -1,
"ascendants": [
-1,
-1,
-1,
-1,
-1,
-1
]
},
"gender": 2,
"stats": {
"stats": [
{
"value": 1,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 3600.0
},
{
"value": 99,
"mod": 0,
"exp": 14400.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 3600.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 3600.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
}
]
},
"pregnant": false,
"babyReady": false,
"assigned": false,
"sawIncident": false,
"WillGoToWasteland": false,
"WillBeEvicted": false,
"IsEvictedWaitingForFollowers": false,
"skinColor": 4285421123,
"hairColor": 4285028691,
"outfitColor": 4294967295,
"pendingExperienceReward": 0,
"hair": "02",
"faceMask": "f_hair_08",
"equipedOutfit": {
"id": "jumpsuit",
"type": "Outfit",
"hasBeenAssigned": false,
"hasRandonWeaponBeenAssigned": false
},
"equipedWeapon": {
"id": "Fist",
"type": "Weapon",
"hasBeenAssigned": false,
"hasRandonWeaponBeenAssigned": false
},
"savedRoom": 64,
"lastChildBorn": -1,
"rarity": "Normal",
"deathTime": -1
},
{
"serializeId": 6,
"name": "Brenda",
"lastName": "Mitchell",
"happiness": {
"happinessValue": 100.0
},
"health": {
"healthValue": 644.0,
"radiationValue": 0.0,
"permaDeath": false,
"lastLevelUpdated": 50,
"maxHealth": 644.0
},
"experience": {
"experienceValue": 1.0,
"currentLevel": 50,
"storage": 0,
"accum": 0,
"needLvUp": false,
"wastelandExperience": 0
},
"relations": {
"relations": [],
"partner": -1,
"lastPartner": -1,
"ascendants": [
-1,
-1,
-1,
-1,
-1,
-1
]
},
"gender": 1,
"stats": {
"stats": [
{
"value": 1,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 3600.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 3600.0
},
{
"value": 99,
"mod": 0,
"exp": 72000.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
}
]
},
"pregnant": true,
"babyReady": false,
"assigned": false,
"sawIncident": false,
"WillGoToWasteland": false,
"WillBeEvicted": false,
"IsEvictedWaitingForFollowers": false,
"skinColor": 4285552709,
"hairColor": 4294945618,
"outfitColor": 4294967295,
"pendingExperienceReward": 0,
"hair": "02",
"equipedOutfit": {
"id": "jumpsuit",
"type": "Outfit",
"hasBeenAssigned": false,
"hasRandonWeaponBeenAssigned": false
},
"equipedWeapon": {
"id": "Fist",
"type": "Weapon",
"hasBeenAssigned": false,
"hasRandonWeaponBeenAssigned": false
},
"savedRoom": 64,
"lastChildBorn": -1,
"rarity": "Common",
"deathTime": -1
},
{
"serializeId": 11,
"name": "Grace",
"lastName": "Davidson",
"happiness": {
"happinessValue": 100.0
},
"health": {
"healthValue": 644.0,
"radiationValue": 0.0,
"permaDeath": false,
"lastLevelUpdated": 50,
"maxHealth": 644.0
},
"experience": {
"experienceValue": 2916000.0,
"currentLevel": 50,
"storage": 0,
"accum": 0,
"needLvUp": false,
"wastelandExperience": 0
},
"relations": {
"relations": [],
"partner": -1,
"lastPartner": -1,
"ascendants": [
-1,
-1,
-1,
-1,
-1,
-1
]
},
"gender": 1,
"stats": {
"stats": [
{
"value": 1,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 3600.0
},
{
"value": 99,
"mod": 0,
"exp": 14400.0
},
{
"value": 99,
"mod": 0,
"exp": 3600.0
},
{
"value": 99,
"mod": 0,
"exp": 3600.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
}
]
},
"pregnant": true,
"babyReady": false,
"assigned": false,
"sawIncident": false,
"WillGoToWasteland": false,
"WillBeEvicted": false,
"IsEvictedWaitingForFollowers": false,
"skinColor": 4294963174,
"hairColor": 4292039478,
"outfitColor": 4294967295,
"pendingExperienceReward": 0,
"hair": "07",
"equipedOutfit": {
"id": "jumpsuit",
"type": "Outfit",
"hasBeenAssigned": false,
"hasRandonWeaponBeenAssigned": false
},
"equipedWeapon": {
"id": "Fist",
"type": "Weapon",
"hasBeenAssigned": false,
"hasRandonWeaponBeenAssigned": false
},
"savedRoom": 76,
"lastChildBorn": -1,
"rarity": "Normal",
"deathTime": -1
},
{
"serializeId": 7,
"name": "Brittany",
"lastName": "Miller",
"happiness": {
"happinessValue": 100.0
},
"health": {
"healthValue": 644.0,
"radiationValue": 0.0,
"permaDeath": false,
"lastLevelUpdated": 50,
"maxHealth": 644.0
},
"experience": {
"experienceValue": 2916000.0,
"currentLevel": 50,
"storage": 0,
"accum": 0,
"needLvUp": false,
"wastelandExperience": 0
},
"relations": {
"relations": [],
"partner": -1,
"lastPartner": -1,
"ascendants": [
-1,
-1,
-1,
-1,
-1,
-1
]
},
"gender": 1,
"stats": {
"stats": [
{
"value": 1,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 72000.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 0.0
},
{
"value": 99,
"mod": 0,
"exp": 14400.0
}
]
},
"pregnant": true,
"babyReady": false,
"assigned": false,
"sawIncident": false,
"WillGoToWasteland": false,
"WillBeEvicted": false,
"IsEvictedWaitingForFollowers": false,
"skinColor": 4294965454,
"hairColor": 4283848802,
"outfitColor": 4294967295,
"pendingExperienceReward": 0,
"hair": "07",
"equipedOutfit": {
"id": "jumpsuit",
"type": "Outfit",
"hasBeenAssigned": false,
"hasRandonWeaponBeenAssigned": false
},
"equipedWeapon": {
"id": "Fist",
"type": "Weapon",
"hasBeenAssigned": false,
"hasRandonWeaponBeenAssigned": false
},
"savedRoom": 64,
"lastChildBorn": -1,
"rarity": "Common",
"deathTime": -1
}
],
"actors": [],
"id": 0,
"mrhId": 1,
"min_happiness": 0.0
},
"constructMgr": {
"roomDeserializeID": 108
},
"vault": {
"rocks": [
{
"r": 7,
"c": 21
},
{
"r": 7,
"c": 4
},
{
"r": 7,
"c": 9
},
{
"r": 8,
"c": 20
},
{
"r": 8,
"c": 8
},
{
"r": 8,
"c": 17
},
{
"r": 9,
"c": 0
},
{
"r": 9,
"c": 19
},
{
"r": 9,
"c": 4
},
{
"r": 10,
"c": 16
},
{
"r": 10,
"c": 19
},
{
"r": 10,
"c": 3
},
{
"r": 11,
"c": 24
},
{
"r": 11,
"c": 17
},
{
"r": 11,
"c": 5
},
{
"r": 11,
"c": 2
},
{
"r": 12,
"c": 1
},
{
"r": 12,
"c": 11
},
{
"r": 12,
"c": 9
},
{
"r": 12,
"c": 16
},
{
"r": 13,
"c": 8
},
{
"r": 13,
"c": 11
},
{
"r": 13,
"c": 2
},
{
"r": 14,
"c": 22
},
{
"r": 14,
"c": 12
},
{
"r": 14,
"c": 20
},
{
"r": 15,
"c": 23
},
{
"r": 15,
"c": 13
},
{
"r": 15,
"c": 9
},
{
"r": 15,
"c": 15
},
{
"r": 16,
"c": 21
},
{
"r": 16,
"c": 9
},
{
"r": 16,
"c": 14
},
{
"r": 16,
"c": 12
},
{
"r": 16,
"c": 24
},
{
"r": 17,
"c": 9
},
{
"r": 17,
"c": 12
},
{
"r": 18,
"c": 12
},
{
"r": 18,
"c": 14
},
{
"r": 18,
"c": 23
},
{
"r": 18,
"c": 2
},
{
"r": 18,
"c": 5
},
{
"r": 19,
"c": 4
},
{
"r": 19,
"c": 0
},
{
"r": 19,
"c": 10
},
{
"r": 19,
"c": 16
},
{
"r": 19,
"c": 23
},
{
"r": 20,
"c": 13
},
{
"r": 20,
"c": 8
},
{
"r": 20,
"c": 17
},
{
"r": 20,
"c": 4
},
{
"r": 20,
"c": 15
},
{
"r": 21,
"c": 9
},
{
"r": 21,
"c": 16
},
{
"r": 21,
"c": 7
},
{
"r": 21,
"c": 19
},
{
"r": 22,
"c": 12
},
{
"r": 22,
"c": 17
},
{
"r": 22,
"c": 10
},
{
"r": 22,
"c": 2
},
{
"r": 22,
"c": 14
},
{
"r": 23,
"c": 7
},
{
"r": 23,
"c": 22
},
{
"r": 23,
"c": 2
},
{
"r": 23,
"c": 13
},
{
"r": 23,
"c": 11
},
{
"r": 23,
"c": 9
},
{
"r": 24,
"c": 20
},
{
"r": 24,
"c": 3
},
{
"r": 24,
"c": 8
},
{
"r": 24,
"c": 16
}
],
"rooms": [
{
"emergencyDone": false,
"type": "Entrance",
"class": "Facility",
"mergeLevel": 2,
"row": 0,
"col": 3,
"power": true,
"roomHealth": {
"damageValue": 0.0,
"initialValue": 0.0
},
"mrHandyList": [],
"rushTask": -1,
"level": 3,
"dwellers": [],
"deadDwellers": [],
"currentStateName": "Idle",
"currentState": {},
"deserializeID": 1,
"assignedDecoration": "",
"roomVisibility": false,
"roomOutline": false,
"broken": false
},
{
"emergencyDone": false,
"type": "FakeWasteland",
"class": "Facility",
"mergeLevel": 1,
"row": 0,
"col": 0,
"power": true,
"roomHealth": {
"damageValue": 0.0,
"initialValue": 0.0
},
"mrHandyList": [],
"rushTask": -1,
"level": 1,
"dwellers": [],
"deadDwellers": [],
"currentStateName": "Idle",
"currentState": {},
"deserializeID": 2,
"assignedDecoration": "",
"roomVisibility": false,
"roomOutline": false
},
{
"emergencyDone": false,
"type": "Elevator",
"class": "Utility",
"mergeLevel": 1,
"row": 0,
"col": 9,
"power": true,
"roomHealth": {
"damageValue": 0.0,
"initialValue": 0.0
},
"mrHandyList": [],
"rushTask": -1,
"level": 1,
"dwellers": [],
"deadDwellers": [],
"currentStateName": "Idle",
"currentState": {},
"deserializeID": 3,
"assignedDecoration": "",
"roomVisibility": false,
"roomOutline": false,
"withHole": false
},
{
"emergencyDone": false,
"type": "Elevator",
"class": "Utility",
"mergeLevel": 1,
"row": 1,
"col": 9,
"power": true,
"roomHealth": {
"damageValue": 0.0,
"initialValue": 0.0
},
"mrHandyList": [],
"rushTask": -1,
"level": 1,
"dwellers": [],
"deadDwellers": [],
"currentStateName": "Idle",
"currentState": {},
"deserializeID": 4,
"assignedDecoration": "",
"roomVisibility": false,
"roomOutline": false,
"withHole": false
},
{
"emergencyDone": false,
"type": "Elevator",
"class": "Utility",
"mergeLevel": 1,
"row": 2,
"col": 9,
"power": true,
"roomHealth": {
"damageValue": 0.0,
"initialValue": 0.0
},
"mrHandyList": [],
"rushTask": -1,
"level": 1,
"dwellers": [],
"deadDwellers": [],
"currentStateName": "Idle",
"currentState": {},
"deserializeID": 5,
"assignedDecoration": "",
"roomVisibility": false,
"roomOutline": false,
"withHole": false
},
{
"emergencyDone": false,
"type": "LivingQuarters",
"class": "Facility",
"mergeLevel": 1,
"row": 0,
"col": 10,
"power": true,
"roomHealth": {
"damageValue": 500.0,
"initialValue": 226.89
},
"mrHandyList": [],
"rushTask": -1,
"level": 3,
"dwellers": [],
"deadDwellers": [],
"currentStateName": "Idle",
"currentState": {},
"deserializeID": 6,
"assignedDecoration": "",
"roomVisibility": false,
"roomOutline": false,
"children": [],
"partners": []
},
{
"emergencyDone": false,
"type": "Geothermal",
"class": "Production",
"mergeLevel": 1,
"row": 1,
"col": 6,
"power": true,
"roomHealth": {
"damageValue": 0.0,
"initialValue": 289.56
},
"mrHandyList": [],
"rushTask": -1,
"level": 3,
"dwellers": [],
"deadDwellers": [],
"currentStateName": "Idle",
"currentState": {},
"deserializeID": 7,
"assignedDecoration": "",
"roomVisibility": false,
"roomOutline": false,
"storage": {
"resources": {
"Nuka": 0.0,
"Food": 0.0,
"Energy": 0.0,
"Water": 0.0,
"StimPack": 0.0,
"RadAway": 0.0,
"Lunchbox": 0.0,
"MrHandy": 0.0,
"PetCarrier": 0.0,
"CraftedOutfit": 0.0,
"CraftedWeapon": 0.0,
"NukaColaQuantum": 0.0,
"CraftedTheme": 0.0
},
"bonus": {
"Nuka": 0.0,
"Food": 0.0,
"Energy": 0.0,
"Water": 0.0,
"StimPack": 0.0,
"RadAway": 0.0,
"Lunchbox": 0.0,
"MrHandy": 0.0,
"PetCarrier": 0.0,
"CraftedOutfit": 0.0,
"CraftedWeapon": 0.0,
"NukaColaQuantum": 0.0,
"CraftedTheme": 0.0
}
},
"numberOfProductionCycle": 100.0,
"ExperienceRewardIsDirty": false
},
{
"emergencyDone": false,
"type": "Cafeteria",
"class": "Production",
"mergeLevel": 1,
"row": 1,
"col": 3,
"power": true,
"roomHealth": {
"damageValue": 500.0,
"initialValue": 186.69
},
"mrHandyList": [],
"rushTask": 659,
"level": 3,
"dwellers": [],
"deadDwellers": [],
"currentStateName": "Idle",
"currentState": {},
"deserializeID": 8,
"assignedDecoration": "",
"roomVisibility": false,
"roomOutline": false,
"storage": {
"resources": {
"Nuka": 0.0,
"Food": 0.0,
"Energy": 0.0,
"Water": 0.0,
"StimPack": 0.0,
"RadAway": 0.0,
"Lunchbox": 0.0,
"MrHandy": 0.0,
"PetCarrier": 0.0,
"CraftedOutfit": 0.0,
"CraftedWeapon": 0.0,
"NukaColaQuantum": 0.0,
"CraftedTheme": 0.0
},
"bonus": {
"Nuka": 0.0,
"Food": 0.0,
"Energy": 0.0,
"Water": 0.0,
"StimPack": 0.0,
"RadAway": 0.0,
"Lunchbox": 0.0,
"MrHandy": 0.0,
"PetCarrier": 0.0,
"CraftedOutfit": 0.0,
"CraftedWeapon": 0.0,
"NukaColaQuantum": 0.0,
"CraftedTheme": 0.0
}
},
"numberOfProductionCycle": 64.0,
"ExperienceRewardIsDirty": false
},
{
"emergencyDone": false,
"type": "WaterPlant",
"class": "Production",
"mergeLevel": 1,
"row": 1,
"col": 0,
"power": true,
"roomHealth": {
"damageValue": 500.0,
"initialValue": 288.2
},
"mrHandyList": [],
"rushTask": 585,
"level": 3,
"dwellers": [],
"deadDwellers": [],
"currentStateName": "Idle",
"currentState": {},
"deserializeID": 9,
"assignedDecoration": "",
"roomVisibility": false,
"roomOutline": false,
"storage": {
"resources": {
"Nuka": 0.0,
"Food": 0.0,
"Energy": 0.0,
"Water": 0.0,
"StimPack": 0.0,
"RadAway": 0.0,
"Lunchbox": 0.0,
"MrHandy": 0.0,
"PetCarrier": 0.0,
"CraftedOutfit": 0.0,
"CraftedWeapon": 0.0,
"NukaColaQuantum": 0.0,
"CraftedTheme": 0.0
},
"bonus": {
"Nuka": 0.0,
"Food": 0.0,
"Energy": 0.0,
"Water": 0.0,
"StimPack": 0.0,
"RadAway": 0.0,
"Lunchbox": 0.0,
"MrHandy": 0.0,
"PetCarrier": 0.0,
"CraftedOutfit": 0.0,
"CraftedWeapon": 0.0,
"NukaColaQuantum": 0.0,
"CraftedTheme": 0.0
}
},
"numberOfProductionCycle": 77.0,
"ExperienceRewardIsDirty": false
},
{
"emergencyDone": false,
"type": "WaterPlant",
"class": "Production",
"mergeLevel": 1,
"row": 1,
"col": 10,
"power": true,
"roomHealth": {
"damageValue": 100.0,
"initialValue": 57.71
},
"mrHandyList": [],
"rushTask": -1,
"level": 1,
"dwellers": [],
"deadDwellers": [],
"currentStateName": "Idle",
"currentState": {},
"deserializeID": 10,
"assignedDecoration": "",
"roomVisibility": false,
"roomOutline": false,
"storage": {
"resources": {
"Nuka": 0.0,
"Food": 0.0,
"Energy": 0.0,
"Water": 0.0,
"StimPack": 0.0,
"RadAway": 0.0,
"Lunchbox": 0.0,
"MrHandy": 0.0,
"PetCarrier": 0.0,
"CraftedOutfit": 0.0,
"CraftedWeapon": 0.0,
"NukaColaQuantum": 0.0,
"CraftedTheme": 0.0
},
"bonus": {
"Nuka": 0.0,
"Food": 0.0,
"Energy": 0.0,
"Water": 0.0,
"StimPack": 0.0,
"RadAway": 0.0,
"Lunchbox": 0.0,
"MrHandy": 0.0,
"PetCarrier": 0.0,
"CraftedOutfit": 0.0,
"CraftedWeapon": 0.0,
"NukaColaQuantum": 0.0,
"CraftedTheme": 0.0
}
},
"numberOfProductionCycle": 68.0,
"ExperienceRewardIsDirty": false
},
{
"emergencyDone": false,
"type": "Storage",
"class": "Facility",
"mergeLevel": 1,
"row": 2,
"col": 0,
"power": true,
"roomHealth": {
"damageValue": 500.0,
"initialValue": 247.95
},
"mrHandyList": [],
"rushTask": -1,
"level": 3,
"dwellers": [],
"deadDwellers": [],
"currentStateName": "Idle",
"currentState": {},
"deserializeID": 13,
"assignedDecoration": "",
"roomVisibility": false,
"roomOutline": false
},
{
"emergencyDone": false,
"type": "WaterPlant",
"class": "Production",
"mergeLevel": 2,
"row": 2,
"col": 3,
"power": true,
"roomHealth": {
"damageValue": 1000.0,
"initialValue": 494.38
},
"mrHandyList": [],
"rushTask": -1,
"level": 3,
"dwellers": [
1
],
"deadDwellers": [],
"currentStateName": "Idle",
"currentState": {},
"deserializeID": 14,
"assignedDecoration": "",
"roomVisibility": false,
"roomOutline": false,
"storage": {
"resources": {
"Nuka": 7.0,
"Food": 0.0,
"Energy": 0.0,
"Water": 26.0,
"StimPack": 0.0,
"RadAway": 0.0,
"Lunchbox": 0.0,
"MrHandy": 0.0,
"PetCarrier": 0.0,
"CraftedOutfit": 0.0,
"CraftedWeapon": 0.0,
"NukaColaQuantum": 0.0,
"CraftedTheme": 0.0
},
"bonus": {
"Nuka": 0.0,
"Food": 0.0,
"Energy": 0.0,
"Water": 0.0,
"StimPack": 0.0,
"RadAway": 0.0,
"Lunchbox": 0.0,
"MrHandy": 0.0,
"PetCarrier": 0.0,
"CraftedOutfit": 0.0,
"CraftedWeapon": 0.0,
"NukaColaQuantum": 0.0,
"CraftedTheme": 0.0
}
},
"numberOfProductionCycle": 14.0,
"ExperienceRewardIsDirty": false
},
{
"emergencyDone": false,
"type": "Cafeteria",
"class": "Production",
"mergeLevel": 1,
"row": 2,
"col": 10,
"power": true,
"roomHealth": {
"damageValue": 500.0,
"initialValue": 250.79
},
"mrHandyList": [],
"rushTask": -1,
"level": 3,
"dwellers": [],
"deadDwellers": [],
"currentStateName": "Idle",
"currentState": {},
"deserializeID": 15,
"assignedDecoration": "",
"roomVisibility": false,
"roomOutline": false,
"storage": {
"resources": {
"Nuka": 0.0,
"Food": 0.0,
"Energy": 0.0,
"Water": 0.0,
"StimPack": 0.0,
"RadAway": 0.0,
"Lunchbox": 0.0,
"MrHandy": 0.0,
"PetCarrier": 0.0,
"CraftedOutfit": 0.0,
"CraftedWeapon": 0.0,
"NukaColaQuantum": 0.0,
"CraftedTheme": 0.0
},
"bonus": {
"Nuka": 0.0,
"Food": 0.0,
"Energy": 0.0,
"Water": 0.0,
"StimPack": 0.0,
"RadAway": 0.0,
"Lunchbox": 0.0,
"MrHandy": 0.0,
"PetCarrier": 0.0,
"CraftedOutfit": 0.0,
"CraftedWeapon": 0.0,
"NukaColaQuantum": 0.0,
"CraftedTheme": 0.0
}
},
"numberOfProductionCycle": 70.0,
"ExperienceRewardIsDirty": false
},
{
"emergencyDone": false,
"type": "Storage",
"class": "Facility",
"mergeLevel": 1,
"row": 2,
"col": 13,
"power": true,
"roomHealth": {
"damageValue": 500.0,
"initialValue": 228.82
},
"mrHandyList": [],
"rushTask": -1,
"level": 3,
"dwellers": [],
"deadDwellers": [],
"currentStateName": "Idle",
"currentState": {},
"deserializeID": 16,
"assignedDecoration": "",
"roomVisibility": false,
"roomOutline": false
},
{
"emergencyDone": false,
"type": "Storage",
"class": "Facility",
"mergeLevel": 1,
"row": 0,
"col": 13,
"power": true,
"roomHealth": {
"damageValue": 500.0,
"initialValue": 209.81
},
"mrHandyList": [],
"rushTask": -1,
"level": 3,
"dwellers": [],
"deadDwellers": [],
"currentStateName": "Idle",
"currentState": {},
"deserializeID": 17,
"assignedDecoration": "",
"roomVisibility": false,
"roomOutline": false
},
{
"emergencyDone": false,
"type": "LivingQuarters",
"class": "Facility",
"mergeLevel": 1,
"row": 1,
"col": 13,
"power": true,
"roomHealth": {
"damageValue": 500.0,
"initialValue": 153.33
},
"mrHandyList": [],
"rushTask": -1,
"level": 3,
"dwellers": [],
"deadDwellers": [],
"currentStateName": "Idle",
"currentState": {},
"deserializeID": 18,
"assignedDecoration": "",
"roomVisibility": false,
"roomOutline": false,
"children": [],
"partners": []
},
{
"emergencyDone": false,
"type": "Geothermal",
"class": "Production",
"mergeLevel": 1,
"row": 0,
"col": 16,
"power": true,
"roomHealth": {
"damageValue": 9.82,
"initialValue": 191.82
},
"mrHandyList": [],
"rushTask": -1,
"level": 3,
"dwellers": [
4
],
"deadDwellers": [],
"currentStateName": "RoomEmergency",
"currentState": {
"mode": 1,
"controllerAdditionnalData": {}
},
"deserializeID": 20,
"assignedDecoration": "",
"roomVisibility": false,
"roomOutline": false,
"storage": {
"resources": {
"Nuka": 5.0,
"Food": 0.0,
"Energy": 15.0,
"Water": 0.0,
"StimPack": 0.0,
"RadAway": 0.0,
"Lunchbox": 0.0,
"MrHandy": 0.0,
"PetCarrier": 0.0,
"CraftedOutfit": 0.0,
"CraftedWeapon": 0.0,
"NukaColaQuantum": 0.0,
"CraftedTheme": 0.0
},
"bonus": {
"Nuka": 0.0,
"Food": 0.0,
"Energy": 0.0,
"Water": 0.0,
"StimPack": 0.0,
"RadAway": 0.0,
"Lunchbox": 0.0,
"MrHandy": 0.0,
"PetCarrier": 0.0,
"CraftedOutfit": 0.0,
"CraftedWeapon": 0.0,
"NukaColaQuantum": 0.0,
"CraftedTheme": 0.0
}
},
"numberOfProductionCycle": 51.0,
"ExperienceRewardIsDirty": false
},
{
"emergencyDone": false,
"type": "LivingQuarters",
"class": "Facility",
"mergeLevel": 2,
"row": 2,
"col": 16,
"power": true,
"roomHealth": {
"damageValue": 1000.0,
"initialValue": 420.93
},
"mrHandyList": [],
"rushTask": -1,
"level": 3,
"dwellers": [
6,
7,
2,
9
],
"deadDwellers": [],
"currentStateName": "Working",
"currentState": {
"breedingTaskId": 1004
},
"deserializeID": 64,
"assignedDecoration": "",
"roomVisibility": false,
"roomOutline": false,
"children": [],
"partners": [
{
"m": 1,
"f": 10,
"s": "RaisingBaby",
"ascendants": [
1,
10,
-1,
-1,
-1,
-1
],
"maleName": "Ethan",
"maleLast": "Snow",
"femaleName": "Theresa",
"femaleLast": "Boyd",
"childColor": 4294963174,
"childHairColor": 4294170924,
"childRarity": 1,
"childStat": [
5
],
"notificationID": -1,
"fatherId": 1,
"pendingChildren": 0,
"templateID": -1,
"t": 791
},
{
"m": 1,
"f": 3,
"s": "RaisingBaby",
"ascendants": [
1,
3,
-1,
-1,
-1,
-1
],
"maleName": "Ethan",
"maleLast": "Snow",
"femaleName": "Veronique",
"femaleLast": "Perry",
"childColor": 4294896868,
"childHairColor": 4292707102,
"childRarity": 2,
"childStat": [
5
],
"notificationID": -1,
"fatherId": 1,
"pendingChildren": 0,
"templateID": -1,
"t": 800
},
{
"m": 1,
"f": 2,
"s": "RaisingBaby",
"ascendants": [
1,
2,
-1,
-1,
-1,
-1
],
"maleName": "Ethan",
"maleLast": "Snow",
"femaleName": "Nancy",
"femaleLast": "Young",
"childColor": 4287918423,
"childHairColor": 4293247334,
"childRarity": 1,
"childStat": [
5
],
"notificationID": -1,
"fatherId": 1,
"pendingChildren": 0,
"templateID": -1,
"t": 828
},
{
"m": 1,
"f": 11,
"s": "RaisingBaby",
"ascendants": [
1,
11,
-1,
-1,
-1,
-1
],
"maleName": "Ethan",
"maleLast": "Snow",
"femaleName": "Grace",
"femaleLast": "Davidson",
"childColor": 4294897125,
"childHairColor": 4293440262,
"childRarity": 2,
"childStat": [
5
],
"notificationID": -1,
"fatherId": 1,
"pendingChildren": 0,
"templateID": -1,
"t": 843
}
]
},
{
"emergencyDone": false,
"type": "Elevator",
"class": "Utility",
"mergeLevel": 1,
"row": 2,
"col": 22,
"power": true,
"roomHealth": {
"damageValue": 0.0,
"initialValue": 0.0
},
"mrHandyList": [],
"rushTask": -1,
"level": 1,
"dwellers": [],
"deadDwellers": [],
"currentStateName": "Idle",
"currentState": {},
"deserializeID": 66,
"assignedDecoration": "",
"roomVisibility": false,
"roomOutline": false,
"withHole": false
},
{
"emergencyDone": false,
"type": "Elevator",
"class": "Utility",
"mergeLevel": 1,
"row": 3,
"col": 22,
"power": true,
"roomHealth": {
"damageValue": 0.0,
"initialValue": 0.0
},
"mrHandyList": [],
"rushTask": -1,
"level": 1,
"dwellers": [],
"deadDwellers": [],
"currentStateName": "Idle",
"currentState": {},
"deserializeID": 67,
"assignedDecoration": "",
"roomVisibility": false,
"roomOutline": false,
"withHole": false
},
{
"emergencyDone": false,
"type": "Elevator",
"class": "Utility",
"mergeLevel": 1,
"row": 3,
"col": 9,
"power": true,
"roomHealth": {
"damageValue": 0.0,
"initialValue": 0.0
},
"mrHandyList": [],
"rushTask": -1,
"level": 1,
"dwellers": [],
"deadDwellers": [],
"currentStateName": "Idle",
"currentState": {},
"deserializeID": 68,
"assignedDecoration": "",
"roomVisibility": false,
"roomOutline": false,
"withHole": false
},
{
"emergencyDone": false,
"type": "Elevator",
"class": "Utility",
"mergeLevel": 1,
"row": 4,
"col": 9,
"power": true,
"roomHealth": {
"damageValue": 0.0,
"initialValue": 0.0
},
"mrHandyList": [],
"rushTask": -1,
"level": 1,
"dwellers": [],
"deadDwellers": [],
"currentStateName": "Idle",
"currentState": {},
"deserializeID": 69,
"assignedDecoration": "",
"roomVisibility": false,
"roomOutline": false,
"withHole": false
},
{
"emergencyDone": false,
"type": "LivingQuarters",
"class": "Facility",
"mergeLevel": 1,
"row": 2,
"col": 23,
"power": true,
"roomHealth": {
"damageValue": 500.0,
"initialValue": 212.86
},
"mrHandyList": [],
"rushTask": -1,
"level": 3,
"dwellers": [],
"deadDwellers": [],
"currentStateName": "Idle",
"currentState": {},
"deserializeID": 70,
"assignedDecoration": "",
"roomVisibility": false,
"roomOutline": false,
"children": [],
"partners": []
},
{
"emergencyDone": false,
"type": "LivingQuarters",
"class": "Facility",
"mergeLevel": 2,
"row": 0,
"col": 19,
"power": true,
"roomHealth": {
"damageValue": 0.0,
"initialValue": 0.0
},
"mrHandyList": [],
"rushTask": -1,
"level": 3,
"dwellers": [],
"deadDwellers": [],
"currentStateName": "Working",
"currentState": {
"breedingTaskId": 1005
},
"deserializeID": 72,
"assignedDecoration": "",
"roomVisibility": false,
"roomOutline": false,
"children": [],
"partners": [
{
"m": 1,
"f": 6,
"s": "RaisingBaby",
"ascendants": [
1,
6,
-1,
-1,
-1,
-1
],
"maleName": "Ethan",
"maleLast": "Snow",
"femaleName": "Brenda",
"femaleLast": "Mitchell",
"childColor": 4285552709,
"childHairColor": 4293440262,
"childRarity": 3,
"childStat": [
5
],
"notificationID": -1,
"fatherId": 1,
"pendingChildren": 0,
"templateID": -1,
"t": 652
},
{
"m": 1,
"f": 7,
"s": "RaisingBaby",
"ascendants": [
1,
7,
-1,
-1,
-1,
-1
],
"maleName": "Ethan",
"maleLast": "Snow",
"femaleName": "Brittany",
"femaleLast": "Miller",
"childColor": 4294898393,
"childHairColor": 4283848802,
"childRarity": 1,
"childStat": [
5
],
"notificationID": -1,
"fatherId": 1,
"pendingChildren": 0,
"templateID": -1,
"t": 658
}
]
},
{
"emergencyDone": false,
"type": "Geothermal",
"class": "Production",
"mergeLevel": 2,
"row": 3,
"col": 10,
"power": true,
"roomHealth": {
"damageValue": 1000.0,
"initialValue": 338.75
},
"mrHandyList": [],
"rushTask": -1,
"level": 3,
"dwellers": [],
"deadDwellers": [],
"currentStateName": "Idle",
"currentState": {},
"deserializeID": 75,
"assignedDecoration": "",
"roomVisibility": false,
"roomOutline": false,
"storage": {
"resources": {
"Nuka": 7.0,
"Food": 0.0,
"Energy": 23.92,
"Water": 0.0,
"StimPack": 0.0,
"RadAway": 0.0,
"Lunchbox": 0.0,
"MrHandy": 0.0,
"PetCarrier": 0.0,
"CraftedOutfit": 0.0,
"CraftedWeapon": 0.0,
"NukaColaQuantum": 0.0,
"CraftedTheme": 0.0
},
"bonus": {
"Nuka": 0.0,
"Food": 0.0,
"Energy": 0.0,
"Water": 0.0,
"StimPack": 0.0,
"RadAway": 0.0,
"Lunchbox": 0.0,
"MrHandy": 0.0,
"PetCarrier": 0.0,
"CraftedOutfit": 0.0,
"CraftedWeapon": 0.0,
"NukaColaQuantum": 0.0,
"CraftedTheme": 0.0
}
},
"numberOfProductionCycle": 18.0,
"ExperienceRewardIsDirty": false
},
{
"emergencyDone": false,
"type": "LivingQuarters",
"class": "Facility",
"mergeLevel": 3,
"row": 1,
"col": 16,
"power": true,
"roomHealth": {
"damageValue": 1000.0,
"initialValue": 505.76
},
"mrHandyList": [],
"rushTask": -1,
"level": 2,
"dwellers": [
11,
3,
10
],
"deadDwellers": [],
"currentStateName": "Working",
"currentState": {
"breedingTaskId": 1006
},
"deserializeID": 76,
"assignedDecoration": "",
"roomVisibility": false,
"roomOutline": false,
"children": [],
"partners": []
},
{
"emergencyDone": false,
"type": "Cafeteria",
"class": "Production",
"mergeLevel": 2,
"row": 3,
"col": 16,
"power": true,
"roomHealth": {
"damageValue": 0.0,
"initialValue": 0.0
},
"mrHandyList": [],
"rushTask": -1,
"level": 3,
"dwellers": [],
"deadDwellers": [],
"currentStateName": "Idle",
"currentState": {},
"deserializeID": 79,
"assignedDecoration": "",
"roomVisibility": false,
"roomOutline": false,
"storage": {
"resources": {
"Nuka": 0.0,
"Food": 0.0,
"Energy": 0.0,
"Water": 0.0,
"StimPack": 0.0,
"RadAway": 0.0,
"Lunchbox": 0.0,
"MrHandy": 0.0,
"PetCarrier": 0.0,
"CraftedOutfit": 0.0,
"CraftedWeapon": 0.0,
"NukaColaQuantum": 0.0,
"CraftedTheme": 0.0
},
"bonus": {
"Nuka": 0.0,
"Food": 0.0,
"Energy": 0.0,
"Water": 0.0,
"StimPack": 0.0,
"RadAway": 0.0,
"Lunchbox": 0.0,
"MrHandy": 0.0,
"PetCarrier": 0.0,
"CraftedOutfit": 0.0,
"CraftedWeapon": 0.0,
"NukaColaQuantum": 0.0,
"CraftedTheme": 0.0
}
},
"numberOfProductionCycle": 0.0,
"ExperienceRewardIsDirty": false
},
{
"emergencyDone": false,
"type": "LivingQuarters",
"class": "Facility",
"mergeLevel": 1,
"row": 4,
"col": 10,
"power": true,
"roomHealth": {
"damageValue": 0.0,
"initialValue": 97.14
},
"mrHandyList": [],
"rushTask": -1,
"level": 1,
"dwellers": [],
"deadDwellers": [],
"currentStateName": "Idle",
"currentState": {},
"deserializeID": 80,
"assignedDecoration": "",
"roomVisibility": false,
"roomOutline": false,
"children": [],
"partners": []
}
],
"storage": {
"resources": {
"Nuka": 1388.0,
"Food": 9999.0,
"Energy": 9999.0,
"Water": 9999.0,
"StimPack": 9999999.0,
"RadAway": 9999999.0,
"Lunchbox": 0.0,
"MrHandy": 0.0,
"PetCarrier": 0.0,
"CraftedOutfit": 0.0,
"CraftedWeapon": 0.0,
"NukaColaQuantum": 0.0,
"CraftedTheme": 0.0
},
"bonus": {
"Nuka": 0.0,
"Food": 0.0,
"Energy": 0.0,
"Water": 0.0,
"StimPack": 0.0,
"RadAway": 0.0,
"Lunchbox": 0.0,
"MrHandy": 0.0,
"PetCarrier": 0.0,
"CraftedOutfit": 0.0,
"CraftedWeapon": 0.0,
"NukaColaQuantum": 0.0,
"CraftedTheme": 0.0
}
},
"inventory": {
"items": [
{
"id": "PrinceSpecial",
"type": "Outfit",
"hasBeenAssigned": false,
"hasRandonWeaponBeenAssigned": false
},
{
"id": "ChemistrySet",
"type": "Junk",
"hasBeenAssigned": false,
"hasRandonWeaponBeenAssigned": false
},
{
"id": "TeddyBear",
"type": "Junk",
"hasBeenAssigned": false,
"hasRandonWeaponBeenAssigned": false
}
]
},
"emergencyData": {
"active": true,
"cicleTaskId": 1002,
"mode": 1,
"consumedFood": 0.0,
"consumedEnergy": 0.0,
"consumedWater": 0.0,
"emergencyConsumption": "VaultEmergencyConsumption",
"randomEventTaskId": 1
},
"roomConsumption": {
"taskIdOnline": 2,
"taskIDShutDown": 3
},
"dwellerWaterConsumption": {
"taskIdOnline": 4
},
"dwellerFoodConsumption": {
"taskIdOnline": 5
},
"lunchboxRandomGenerator": "0001000000FFFFFFFF010000000000000004010000000D53797374656D2E52616E646F6D0300000005696E65787406696E65787470095365656441727261790000070808081D0000000500000009020000000F02000000380000000800000000A86D7720564E67678775DE4BC9BF60332CE773262BACCA3AD512971CDDBEC668EC8AAE737EDDF634C20480758D7BB625CC8B7548F4EB9D2602AA5766D9670E18F7DE7A013C12C1268ECEB11A8F170F0F8760AF0CB85A3E4886546A15C2484029418F1936FEBB737290E6125214A6EF5FDD89B24FDC0C3E7686EED13D80C2623592D8422C986AFE5498D7D316D3F17A3E0DD98A17F809386912D1A01431856542303FC14D7368BB41CA7CD666DB85560C5E42F53D76058768011B86758457F138AADCB767F821A45DA050206FA80C671EB2481C63692F5E5265401B160B",
"LunchBoxesByType": [],
"LunchBoxesCount": 0,
"VaultName": "652",
"VaultMode": "Normal",
"VaultTheme": 0,
"Achievements": {
"objectivesInProgress": [
{
"objectiveID": "rush_success_01",
"requirements": [
{
"requirementID": "rush_success_01",
"satisfied": false,
"rushCount": 29
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "merge_power_01",
"requirements": [
{
"requirementID": "merge_power_01",
"satisfied": false,
"higherMergeLevelFound": 2
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "wasteland_kill_01",
"requirements": [
{
"requirementID": "wasteland_kill_01",
"satisfied": false,
"currentCreatures": 37
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "merge_food_01",
"requirements": [
{
"requirementID": "merge_food_01",
"satisfied": false,
"higherMergeLevelFound": 2
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "merge_water_01",
"requirements": [
{
"requirementID": "merge_water_01",
"satisfied": false,
"higherMergeLevelFound": 2
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "make_baby_01",
"requirements": [
{
"requirementID": "make_baby_01",
"satisfied": false,
"currentBabies": 0
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "upgrade_room_01",
"requirements": [
{
"requirementID": "upgrade_room_01",
"satisfied": false,
"acceptedRoom": 19
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "build_nukacola_01",
"requirements": [
{
"requirementID": "build_nukacola_01",
"satisfied": false,
"acceptedRoom": 0
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "build_rooms_02",
"requirements": [
{
"requirementID": "build_rooms_02",
"satisfied": false,
"acceptedRoom": 26
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "stop_raider_01",
"requirements": [
{
"requirementID": "stop_raider_01",
"satisfied": false,
"currentInvasions": 0
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "build_each_rooms_01",
"requirements": [
{
"requirementID": "build_each_rooms_01_casino",
"satisfied": false,
"acceptedRoom": 0
},
{
"requirementID": "build_each_rooms_01_storage",
"satisfied": true,
"acceptedRoom": 3
},
{
"requirementID": "build_each_rooms_01_nukeCola",
"satisfied": false,
"acceptedRoom": 0
},
{
"requirementID": "build_each_rooms_01_superRoom2",
"satisfied": false,
"acceptedRoom": 0
},
{
"requirementID": "build_each_rooms_01_hydroponic",
"satisfied": false,
"acceptedRoom": 0
},
{
"requirementID": "build_each_rooms_01_scienceLab",
"satisfied": false,
"acceptedRoom": 0
},
{
"requirementID": "build_each_rooms_01_medBay",
"satisfied": false,
"acceptedRoom": 0
},
{
"requirementID": "build_each_rooms_01_classroom",
"satisfied": false,
"acceptedRoom": 0
},
{
"requirementID": "build_each_rooms_01_armory",
"satisfied": false,
"acceptedRoom": 0
},
{
"requirementID": "build_each_rooms_01_radio",
"satisfied": false,
"acceptedRoom": 0
},
{
"requirementID": "build_each_rooms_01_water2",
"satisfied": false,
"acceptedRoom": 0
},
{
"requirementID": "build_each_rooms_01_energy2",
"satisfied": false,
"acceptedRoom": 0
},
{
"requirementID": "build_each_rooms_01_gym",
"satisfied": false,
"acceptedRoom": 0
},
{
"requirementID": "build_each_rooms_01_bar",
"satisfied": false,
"acceptedRoom": 0
},
{
"requirementID": "build_each_rooms_01_dojo",
"satisfied": false,
"acceptedRoom": 0
},
{
"requirementID": "build_each_rooms_01_livingquarters",
"satisfied": true,
"acceptedRoom": 11
},
{
"requirementID": "build_each_rooms_01_geothermal",
"satisfied": true,
"acceptedRoom": 4
},
{
"requirementID": "build_each_rooms_01_cafeteria",
"satisfied": true,
"acceptedRoom": 4
},
{
"requirementID": "build_each_rooms_01_waterplant",
"satisfied": true,
"acceptedRoom": 4
},
{
"requirementID": "build_each_rooms_01_overseer",
"satisfied": false,
"acceptedRoom": 0
},
{
"requirementID": "build_each_rooms_01_weaponfactory",
"satisfied": false,
"acceptedRoom": 0
},
{
"requirementID": "build_each_rooms_01_outfitfactory",
"satisfied": false,
"acceptedRoom": 0
},
{
"requirementID": "build_each_rooms_01_designfactory",
"satisfied": false,
"acceptedRoom": 0
},
{
"requirementID": "build_each_rooms_01_barbershop",
"satisfied": false,
"acceptedRoom": 0
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "completed_objectives_01",
"requirements": [
{
"requirementID": "completed_objectives_01",
"satisfied": false,
"numberOfObjectivesCompleted": 17
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "collect_legendary_dwellers_01",
"requirements": [
{
"requirementID": "collect_legendary_dwellers_01",
"satisfied": false,
"currentLegendaryDwellers": 0
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "collect_legendary_outfits_01",
"requirements": [
{
"requirementID": "collect_legendary_outfits_01",
"satisfied": false,
"currentLegendaryOutfits": 0
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "collect_legendary_weapons_01",
"requirements": [
{
"requirementID": "collect_legendary_weapons_01",
"satisfied": false,
"currentLegendaryWeapons": 1
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "complete_quests_10",
"requirements": [
{
"requirementID": "complete_quests_10",
"satisfied": false,
"currentNumberQuestsCompleted": 0
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "complete_quests_30",
"requirements": [
{
"requirementID": "complete_quests_30",
"satisfied": false,
"currentNumberQuestsCompleted": 0
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "complete_quests_60",
"requirements": [
{
"requirementID": "complete_quests_60",
"satisfied": false,
"currentNumberQuestsCompleted": 0
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "complete_quests_100",
"requirements": [
{
"requirementID": "complete_quests_100",
"satisfied": false,
"currentNumberQuestsCompleted": 0
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "barbershop_10",
"requirements": [
{
"requirementID": "barbershop_10",
"satisfied": false,
"acceptedRoom": 0
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "craft_10_weapons",
"requirements": [
{
"requirementID": "craft_10_weapons",
"satisfied": false,
"currentNumberItems": 0
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "craft_10_outfits",
"requirements": [
{
"requirementID": "craft_10_outfits",
"satisfied": false,
"currentNumberItems": 0
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "craft_outfit_weapon_theme",
"requirements": [
{
"requirementID": "craft_outfit",
"satisfied": false,
"currentNumberItems": 0
},
{
"requirementID": "craft_weapon",
"satisfied": false,
"currentNumberItems": 0
},
{
"requirementID": "craft_theme",
"satisfied": false,
"currentNumberItems": 0
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "",
"requirements": [
{
"requirementID": "scrap_items_500",
"satisfied": false,
"currentNumberItems": 0
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "collect_theme_all_parts_1",
"requirements": [
{
"requirementID": "collect_theme_all_parts_1",
"satisfied": false,
"currentAllThemeParts": 0
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "collect_theme_all_parts_4",
"requirements": [
{
"requirementID": "collect_theme_all_parts_4",
"satisfied": false,
"currentAllThemeParts": 0
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "quest_dialog_choices_01",
"requirements": [
{
"requirementID": "quest_dialog_choices_01",
"satisfied": false,
"currentNumberQuestDialogChoicesMade": 0
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "craft_themes_8",
"requirements": [
{
"requirementID": "craft_themes_8",
"satisfied": false,
"currentNumberItems": 0
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "kill_on_quests_500",
"requirements": [
{
"requirementID": "kill_on_quests_500",
"satisfied": false,
"currentNumberQuestEnemiesToKill": 0
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "stimpacks_100_radaway_100",
"requirements": [
{
"requirementID": "stimpack_100",
"satisfied": false,
"currentNumberLoot": 0
},
{
"requirementID": "radaway_100",
"satisfied": false,
"currentNumberLoot": 0
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "boss_kill_10",
"requirements": [
{
"requirementID": "boss_kill_10",
"satisfied": false,
"currentNumberQuestEnemiesToKill": 0
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "build_rooms_stat",
"requirements": [
{
"requirementID": "build_rooms_stat",
"satisfied": false,
"acceptedRoom": 26
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "collect_caps_stat",
"requirements": [
{
"requirementID": "collect_caps_stat",
"satisfied": false,
"nukaCount": 41323.0,
"isAchievement": true
}
],
"completed": false,
"incrementLevel": 0
}
],
"completed": [
"levelup_dweller_01",
"collect_caps_01",
"levelup_dweller_02",
"levelup_dweller_03",
"collect_caps_02",
"build_rooms_01"
]
},
"wasteland": {
"state": {
"name": "Running"
},
"teams": [
{
"dwellers": [
8
],
"returnTripDuration": 593.5,
"elapsedTimeAliveExploring": 1187,
"elapsedReturningTime": 527,
"teamIndex": 0,
"teamType": "Explore",
"status": "ReturningToVault",
"logs": [
"0000@l2W",
"0000@l44",
"0001@l5#l8",
"0001@l9",
"0001@lA",
"0001@l2a#s10@l2f#r025",
"0002@l2c#r0297",
"0002@l1b#l1u",
"0002@l2D",
"0002@l1w",
"0002@l2e#s1@l2a#s915",
"0003@l1Z#l1m",
"0003@l1v",
"0003@l1o",
"0003@l2a#s255",
"0005@l1a#l1q",
"0005@l25",
"0005@l1s",
"0005@l2a#s285",
"0006@l1Y#l1i",
"0006@l1n",
"0006@l1k",
"0006@l2a#s225",
"0008@ll#ls",
"0008@lt",
"0008@lu",
"0008@l2a#s35",
"0009@l1Z#l1q",
"0009@l29",
"0009@l1s",
"0009@l2a#s285",
"0010@l1X#l1i",
"0010@l1n",
"0010@l1k",
"0010@l2a#s225",
"0011@l1b#l1q",
"0011@l29",
"0011@l1s",
"0011@l2a#s285",
"0011@l2k#io6",
"0012@l1c#l1e",
"0012@l1f",
"0012@l1g",
"0012@l2a#s200",
"0013@lh#lo",
"0013@lp",
"0013@lq",
"0013@l2a#s25",
"0014@l1Y#l1q",
"0014@l29",
"0014@l1s",
"0014@l2a#s285",
"0017@l0#s0",
"0017@l2c#r0792",
"0017@l1a#l1e",
"0017@l1j",
"0017@l1g",
"0017@l2a#s200",
"0019@l1Z#l1q",
"0019@l25",
"0019@l1s",
"0019@l2a#s285"
],
"missedEncounters": 0,
"introMessages": 1,
"notificationID": -1,
"notificationQuestArrivalID": -1,
"isForceReturningToVault": false,
"isSkippingTime": false,
"isDoingQuest": false,
"questSucceeded": false,
"questDone": false,
"teamEquipment": {
"storage": {
"resources": {
"Nuka": 1114.0,
"Food": 0.0,
"Energy": 0.0,
"Water": 0.0,
"StimPack": 25.0,
"RadAway": 25.0,
"Lunchbox": 0.0,
"MrHandy": 0.0,
"PetCarrier": 0.0,
"CraftedOutfit": 0.0,
"CraftedWeapon": 0.0,
"NukaColaQuantum": 0.0,
"CraftedTheme": 0.0
},
"bonus": {
"Nuka": 0.0,
"Food": 0.0,
"Energy": 0.0,
"Water": 0.0,
"StimPack": 0.0,
"RadAway": 0.0,
"Lunchbox": 0.0,
"MrHandy": 0.0,
"PetCarrier": 0.0,
"CraftedOutfit": 0.0,
"CraftedWeapon": 0.0,
"NukaColaQuantum": 0.0,
"CraftedTheme": 0.0
}
},
"inventory": {
"items": []
},
"dwellers": [],
"questClues": [],
"collectedThemes": {
"themeList": []
}
},
"questteamQuestSeed": -1,
"retryCount": 0,
"questOverallPerformance": {
"numberCombatsWon": 0,
"numberCriticalHitsPerformed": 0,
"numberPerfectCriticalHitsPerformed": 0,
"numberContainersCollected": 0,
"numberCapsCollected": 0,
"numberLevelsGained": 0,
"numberLevelsGainedWithBonus": 0
},
"actors": [],
"initialItems": [
{
"initWeapon": "Fist",
"initOutfit": "jumpsuit",
"initPet": ""
}
],
"postQuestStimpakDifference": 0,
"postQuestRadawayDifference": 0,
"initialRadDamage": [
0.0
]
}
],
"cycles": [
{
"cycleType": "Radiation",
"taskId": 213
},
{
"cycleType": "Experience",
"taskId": 214
},
{
"cycleType": "NukaCaps",
"taskId": 215
},
{
"cycleType": "Encounters",
"taskId": 216,
"pendingCycles": [],
"teamEncounters": [
{
"teamEncounters": [
{
"nonRepeatable": [],
"poolType": "Creatures"
},
{
"nonRepeatable": [
"Location2",
"Location1"
],
"poolType": "Location"
},
{
"nonRepeatable": [
"NPC1"
],
"poolType": "NPC"
}
],
"teamID": 8
}
],
"inProgress": []
},
{
"cycleType": "Item",
"taskId": 217,
"pendingCycles": []
},
{
"cycleType": "Speech",
"taskId": 218
},
{
"cycleType": "Junk",
"taskId": 219,
"pendingCycles": [],
"teamEncounters": [
{
"teamEncounters": [
{
"nonRepeatable": [],
"poolType": "JunkCycle_Location"
}
],
"teamID": 8
}
],
"inProgress": []
}
],
"mrHandyCycles": [
{
"cycleType": "Speech",
"taskId": 220
},
{
"cycleType": "NukaCaps",
"taskId": 221
}
],
"questCycles": [],
"allTimeTeamsCounter": 9,
"lastSurpriseQuest": "",
"lastSurprisePopupTime": -1.0
}
},
"dwellerSpawner": {
"dwellersWaiting": []
},
"deviceName": "DESKTOP-08TF5K6",
"tutorialManager": {
"phase": "Phase5",
"taskNumber": 0,
"objectivesTutorialMessage": true,
"lunchboxTutorialMessage": true,
"showingObjectiveTutorialMessage": false,
"showingLunchboxTutorialMessage": false,
"showWastelandMessageTime": 300.0,
"showExploreWastelandMessageTime": 10.0,
"exploreWastelandMessageShown": true,
"skippedTutorial": 0,
"questTutorialCompleted": 0,
"intialTimerTasks": [],
"ContextualVaultTecObjectives": false,
"ContextualAddFriends": false,
"ContextualWasteland": true,
"ContextualRadioRoom": false,
"ContextualWeaponsAndOutfits": true,
"ContextualTrainDweller": false,
"ContextualBabies": true,
"ContextualDestroyRocks": true,
"ContextualStorage": true,
"ContextualNoRoomForDwellers": true,
"ContextualUnequipedDweller": true,
"ContextualBuildAnElevator": false,
"ContextualDestroyRockToBuild": false,
"ContextualNoBuildZonesAvailableByRock": false,
"ContextualDestroyRockToAccessNextFloor": false,
"ContextualResourcesAlert": false,
"ContextualIncidentOcurs": true,
"ContextualLowPowerAlert": false,
"ContextualStorageFull": false,
"ContextualMergeOrUpgradeRoom": true,
"ContextualWastelandMessage": true,
"ContextualObjectivesCompleted": true,
"ContextualBabiesTutorial": true,
"ContextualStimpackMessage": false,
"ContextualLunchboxTutorial": true,
"ContextualRadwayMessage": false,
"ContextualRoomMerge2": true,
"ContextualRoomMerge3": true,
"ContextualStorage2": false,
"ContextualEquippingItemsWeapon": false,
"ContextualLuck": true,
"ContextualEquppingItemsPet": false,
"ContextualCrafting": false,
"ContextualDecorations": false,
"ContextualRequestJunk": false,
"ContextualJunk": true,
"ContextualTriggeredBirth": false,
"ContextualInventoryFull": false,
"ContextualInventoryFullWindow": false,
"ContextualJunkGiveAway": false,
"ContextualScrapping": false,
"ContextualAssignWith3DTouch": false,
"ContextualNukaQuantum": true,
"ContextualSurpriseQuests": false,
"ContextualReturningFromQuests": false,
"ContextualRadioRoomToggle": false,
"ContextualCraftTheme": false,
"ContextualJoystickNavigationInVault": false,
"MaleTasksQuant": 0,
"FemaleTasksQuant": 0
},
"objectiveMgr": {
"taskID": 8,
"canDiscard": true,
"nukaQuantumIncrement": 0,
"shuffleBags": [
[
"QuestPerformPerfectCriticalHit1",
"CollectRareWeapons1",
"QuestPerformCriticalHit1_Survival",
"KillRandomEnemyUnarmedHardcore1",
"StopMoleratWithoutPropagate1",
"EquipRandomWeapon",
"RushRoom1",
"RaiseSpecificSpecialLevel1",
"Radaway1",
"ExtinguishFireHardcore1",
"CraftRareOutfits1",
"UpgradeRoom1",
"QuestWinBattles1",
"CollectWeapons1",
"SurviveMoleratNoCasualtiesHardcore1",
"KillDeathclawAtVaultDoor1",
"RushRoomHardcore1",
"SellJunk1",
"Outfits1",
"Food1",
"Water1_Survival",
"QuestWinBattles1_Survival",
"BaldDwellersInVault",
"StopIncidentsHardcore1",
"SurviveMoleratNoCasualties1",
"CollectFoodWithinOneMin1",
"KillCreatureHardcore1",
"RightRoom1",
"ExploreWastelandHours1_Survival",
"CollectPowerWithinOneMin1",
"Stimpack1",
"KillCreature1",
"CraftWeapons1",
"SurviveRadscorpionNoCasualties1_Survival",
"RaiseSpecialLevel1",
"CraftOutfits1",
"Power1",
"MysteriousStranger1",
"SurviveDeathclawNoCasualties1_Survival",
"Water1",
"Babies1",
"SurviveGhoulNoCasualties1_Survival",
"KillCreature1_Survival",
"CraftRareWeapons1",
"SurviveDeathclawNoCasualtiesHardcore1",
"ScrapItems1",
"StopRadroachWithoutPropagate1",
"WastelandLevelUp1",
"CollectOutfitsHardcore1",
"CollectRareOutfits1",
"Wasteland1",
"CollectOutfits1",
"Food1_Survival",
"CollectWeaponsHardcore1",
"QuestPerformPerfectCriticalHit1_Survival",
"DwellerHappy1",
"ExploreWastelandHoursWithoutRadaway1",
"MakeFriend1",
"RushRoom1_Survival",
"BarberShopUse1",
"WastelandCollectCaps1",
"CollectCaps1",
"Weapons1",
"SurviveGhoulNoCasualties1",
"SurviveRadscorpionNoCasualties1",
"QuestCollectCaps1",
"SurviveMoleratNoCasualties1_Survival",
"RandomEnemyKilledUnarmed1_Survival",
"StimpackHardcore1",
"CompleteQuestInWasteland1",
"CollectJunk1",
"QuestCollectContainers1_Survival",
"SurviveHoursNoCasualties1",
"WastelandWithPet1",
"LevelUp1",
"StopIncidents1_Survival",
"CollectPets1",
"QuestCollectContainers1",
"Couple1",
"QuestCollectCaps1_Survival",
"DwellerHappy1_Survival",
"CollectWaterWithinOneMin1",
"KillRaiderAtVaultDoor1",
"ExploreLocation1",
"ExtinguishFire1",
"OutfitsHardcore1",
"QuestPerformCriticalHit1",
"RandomEnemyKilledUnarmed",
"CompleteQuestInOverseer1",
"CompleteQuestInWasteland1_Survival",
"WeaponsHardcore1",
"CompleteQuestInOverseer1_Survival",
"MergeTwoRooms1",
"SellItemHarcore1",
"StopIncidents1",
"SurviveDeathclawNoCasualties1"
],
[
"WeaponsHardcore2",
"StopMoleratWithoutPropagate2",
"Power2_Survival",
"EquipRandomWeapon2",
"QuestCollectContainers2_Survival",
"SurviveRadscorpionNoCasualties2",
"QuestPerformCriticalHit2_Survival",
"KillRaiderAtVaultDoor2",
"CollectFoodWithinOneMin2",
"ExploreWastelandHoursWithoutRadaway2",
"RandomEnemyKilledUnarmed2_Survival",
"LevelUp2",
"ExtinguishFireHardcore2",
"Outfits2",
"StopRadroachWithoutPropagate2",
"ExploreLocation2",
"WastelandWithPet2",
"QuestWinBattles2",
"QuestPerformPerfectCriticalHit2_Survival",
"SurviveMoleratNoCasualties2",
"Weapons2",
"StopIncidents2_Survival",
"QuestPerformCriticalHit2",
"StopIncidents2",
"CollectWeapons2",
"WastelandCollectCaps2",
"CollectPets2",
"Couple2",
"SurviveMoleratNoCasualties2_Survival",
"CompleteQuestInWasteland2_Survival",
"KillDeathclawAtVaultDoor2",
"DwellerHappy2",
"SurviveHoursNoCasualties2",
"CollectCaps2",
"RightRoom2",
"CollectOutfits2",
"KillCreature2",
"Pregnant2",
"RaiseSpecificSpecialLevel2",
"BaldDwellersInVault2",
"SurviveGhoulNoCasualties2",
"Water2_Survival",
"ExploreWastelandHours2",
"SellJunk2",
"WastelandLevelUp2",
"CollectWaterWithinOneMin2",
"CraftRareWeapons2",
"QuestWinBattles2_Survival",
"Water2",
"OutfitsHardcore2",
"MergeTwoRooms2",
"Food2_Survival",
"CollectOutfitsHardcore2",
"SurviveGhoulNoCasualties2_Survival",
"SellItemHarcore2",
"SurviveDeathclawNoCasualties2",
"RushRoomHardcore2",
"CollectPowerWithinOneMin2",
"CompleteQuestInOverseer2",
"CraftRareOutfits2",
"RushRoom2",
"CollectRareOutfits2",
"QuestCollectCaps2_Survival",
"QuestCollectContainers2",
"MysteriousStranger2",
"BarberShopUse2",
"UpgradeRoom2",
"SurviveMoleratNoCasualtiesHardcore2",
"Babies2",
"SurviveDeathclawNoCasualtiesHardcore2",
"CollectWeaponsHardcore2",
"CollectRareWeapons2",
"SurviveDeathclawNoCasualties2_Survival",
"CraftWeapons2",
"SurviveRadscorpionNoCasualties2_Survival",
"MakeFriend2",
"RushRoom2_Survival",
"StopIncidentsHardcore2",
"KillCreatureHardcore2",
"CollectJunk2",
"QuestCollectCaps2",
"ExploreWastelandHours2_Survival",
"CompleteQuestInOverseer2_Survival",
"Power2",
"ExtinguishFire2",
"KillRandomEnemyUnarmedHardcore2",
"Radaway2",
"RaiseSpecialLevel2",
"ScrapItems2",
"Stimpack2",
"QuestPerformPerfectCriticalHit2",
"StimpackHardcore2",
"Wasteland2",
"RandomEnemyKilledUnarmed2",
"CompleteQuestInWasteland2",
"CraftOutfits2"
],
[
"QuestPerformCriticalHit3_Survival",
"KillCreatureHardcore3",
"CompleteQuestInOverseer3",
"WastelandWithPet3",
"QuestCollectCaps3_Survival",
"BaldDwellersInVault3",
"KillRaiderAtVaultDoor3",
"CompleteQuestInWasteland3",
"EquipRandomWeapon3",
"Radaway3",
"CollectOutfits3",
"DwellerHappy3_Survival",
"Wasteland3",
"QuestPerformPerfectCriticalHit3",
"SurviveMoleratNoCasualtiesHardcore3",
"RightRoom3",
"UpgradeRoom3",
"DwellerHappy3",
"CraftRareWeapons3",
"CollectWaterWithinOneMin3",
"OutfitsHardcore3",
"SurviveRadscorpionNoCasualties3_Survival",
"ExploreLocation3",
"RaiseSpecialLevel3",
"QuestPerformPerfectCriticalHit3_Survival",
"Food3_Survival",
"StopRadroachWithoutPropagate3",
"RushRoom3_Survival",
"CollectPowerWithinOneMin3",
"CollectJunk3",
"StopIncidents3_Survival",
"StopMoleratWithoutPropagate3",
"OutfitsT1",
"KillRandomEnemyUnarmedHardcore3",
"MakeFriend3",
"Outfits3",
"QuestCollectContainers3_Survival",
"Stimpack3",
"RaiseSpecificSpecialLevel3",
"ExtinguishFire3",
"CollectPets3",
"Water3_Survival",
"LevelUp3",
"CraftWeapons3",
"SellJunk3",
"SurviveDeathclawNoCasualties3_Survival",
"WastelandLevelUp3",
"ExtinguishFireHardcore3",
"CollectCaps3",
"KillCreature3_Survival",
"RushRoomHardcore3",
"QuestCollectContainers3",
"SurviveMoleratNoCasualties3",
"QuestWinBattles3",
"KillDeathclawAtVaultDoor3",
"StimpackHardcore3",
"CollectFoodWithinOneMin3",
"SurviveDeathclawNoCasualtiesHardcore3",
"StopIncidents3",
"Pregnant3",
"Power3_Survival",
"SurviveGhoulNoCasualties3",
"CollectWeaponsHardcore3",
"SurviveHoursNoCasualties3",
"ExploreWastelandHoursWithoutRadaway3",
"ExploreWastelandHours3",
"ExploreWastelandHours3_Survival",
"SellItemHarcore3",
"SurviveDeathclawNoCasualties3",
"CompleteQuestInOverseer3_Survival",
"Couple3",
"StopIncidentsHardcore3",
"RandomEnemyKilledUnarmed3_Survival",
"WastelandCollectCaps3",
"Babies3",
"BarberShopUse3",
"Weapons3",
"QuestWinBattles3_Survival",
"SurviveMoleratNoCasualties3_Survival",
"CompleteQuestInWasteland3_Survival",
"WeaponsHardcore3",
"Food3",
"QuestCollectCaps3",
"WeaponsT1",
"RushRoom3",
"RandomEnemyKilledUnarmed3",
"RadawayT1",
"CollectOutfitsHardcore3",
"CollectRareWeapons3",
"MysteriousStranger3",
"QuestPerformCriticalHit3",
"SurviveGhoulNoCasualties3_Survival",
"CraftRareOutfits3",
"Water3",
"CraftOutfits3",
"MergeTwoRooms3",
"SurviveRadscorpionNoCasualties3",
"KillCreature3",
"CollectWeapons3",
"ScrapItems3",
"CollectRareOutfits3"
],
[
"SurviveGhoulNoCasualties4_Survival",
"DwellerHappy4_Survival",
"BaldDwellersInVault4",
"SellItemHarcore4",
"MergeTwoRooms4",
"Weapons4",
"RandomEnemyKilledUnarmed4",
"SellJunk4",
"QuestCollectContainers4_Survival",
"WastelandLevelUp4",
"CollectJunk4",
"QuestPerformPerfectCriticalHit4",
"QuestPerformCriticalHit4_Survival",
"StimpackHardcore4",
"Water4_Survival",
"RushRoomHardcore4",
"SurviveDeathclawNoCasualtiesHardcore4",
"QuestPerformCriticalHit4",
"SurviveRadscorpionNoCasualties4",
"WeaponsHardcore4",
"UpgradeRoom4",
"CollectWeapons4",
"CollectWaterWithinOneMin4",
"SurviveDeathclawNoCasualties4",
"SurviveGhoulNoCasualties4",
"Stimpack4",
"Pregnant4",
"KillCreature4_Survival",
"QuestWinBattles4",
"ExploreWastelandHours4_Survival",
"CompleteQuestInWasteland4",
"KillCreature4",
"EquipRandomWeapon4",
"Food4_Survival",
"ExtinguishFireHardcore4",
"SurviveMoleratNoCasualtiesHardcore4",
"DwellerHappy4",
"RushRoom4",
"Wasteland4",
"ExploreWastelandHoursWithoutRadaway4",
"QuestPerformPerfectCriticalHit4_Survival",
"StopMoleratWithoutPropagate4",
"CompleteQuestInOverseer4",
"RandomEnemyKilledUnarmed4_Survival",
"CraftWeapons4",
"Radaway4",
"CraftRareOutfits4",
"StopIncidents4",
"ScrapItems4",
"SurviveMoleratNoCasualties4",
"Power4",
"CollectOutfits4",
"SurviveDeathclawNoCasualties4_Survival",
"StopIncidentsHardcore4",
"StopIncidents4_Survival",
"StopRadroachWithoutPropagate4",
"ExtinguishFire4",
"RaiseSpecialLevel4",
"QuestWinBattles4_Survival",
"QuestCollectContainers4",
"CraftOutfits4",
"BarberShopUse4",
"MysteriousStranger4",
"SurviveMoleratNoCasualties4_Survival",
"Food4",
"RaiseSpecificSpecialLevel4",
"Babies4",
"RushRoom4_Survival",
"SurviveRadscorpionNoCasualties4_Survival",
"CompleteQuestInWasteland4_Survival",
"Outfits4",
"CraftRareWeapons4",
"WastelandWithPet4",
"KillRandomEnemyUnarmedHardcore4",
"ExploreWastelandHours4",
"CollectPowerWithinOneMin4",
"QuestCollectCaps4_Survival",
"CollectRareOutfits4",
"ExploreLocation4",
"RightRoom4",
"CollectCaps4",
"QuestCollectCaps4",
"MakeFriend4",
"WastelandCollectCaps4",
"Power4_Survival",
"KillDeathclawAtVaultDoor4",
"CollectRareWeapons4",
"Water4",
"CollectOutfitsHardcore4",
"SurviveHoursNoCasualties4",
"CompleteQuestInOverseer4_Survival",
"CollectFoodWithinOneMin4",
"CollectPets4",
"KillCreatureHardcore4",
"OutfitsHardcore4",
"CollectWeaponsHardcore4",
"KillRaiderAtVaultDoor4",
"Couple4"
],
[
"CollectRareOutfits5",
"CollectWaterWithinOneMin5",
"ExploreWastelandHoursWithoutRadaway5",
"SurviveMoleratNoCasualties5_Survival",
"ExploreLocation5",
"Food5_Survival",
"RushRoom5_Survival",
"BarberShopUse5",
"CollectFoodWithinOneMin5",
"RandomEnemyKilledUnarmed5_Survival",
"QuestWinBattles5",
"SurviveRadscorpionNoCasualties5",
"RushRoom5",
"RaiseSpecificSpecialLevel5",
"StopIncidents5",
"Food5",
"StopIncidentsHardcore5",
"LevelUp5",
"ExtinguishFire5",
"CompleteQuestInWasteland5",
"QuestPerformPerfectCriticalHit5",
"WastelandLevelUp5",
"DwellerHappy5_Survival",
"SurviveGhoulNoCasualties5",
"CompleteQuestInOverseer5_Survival",
"EquipRandomWeapon5",
"SurviveDeathclawNoCasualties5",
"Water5",
"QuestPerformCriticalHit5_Survival",
"CollectWeapons5",
"ScrapItems5",
"Outfits5",
"UpgradeRoom5",
"StopRadroachWithoutPropagate5",
"ExtinguishFireHardcore5",
"CraftRareOutfits5",
"CollectPets5",
"CraftRareWeapons5",
"QuestCollectCaps5_Survival",
"MakeFriend5",
"CollectJunk5",
"DwellerHappy5",
"Radaway5",
"OutfitsHardcore5",
"Pregnant5",
"StopMoleratWithoutPropagate5",
"BaldDwellersInVault5",
"SellJunk5",
"WeaponsHardcore5",
"StimpackHardcore5",
"CollectWeaponsHardcore5",
"CraftWeapons5",
"RushRoomHardcore5",
"SurviveGhoulNoCasualties5_Survival",
"QuestCollectContainers5",
"CollectRareWeapons5",
"CollectPowerWithinOneMin5",
"QuestCollectCaps5",
"SellItemHarcore5",
"CompleteQuestInWasteland5_Survival",
"WastelandWithPet5",
"ExploreWastelandHours5",
"SurviveHoursNoCasualties5",
"Power5",
"RightRoom5",
"QuestPerformCriticalHit5",
"KillCreature5",
"CompleteQuestInOverseer5",
"KillDeathclawAtVaultDoor5",
"Wasteland5",
"SurviveMoleratNoCasualtiesHardcore5",
"KillRandomEnemyUnarmedHardcore5",
"Water5_Survival",
"KillCreatureHardcore5",
"RandomEnemyKilledUnarmed5",
"SurviveMoleratNoCasualties5",
"Stimpack5",
"QuestCollectContainers5_Survival",
"QuestWinBattles5_Survival",
"MysteriousStranger5",
"Power5_Survival",
"KillCreature5_Survival",
"MergeTwoRooms5",
"QuestPerformPerfectCriticalHit5_Survival",
"CraftOutfits5",
"Weapons5",
"ExploreWastelandHours5_Survival",
"KillRaiderAtVaultDoor5",
"RaiseSpecialLevel5",
"CollectOutfits5",
"Couple5",
"SurviveDeathclawNoCasualties5_Survival",
"CollectOutfitsHardcore5",
"SurviveRadscorpionNoCasualties5_Survival",
"StopIncidents5_Survival",
"SurviveDeathclawNoCasualtiesHardcore5",
"CollectCaps5",
"Babies5"
]
],
"slotArray": [
{
"objective": {
"objectiveID": "WastelandCollectCaps5",
"requirements": [
{
"requirementID": "r1",
"satisfied": false,
"nukaCount": 0.0,
"isAchievement": false
}
],
"completed": false,
"incrementLevel": 0
},
"incLevel": 0,
"lottery": [
false,
false,
false,
false,
false
]
},
{
"objective": {
"objectiveID": "ExploreWastelandHours1",
"requirements": [
{
"requirementID": "r1",
"satisfied": false,
"dwellers": [
8
]
}
],
"completed": false,
"incrementLevel": 0
},
"incLevel": 0,
"lottery": [
false,
true,
true,
true,
true
]
},
{
"objective": {
"objectiveID": "BabiesT1",
"requirements": [
{
"requirementID": "r1",
"satisfied": false,
"currentBabies": 0
}
],
"completed": false,
"incrementLevel": 0
},
"incLevel": 0,
"lottery": [
true,
true,
true,
true,
true
]
}
]
},
"unlockableMgr": {
"objectivesInProgress": [
{
"objectiveID": "MedbayUnlock",
"requirements": [
{
"requirementID": "unlock1",
"satisfied": false,
"currentCount": 12
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "RadioStationUnlock",
"requirements": [
{
"requirementID": "unlock3",
"satisfied": false,
"currentCount": 12
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "NukacolaUnlock",
"requirements": [
{
"requirementID": "unlock15",
"satisfied": false,
"currentCount": 12
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "SciencelabUnlock",
"requirements": [
{
"requirementID": "unlock2",
"satisfied": false,
"currentCount": 12
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "GymUnlock",
"requirements": [
{
"requirementID": "unlock4",
"satisfied": false,
"currentCount": 12
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "ArmoryUnlock",
"requirements": [
{
"requirementID": "unlock6",
"satisfied": false,
"currentCount": 12
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "PowerPlantUnlock",
"requirements": [
{
"requirementID": "unlock12",
"satisfied": false,
"currentCount": 12
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "CardioUnlock",
"requirements": [
{
"requirementID": "unlock8",
"satisfied": false,
"currentCount": 12
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "WaterroomUnlock",
"requirements": [
{
"requirementID": "unlock13",
"satisfied": false,
"currentCount": 12
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "HydroponicUnlock",
"requirements": [
{
"requirementID": "unlock14",
"satisfied": false,
"currentCount": 12
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "BarUnlock",
"requirements": [
{
"requirementID": "unlock9",
"satisfied": false,
"currentCount": 12
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "ClassUnlock",
"requirements": [
{
"requirementID": "unlock7",
"satisfied": false,
"currentCount": 12
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "DojoUnlock",
"requirements": [
{
"requirementID": "unlock5",
"satisfied": false,
"currentCount": 12
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "GameRoomUnlock",
"requirements": [
{
"requirementID": "unlock10",
"satisfied": false,
"currentCount": 12
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "WeaponFactoryUnlock",
"requirements": [
{
"requirementID": "unlock16",
"satisfied": false,
"currentCount": 12
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "OutfitFactoryUnlock",
"requirements": [
{
"requirementID": "unlock17",
"satisfied": false,
"currentCount": 12
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "BarberShopUnlock",
"requirements": [
{
"requirementID": "unlock18",
"satisfied": false,
"currentCount": 12
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "OverseerUnlock",
"requirements": [
{
"requirementID": "unlock20",
"satisfied": false,
"currentCount": 12
}
],
"completed": false,
"incrementLevel": 0
},
{
"objectiveID": "DesignFactoryUnlock",
"requirements": [
{
"requirementID": "unlock21",
"satisfied": false,
"currentCount": 12
}
],
"completed": false,
"incrementLevel": 0
}
],
"completed": [],
"claimed": [
"StorageUnlock"
]
},
"survivalW": {
"weapons": [
"N119",
"N59",
"N100",
"N7"
],
"outfits": [
"N95",
"N79",
"N76"
],
"dwellers": [],
"pets": [],
"breeds": [],
"decorations": [],
"junk": [
"NAlarmClock",
"NTeddyBear",
"NChemistrySet"
],
"recipes": [
"BOSUniform_Advanced",
"RadiationSuit_Advanced",
"UtilityJumpsuit",
"BattleArmor",
"CombatArmor",
"ScribeRobe_Elder",
"InstituteJumper_Expert",
"FormalWear_Fancy",
"FlightSuit",
"FormalWear",
"HandymanJumpsuit",
"BattleArmor_Heavy",
"RiotGear_Heavy",
"ScribeRobe_Initiate",
"ScientistScrubs",
"LabCoat",
"WandererArmor",
"MechanicJumpsuit",
"RiotGear",
"MilitaryJumpsuit",
"MoviefanSpecial",
"AllNightware",
"NinjaSuit",
"ScientistScrubs_Officer",
"PolkaDotDress",
"SweaterVest",
"RadiationSuit",
"RaiderArmor",
"WorkDress",
"CheckeredShirt",
"CombatArmor_Sturdy",
"PowerArmor",
"HunterGear_Treasure",
"SequinDress",
"HazmatSuit",
"WastelandSurgeon_Settler",
"WastelandSurgeon",
"PowerArmor_MkI",
"032Pistol",
"032Pistol_Enhanced",
"032Pistol_Rusty",
"AlienBlaster_Tuned",
"BBGun",
"BBGun_Enhanced",
"BBGun_Hardened",
"BBGun_Rusty",
"CombatShotgun_DoubleBarrelled",
"CombatShotgun_Enhanced",
"Flamer",
"GaussRifle_Enhanced",
"HuntingRifle",
"HuntingRifle_Enhanced",
"HuntingRifle_Hardened",
"HuntingRifle_Rusty",
"LaserPistol_Rusty",
"LaserPistol_Tuned",
"LaserRifle",
"LaserRifle_Amplified",
"Magnum",
"Magnum_ArmorPiercing",
"Magnum_Enhanced",
"Magnum_Rusty",
"Minigun_ArmorPiercing",
"MissilLauncher_Guided",
"MissilLauncher_Rusty",
"PipePistol",
"PipePistol_HairTrigger",
"PipePistol_Heavy",
"Pistol",
"Pistol_Enhanced",
"Pistol_Hardened",
"Pistol_Rusty",
"PlasmaPistol_Amplified",
"PlasmaPistol_Rusty",
"PlasmaRifle_Rusty",
"PlasmaRifle_Tuned",
"Railgun_Accelerated",
"Rifle",
"Rifle_Enhanced",
"Rifle_Rusty",
"SawedOffShotgun",
"SawedOffShotgun_DoubleBarrelled",
"SawedOffShotgun_Enhanced",
"SawedOffShotgun_Rusty",
"Shotgun"
],
"claimedRecipes": [],
"collectedThemes": {
"themeList": []
}
},
"ShopWindow": {
"isStarterPackPurchased": false,
"hasStarterPackPopupShown": true
},
"happinessManager": {
"dweller10": [
{
"type": 2,
"rh": 0.0,
"in": true
}
],
"dweller11": [
{
"type": 2,
"rh": 0.0,
"in": true
}
],
"dweller2": [
{
"type": 2,
"rh": 0.0,
"in": true
}
],
"dweller3": [
{
"type": 2,
"rh": 0.0,
"in": true
}
],
"dweller7": [
{
"type": 2,
"rh": 0.0,
"in": true
}
],
"dweller4": [
{
"type": 2,
"rh": 0.0,
"in": true
}
],
"dweller9": [
{
"type": 2,
"rh": 0.0,
"in": true
}
],
"dweller6": [
{
"type": 2,
"rh": 0.0,
"in": true
}
],
"dwellers": [
10,
11,
2,
3,
7,
4,
9,
6
]
},
"refugeeSpawner": {
"newGameTask": -1,
"succeed": 834,
"currentPool": "Pool2",
"currentPoolData": {
"retrievedDwellers": 0,
"failedChances": 1
},
"globalTimerTask": 835,
"collectedAllRefugees": false
},
"LunchBoxCollectWindow": null,
"DeathclawManager": {
"deathclawTotalExtraChance": 0.0,
"canDeathclawEmergencyOccurs": true,
"deathclawCooldownID": -1
},
"PromoCodesWindow": null,
"JunkGiveAwayWindow": null,
"MysteriousStranger": {
"currentState": "Hiding",
"canAppear": true,
"timeToAppear": 308.89,
"remainingTimeToAppear": 250.0
},
"StatsWindow": {
"vaultData": {
"collectedRes": {
"Nuka": 42093.0,
"Food": 10291.23,
"Energy": 11060.09,
"Water": 10441.77,
"StimPack": 0.0,
"RadAway": 0.0,
"Lunchbox": 0.0,
"MrHandy": 0.0,
"PetCarrier": 0.0,
"CraftedOutfit": 0.0,
"CraftedWeapon": 0.0,
"NukaColaQuantum": 0.0,
"CraftedTheme": 0.0
},
"collectedOutfits": 3,
"collectedWeapons": 4,
"collectedDecorations": 0,
"collectedPets": 0,
"raidersKilled": 0,
"elevatorRides": 339,
"lunchBoxesOpened": 3,
"petCarriersOpened": 0,
"totalLifetimeDwellers": 12,
"babiesBorn": 0,
"dwellersRevived": 1,
"levelsGained": 538,
"specialStatTrained": 0,
"takenStimpack": 0,
"takenRadaway": 0,
"deadDwellers": 0,
"evictedDwellers": 2,
"soldWeapons": 1,
"soldOutfits": 0,
"soldDecorations": 0,
"soldPets": 0,
"scrappedOutfits": 0,
"scrappedWeapons": 0,
"craftedWeapons": 0,
"craftedOutfits": 0,
"craftedDecorations": 0,
"craftedThemes": 0,
"collectedJunk": 3,
"soldJunk": 1,
"successfulRushes": 29,
"failRushes": 0,
"firesExtinguised": 3,
"emergencyStopRaider": 0,
"emergencyStopDeathClaw": 0,
"emergencyStopGhoul": 0,
"emergencyStopRadroach": 1,
"emergencyStopRadscorpion": 0,
"emergencyStopMolerat": 0,
"dwellersSentWasteland": 9,
"wastelandTotalTime": 3154.0,
"wastelandCreaturesKilled": 37.0,
"vaultCreatedBeforeUpdate": false,
"dwellersCustomized": 0,
"measuringSince": 638895246710443020,
"previousVResources": {
"Nuka": 1388.0,
"Food": 9998.46,
"Energy": 9999.0,
"Water": 9998.46,
"StimPack": 0.0,
"RadAway": 0.0,
"Lunchbox": 0.0,
"MrHandy": 0.0,
"PetCarrier": 0.0,
"CraftedOutfit": 0.0,
"CraftedWeapon": 0.0,
"NukaColaQuantum": 0.0,
"CraftedTheme": 0.0
},
"weaponFactoryBuilt": false,
"outfitFactoryBuilt": false
}
},
"PromotionFlags": [],
"appVersion": " 1.13.13",
"BottleAndCappyMgrSerializeKey": {
"SerializeAccumulatedTriggerChance": 0.0,
"SerializeLocked": false
},
"completedQuestDataManager": {
"taskID": 391,
"completedQuests": [],
"foundClues": [],
"standaloneQuestPicker": {},
"dailyQuestPicker": {
"currentDailies": [],
"historyDailies": []
},
"weeklyQuestPicker": {
"currentWeeklies": [],
"historyWeeklies": []
},
"nukaQuantumIncrement": 0,
"standaloneQuestSkipper": {
"skippedQuests": []
},
"dailyQuestSkipper": {
"skippedQuests": []
},
"weeklyQuestSkipper": {
"skippedQuests": []
}
},
"versionCount": 154
}

BIN
enhanced_save_decoder.py Normal file

Binary file not shown.

605
fallout_shelter_editor.py Normal file
View File

@@ -0,0 +1,605 @@
#!/usr/bin/env python3
"""
Fallout Shelter Save Editor
A comprehensive tool for editing Fallout Shelter save files.
"""
import base64
import json
import zlib
import gzip
import struct
import os
import shutil
from datetime import datetime
from typing import Dict, Any, Optional, List
import tkinter as tk
from tkinter import ttk, filedialog, messagebox, scrolledtext
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
class FalloutShelterSave:
"""Handles Fallout Shelter save file operations."""
# Fallout Shelter encryption constants (from JavaScript code)
AES_KEY = bytes([
(2815074099 >> 24) & 0xFF, (2815074099 >> 16) & 0xFF, (2815074099 >> 8) & 0xFF, 2815074099 & 0xFF,
(1725469378 >> 24) & 0xFF, (1725469378 >> 16) & 0xFF, (1725469378 >> 8) & 0xFF, 1725469378 & 0xFF,
(4039046167 >> 24) & 0xFF, (4039046167 >> 16) & 0xFF, (4039046167 >> 8) & 0xFF, 4039046167 & 0xFF,
(874293617 >> 24) & 0xFF, (874293617 >> 16) & 0xFF, (874293617 >> 8) & 0xFF, 874293617 & 0xFF,
(3063605751 >> 24) & 0xFF, (3063605751 >> 16) & 0xFF, (3063605751 >> 8) & 0xFF, 3063605751 & 0xFF,
(3133984764 >> 24) & 0xFF, (3133984764 >> 16) & 0xFF, (3133984764 >> 8) & 0xFF, 3133984764 & 0xFF,
(4097598161 >> 24) & 0xFF, (4097598161 >> 16) & 0xFF, (4097598161 >> 8) & 0xFF, 4097598161 & 0xFF,
(3620741625 >> 24) & 0xFF, (3620741625 >> 16) & 0xFF, (3620741625 >> 8) & 0xFF, 3620741625 & 0xFF
])
AES_IV = bytes.fromhex("7475383967656A693334307438397532")
def __init__(self, filepath: str = None):
self.filepath = filepath
self.raw_data = None
self.decoded_data = None
self.save_data = None
self.is_loaded = False
def load_save(self, filepath: str = None) -> bool:
"""Load and decode a Fallout Shelter save file."""
if filepath:
self.filepath = filepath
if not self.filepath or not os.path.exists(self.filepath):
return False
try:
with open(self.filepath, 'rb') as f:
self.raw_data = f.read()
# Decode base64
self.decoded_data = base64.b64decode(self.raw_data)
# Try to decompress and parse
self.save_data = self._parse_save_data()
self.is_loaded = True
return True
except Exception as e:
print(f"Error loading save: {e}")
return False
def _parse_save_data(self) -> Dict[str, Any]:
"""Parse the decoded save data using AES-CBC decryption."""
try:
# Decrypt using AES-CBC
cipher = AES.new(self.AES_KEY, AES.MODE_CBC, self.AES_IV)
decrypted_data = cipher.decrypt(self.decoded_data)
# Remove PKCS7 padding
try:
unpadded_data = unpad(decrypted_data, AES.block_size)
except ValueError:
# If unpadding fails, try without unpadding (some implementations don't pad)
unpadded_data = decrypted_data.rstrip(b'\x00')
# Convert to string and parse JSON
json_str = unpadded_data.decode('utf-8')
return json.loads(json_str)
except Exception as e:
print(f"AES decryption failed: {e}")
# Fallback to old methods if AES fails
return self._try_fallback_methods()
def _try_fallback_methods(self) -> Dict[str, Any]:
"""Fallback methods if AES decryption fails."""
decompression_methods = [
self._try_raw_json,
self._try_zlib_decompress,
self._try_gzip_decompress,
self._try_custom_decompress,
]
for method in decompression_methods:
try:
result = method(self.decoded_data)
if result:
return result
except Exception as e:
continue
# If all methods fail, return raw data info
return {
"error": "Could not parse save data",
"raw_size": len(self.decoded_data),
"raw_preview": self.decoded_data[:100].hex()
}
def _try_raw_json(self, data: bytes) -> Optional[Dict]:
"""Try to parse as raw JSON."""
try:
text = data.decode('utf-8')
return json.loads(text)
except:
return None
def _try_zlib_decompress(self, data: bytes) -> Optional[Dict]:
"""Try zlib decompression then JSON parse."""
try:
decompressed = zlib.decompress(data)
text = decompressed.decode('utf-8')
return json.loads(text)
except:
return None
def _try_gzip_decompress(self, data: bytes) -> Optional[Dict]:
"""Try gzip decompression then JSON parse."""
try:
decompressed = gzip.decompress(data)
text = decompressed.decode('utf-8')
return json.loads(text)
except:
return None
def _try_custom_decompress(self, data: bytes) -> Optional[Dict]:
"""Try custom Fallout Shelter decompression."""
# Some Fallout Shelter saves use a custom format
# This is a placeholder for reverse-engineered decompression
try:
# Check for common patterns in Fallout Shelter saves
if len(data) > 4:
# Try reading as little-endian uint32 header
header = struct.unpack('<I', data[:4])[0]
if header < len(data): # Reasonable size check
# Try decompressing the rest
compressed_data = data[4:]
decompressed = zlib.decompress(compressed_data)
text = decompressed.decode('utf-8')
return json.loads(text)
except:
pass
return None
def backup_save(self) -> str:
"""Create a backup of the current save file."""
if not self.filepath:
return ""
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_path = f"{self.filepath}.backup_{timestamp}"
shutil.copy2(self.filepath, backup_path)
return backup_path
def save_file(self, filepath: str = None) -> bool:
"""Save the modified data back to file."""
if not self.save_data or not self.is_loaded:
return False
save_path = filepath or self.filepath
if not save_path:
return False
try:
# Convert back to JSON (compact format)
json_data = json.dumps(self.save_data, separators=(',', ':'))
json_bytes = json_data.encode('utf-8')
# Pad data for AES encryption
padded_data = pad(json_bytes, AES.block_size)
# Encrypt using AES-CBC
cipher = AES.new(self.AES_KEY, AES.MODE_CBC, self.AES_IV)
encrypted_data = cipher.encrypt(padded_data)
# Encode to base64
encoded = base64.b64encode(encrypted_data)
# Write to file
with open(save_path, 'wb') as f:
f.write(encoded)
return True
except Exception as e:
print(f"Error saving file: {e}")
return False
class SaveEditorGUI:
"""GUI for the Fallout Shelter Save Editor."""
def __init__(self):
self.root = tk.Tk()
self.root.title("Fallout Shelter Save Editor")
self.root.geometry("800x600")
self.save_handler = FalloutShelterSave()
self.setup_ui()
def setup_ui(self):
"""Set up the user interface."""
# Menu bar
menubar = tk.Menu(self.root)
self.root.config(menu=menubar)
file_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="File", menu=file_menu)
file_menu.add_command(label="Open Save", command=self.open_save)
file_menu.add_command(label="Save", command=self.save_file)
file_menu.add_command(label="Save As", command=self.save_as)
file_menu.add_separator()
file_menu.add_command(label="Create Backup", command=self.create_backup)
file_menu.add_separator()
file_menu.add_command(label="Exit", command=self.root.quit)
# Main frame
main_frame = ttk.Frame(self.root, padding="10")
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
# File info frame
info_frame = ttk.LabelFrame(main_frame, text="Save File Info", padding="5")
info_frame.grid(row=0, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 10))
self.file_label = ttk.Label(info_frame, text="No file loaded")
self.file_label.grid(row=0, column=0, sticky=tk.W)
# Notebook for different edit categories
self.notebook = ttk.Notebook(main_frame)
self.notebook.grid(row=1, column=0, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S))
# Raw data tab
self.setup_raw_tab()
# Vault stats tab (placeholder for when we can parse the data)
self.setup_vault_tab()
# Resources tab
self.setup_resources_tab()
# Dwellers tab
self.setup_dwellers_tab()
# Configure grid weights
self.root.columnconfigure(0, weight=1)
self.root.rowconfigure(0, weight=1)
main_frame.columnconfigure(0, weight=1)
main_frame.rowconfigure(1, weight=1)
def setup_raw_tab(self):
"""Set up the raw data viewing tab."""
raw_frame = ttk.Frame(self.notebook)
self.notebook.add(raw_frame, text="Raw Data")
# Text area for raw data
self.raw_text = scrolledtext.ScrolledText(raw_frame, wrap=tk.WORD, height=20)
self.raw_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
def setup_vault_tab(self):
"""Set up the vault statistics tab."""
vault_frame = ttk.Frame(self.notebook)
self.notebook.add(vault_frame, text="Vault Stats")
# Vault info
ttk.Label(vault_frame, text="Vault Name:").grid(row=0, column=0, sticky=tk.W, padx=5, pady=2)
self.vault_name = ttk.Entry(vault_frame, width=30)
self.vault_name.grid(row=0, column=1, sticky=tk.W, padx=5, pady=2)
ttk.Label(vault_frame, text="Vault Mode:").grid(row=1, column=0, sticky=tk.W, padx=5, pady=2)
self.vault_number = ttk.Entry(vault_frame, width=30)
self.vault_number.grid(row=1, column=1, sticky=tk.W, padx=5, pady=2)
def setup_resources_tab(self):
"""Set up the resources editing tab."""
resources_frame = ttk.Frame(self.notebook)
self.notebook.add(resources_frame, text="Resources")
# Common resources
resources = ["Caps", "Food", "Water", "Power", "Stimpaks", "RadAway", "Nuka Cola Quantum"]
self.resource_entries = {}
for i, resource in enumerate(resources):
ttk.Label(resources_frame, text=f"{resource}:").grid(row=i, column=0, sticky=tk.W, padx=5, pady=2)
entry = ttk.Entry(resources_frame, width=20)
entry.grid(row=i, column=1, sticky=tk.W, padx=5, pady=2)
self.resource_entries[resource.lower().replace(" ", "_")] = entry
# Add buttons for resource modification
button_frame = ttk.Frame(resources_frame)
button_frame.grid(row=len(resources), column=0, columnspan=2, pady=10)
ttk.Button(button_frame, text="Apply Changes", command=self.apply_resource_changes).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text="Max All Resources", command=self.max_all_resources).pack(side=tk.LEFT, padx=5)
def setup_dwellers_tab(self):
"""Set up the dwellers editing tab."""
dwellers_frame = ttk.Frame(self.notebook)
self.notebook.add(dwellers_frame, text="Dwellers")
# Dwellers list (placeholder)
ttk.Label(dwellers_frame, text="Dwellers management will be available when save format is decoded").pack(pady=20)
def open_save(self):
"""Open a save file."""
filepath = filedialog.askopenfilename(
title="Select Fallout Shelter Save File",
filetypes=[("Save files", "*.sav"), ("All files", "*.*")]
)
if filepath:
if self.save_handler.load_save(filepath):
self.file_label.config(text=f"Loaded: {os.path.basename(filepath)}")
self.update_display()
messagebox.showinfo("Success", "Save file loaded successfully!")
else:
messagebox.showerror("Error", "Failed to load save file")
def update_display(self):
"""Update the display with loaded save data."""
if not self.save_handler.is_loaded:
return
# Update raw data tab
if self.save_handler.save_data:
raw_text = json.dumps(self.save_handler.save_data, indent=2)
self.raw_text.delete(1.0, tk.END)
self.raw_text.insert(1.0, raw_text)
# Try to populate other tabs if data structure is known
self.populate_vault_info()
self.populate_resources()
def populate_vault_info(self):
"""Populate vault information if available."""
if not self.save_handler.save_data:
return
data = self.save_handler.save_data
# Get vault information from the correct location
if "vault" in data and isinstance(data["vault"], dict):
vault = data["vault"]
# Vault name
if "VaultName" in vault:
self.vault_name.delete(0, tk.END)
self.vault_name.insert(0, str(vault["VaultName"]))
# Vault mode (instead of number, Fallout Shelter has mode)
if "VaultMode" in vault:
self.vault_number.delete(0, tk.END)
self.vault_number.insert(0, str(vault["VaultMode"]))
def populate_resources(self):
"""Populate resource information if available."""
if not self.save_handler.save_data:
return
data = self.save_handler.save_data
# Fallout Shelter resource field mappings (based on actual save structure)
resource_mappings = {
"caps": ["Nuka"], # Caps are stored as "Nuka" in the save
"food": ["Food"],
"water": ["Water"],
"power": ["Energy"], # Power is stored as "Energy"
"stimpaks": ["StimPack"],
"radaway": ["RadAway"],
"nuka_cola_quantum": ["NukaColaQuantum"]
}
# Try to find and populate resource values
# Look specifically in vault.storage.resources
vault_storage = self._get_vault_storage(data)
if vault_storage and "resources" in vault_storage:
resources = vault_storage["resources"]
for resource_key, entry in self.resource_entries.items():
possible_fields = resource_mappings.get(resource_key, [resource_key])
for field in possible_fields:
if field in resources:
entry.delete(0, tk.END)
entry.insert(0, str(int(resources[field])))
break
def _find_nested_value(self, data, key):
"""Recursively search for a key in nested dictionaries."""
if isinstance(data, dict):
if key in data:
return data[key]
for value in data.values():
result = self._find_nested_value(value, key)
if result is not None:
return result
elif isinstance(data, list):
for item in data:
result = self._find_nested_value(item, key)
if result is not None:
return result
return None
def save_file(self):
"""Save the current file."""
if self.save_handler.save_file():
messagebox.showinfo("Success", "Save file updated successfully!")
else:
messagebox.showerror("Error", "Failed to save file")
def save_as(self):
"""Save as a new file."""
filepath = filedialog.asksaveasfilename(
title="Save As",
defaultextension=".sav",
filetypes=[("Save files", "*.sav"), ("All files", "*.*")]
)
if filepath:
if self.save_handler.save_file(filepath):
messagebox.showinfo("Success", f"Save file created: {os.path.basename(filepath)}")
else:
messagebox.showerror("Error", "Failed to create save file")
def create_backup(self):
"""Create a backup of the current save."""
if not self.save_handler.filepath:
messagebox.showwarning("Warning", "No save file loaded")
return
backup_path = self.save_handler.backup_save()
if backup_path:
messagebox.showinfo("Success", f"Backup created: {os.path.basename(backup_path)}")
else:
messagebox.showerror("Error", "Failed to create backup")
def apply_resource_changes(self):
"""Apply resource changes to the save data."""
if not self.save_handler.save_data:
messagebox.showwarning("Warning", "No save data loaded")
return
try:
# Fallout Shelter resource field mappings
resource_mappings = {
"caps": ["Nuka"],
"food": ["Food"],
"water": ["Water"],
"power": ["Energy"],
"stimpaks": ["StimPack"],
"radaway": ["RadAway"],
"nuka_cola_quantum": ["NukaColaQuantum"]
}
# Get vault storage
vault_storage = self._get_vault_storage(self.save_handler.save_data)
if not vault_storage or "resources" not in vault_storage:
messagebox.showerror("Error", "Could not find vault storage in save data")
return
resources = vault_storage["resources"]
changes_made = 0
for resource_key, entry in self.resource_entries.items():
value_str = entry.get().strip()
if value_str:
try:
value = float(value_str) # Fallout Shelter uses floats for resources
possible_fields = resource_mappings.get(resource_key, [resource_key])
for field in possible_fields:
if field in resources:
resources[field] = value
changes_made += 1
break
except ValueError:
messagebox.showerror("Error", f"Invalid value for {resource_key}: {value_str}")
return
if changes_made > 0:
messagebox.showinfo("Success", f"Applied {changes_made} resource changes")
else:
messagebox.showinfo("Info", "No changes were applied")
except Exception as e:
messagebox.showerror("Error", f"Failed to apply changes: {e}")
def max_all_resources(self):
"""Set all resources to maximum values."""
max_values = {
"caps": 999999,
"food": 999999,
"water": 999999,
"power": 999999,
"stimpaks": 999,
"radaway": 999,
"nuka_cola_quantum": 999
}
for resource_key, entry in self.resource_entries.items():
if resource_key in max_values:
entry.delete(0, tk.END)
entry.insert(0, str(max_values[resource_key]))
self.apply_resource_changes()
def _get_vault_storage(self, data):
"""Get the vault storage section from save data."""
if isinstance(data, dict) and "vault" in data:
vault = data["vault"]
if isinstance(vault, dict) and "storage" in vault:
return vault["storage"]
return None
def _set_nested_value(self, data, key, value):
"""Recursively search for a key and set its value."""
if isinstance(data, dict):
if key in data:
data[key] = value
return True
for nested_data in data.values():
if self._set_nested_value(nested_data, key, value):
return True
elif isinstance(data, list):
for item in data:
if self._set_nested_value(item, key, value):
return True
return False
def run(self):
"""Start the GUI."""
self.root.mainloop()
def main():
"""Main function."""
print("Fallout Shelter Save Editor")
print("=" * 30)
# Check if GUI is available
try:
app = SaveEditorGUI()
app.run()
except ImportError:
print("GUI not available, running in console mode")
console_mode()
def console_mode():
"""Console-based interface."""
print("\nConsole Mode")
print("1. Analyze save file")
print("2. Create backup")
print("3. Exit")
while True:
choice = input("\nEnter choice (1-3): ").strip()
if choice == "1":
filepath = input("Enter save file path: ").strip()
if os.path.exists(filepath):
save = FalloutShelterSave(filepath)
if save.load_save():
print(f"Save loaded successfully!")
print(f"Data preview: {str(save.save_data)[:200]}...")
else:
print("Failed to load save file")
else:
print("File not found")
elif choice == "2":
filepath = input("Enter save file path: ").strip()
if os.path.exists(filepath):
save = FalloutShelterSave(filepath)
backup_path = save.backup_save()
if backup_path:
print(f"Backup created: {backup_path}")
else:
print("Failed to create backup")
else:
print("File not found")
elif choice == "3":
break
else:
print("Invalid choice")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,823 @@
#!/usr/bin/env python3
"""
Fallout Shelter Save Editor - PyQt GUI
A comprehensive PyQt-based tool for editing Fallout Shelter save files.
"""
import sys
import os
import json
import base64
import zlib
import gzip
import struct
import shutil
from datetime import datetime
from typing import Dict, Any, Optional, List, Tuple
import traceback
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QTabWidget, QTextEdit, QLabel, QLineEdit, QPushButton, QFileDialog,
QMessageBox, QGroupBox, QGridLayout, QSpinBox, QDoubleSpinBox,
QScrollArea, QFrame, QSplitter, QTreeWidget, QTreeWidgetItem,
QHeaderView, QComboBox, QCheckBox, QProgressBar, QStatusBar,
QMenuBar, QAction, QToolBar, QTableWidget, QTableWidgetItem
)
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer
from PyQt5.QtGui import QFont, QIcon, QPixmap
class SaveDecryptionWorker(QThread):
"""Worker thread for save file decryption/analysis."""
progress_updated = pyqtSignal(int)
status_updated = pyqtSignal(str)
finished_signal = pyqtSignal(dict)
error_signal = pyqtSignal(str)
def __init__(self, filepath: str):
super().__init__()
self.filepath = filepath
def run(self):
"""Run the decryption process."""
try:
self.status_updated.emit("Loading save file...")
self.progress_updated.emit(10)
with open(self.filepath, 'rb') as f:
raw_data = f.read()
self.status_updated.emit("Decoding base64...")
self.progress_updated.emit(20)
decoded_data = base64.b64decode(raw_data)
self.status_updated.emit("Attempting decompression...")
self.progress_updated.emit(40)
# Try various decompression methods
result = self.try_decompress(decoded_data)
if result:
self.status_updated.emit("Save file loaded successfully!")
self.progress_updated.emit(100)
self.finished_signal.emit(result)
else:
self.status_updated.emit("Could not decrypt save file")
self.error_signal.emit("Unable to decrypt or decompress save file")
except Exception as e:
self.error_signal.emit(f"Error loading save: {str(e)}")
def try_decompress(self, data: bytes) -> Optional[Dict]:
"""Try various decompression methods."""
methods = [
("Raw JSON", self.try_raw_json),
("Zlib", self.try_zlib),
("Gzip", self.try_gzip),
("Custom Format", self.try_custom_format),
]
for i, (name, method) in enumerate(methods):
self.status_updated.emit(f"Trying {name}...")
self.progress_updated.emit(40 + (i * 15))
try:
result = method(data)
if result:
return {
"method": name,
"data": result,
"raw_size": len(data)
}
except Exception as e:
continue
return None
def try_raw_json(self, data: bytes) -> Optional[Dict]:
"""Try parsing as raw JSON."""
text = data.decode('utf-8')
return json.loads(text)
def try_zlib(self, data: bytes) -> Optional[Dict]:
"""Try zlib decompression."""
decompressed = zlib.decompress(data)
text = decompressed.decode('utf-8')
return json.loads(text)
def try_gzip(self, data: bytes) -> Optional[Dict]:
"""Try gzip decompression."""
decompressed = gzip.decompress(data)
text = decompressed.decode('utf-8')
return json.loads(text)
def try_custom_format(self, data: bytes) -> Optional[Dict]:
"""Try custom Fallout Shelter format."""
# Try with header
if len(data) > 4:
header = struct.unpack('<I', data[:4])[0]
if 4 < header < len(data):
compressed_data = data[4:]
decompressed = zlib.decompress(compressed_data)
text = decompressed.decode('utf-8')
return json.loads(text)
return None
class FalloutShelterSaveEditor(QMainWindow):
"""Main application window."""
def __init__(self):
super().__init__()
self.save_data = None
self.save_filepath = None
self.save_method = None
self.is_modified = False
self.init_ui()
self.setup_connections()
def init_ui(self):
"""Initialize the user interface."""
self.setWindowTitle("Fallout Shelter Save Editor")
self.setGeometry(100, 100, 1200, 800)
# Create menu bar
self.create_menu_bar()
# Create toolbar
self.create_toolbar()
# Create status bar
self.status_bar = QStatusBar()
self.setStatusBar(self.status_bar)
# Progress bar for status bar
self.progress_bar = QProgressBar()
self.progress_bar.setVisible(False)
self.status_bar.addPermanentWidget(self.progress_bar)
# Central widget
central_widget = QWidget()
self.setCentralWidget(central_widget)
# Main layout
main_layout = QVBoxLayout(central_widget)
# File info section
self.create_file_info_section(main_layout)
# Main content area with tabs
self.create_main_content(main_layout)
# Update UI state
self.update_ui_state()
def create_menu_bar(self):
"""Create the menu bar."""
menubar = self.menuBar()
# File menu
file_menu = menubar.addMenu('File')
open_action = QAction('Open Save File', self)
open_action.setShortcut('Ctrl+O')
open_action.triggered.connect(self.open_save_file)
file_menu.addAction(open_action)
save_action = QAction('Save', self)
save_action.setShortcut('Ctrl+S')
save_action.triggered.connect(self.save_file)
file_menu.addAction(save_action)
save_as_action = QAction('Save As...', self)
save_as_action.setShortcut('Ctrl+Shift+S')
save_as_action.triggered.connect(self.save_file_as)
file_menu.addAction(save_as_action)
file_menu.addSeparator()
backup_action = QAction('Create Backup', self)
backup_action.triggered.connect(self.create_backup)
file_menu.addAction(backup_action)
file_menu.addSeparator()
exit_action = QAction('Exit', self)
exit_action.setShortcut('Ctrl+Q')
exit_action.triggered.connect(self.close)
file_menu.addAction(exit_action)
# Edit menu
edit_menu = menubar.addMenu('Edit')
refresh_action = QAction('Refresh Data', self)
refresh_action.setShortcut('F5')
refresh_action.triggered.connect(self.refresh_data)
edit_menu.addAction(refresh_action)
# Help menu
help_menu = menubar.addMenu('Help')
about_action = QAction('About', self)
about_action.triggered.connect(self.show_about)
help_menu.addAction(about_action)
def create_toolbar(self):
"""Create the toolbar."""
toolbar = QToolBar()
self.addToolBar(toolbar)
# Open button
open_btn = QPushButton("Open Save")
open_btn.clicked.connect(self.open_save_file)
toolbar.addWidget(open_btn)
# Save button
self.save_btn = QPushButton("Save")
self.save_btn.clicked.connect(self.save_file)
self.save_btn.setEnabled(False)
toolbar.addWidget(self.save_btn)
# Backup button
self.backup_btn = QPushButton("Backup")
self.backup_btn.clicked.connect(self.create_backup)
self.backup_btn.setEnabled(False)
toolbar.addWidget(self.backup_btn)
def create_file_info_section(self, parent_layout):
"""Create the file information section."""
info_group = QGroupBox("Save File Information")
info_layout = QGridLayout(info_group)
# File path
info_layout.addWidget(QLabel("File:"), 0, 0)
self.file_path_label = QLabel("No file loaded")
self.file_path_label.setStyleSheet("font-weight: bold;")
info_layout.addWidget(self.file_path_label, 0, 1)
# File size
info_layout.addWidget(QLabel("Size:"), 1, 0)
self.file_size_label = QLabel("-")
info_layout.addWidget(self.file_size_label, 1, 1)
# Decryption method
info_layout.addWidget(QLabel("Method:"), 2, 0)
self.method_label = QLabel("-")
info_layout.addWidget(self.method_label, 2, 1)
# Modified status
info_layout.addWidget(QLabel("Status:"), 3, 0)
self.status_label = QLabel("Not modified")
info_layout.addWidget(self.status_label, 3, 1)
parent_layout.addWidget(info_group)
def create_main_content(self, parent_layout):
"""Create the main content area with tabs."""
self.tab_widget = QTabWidget()
# Raw Data tab
self.create_raw_data_tab()
# Vault Info tab
self.create_vault_info_tab()
# Resources tab
self.create_resources_tab()
# Dwellers tab
self.create_dwellers_tab()
# Rooms tab
self.create_rooms_tab()
parent_layout.addWidget(self.tab_widget)
def create_raw_data_tab(self):
"""Create the raw data viewing/editing tab."""
raw_widget = QWidget()
layout = QVBoxLayout(raw_widget)
# JSON editor
self.json_editor = QTextEdit()
self.json_editor.setFont(QFont("Consolas", 10))
self.json_editor.textChanged.connect(self.on_data_modified)
layout.addWidget(self.json_editor)
# Format button
format_btn = QPushButton("Format JSON")
format_btn.clicked.connect(self.format_json)
layout.addWidget(format_btn)
self.tab_widget.addTab(raw_widget, "Raw Data")
def create_vault_info_tab(self):
"""Create the vault information tab."""
vault_widget = QWidget()
layout = QVBoxLayout(vault_widget)
# Scroll area for vault info
scroll = QScrollArea()
scroll_widget = QWidget()
scroll_layout = QGridLayout(scroll_widget)
# Vault basic info
basic_group = QGroupBox("Basic Information")
basic_layout = QGridLayout(basic_group)
# Vault name
basic_layout.addWidget(QLabel("Vault Name:"), 0, 0)
self.vault_name_edit = QLineEdit()
self.vault_name_edit.textChanged.connect(self.on_data_modified)
basic_layout.addWidget(self.vault_name_edit, 0, 1)
# Vault number
basic_layout.addWidget(QLabel("Vault Number:"), 1, 0)
self.vault_number_spin = QSpinBox()
self.vault_number_spin.setRange(1, 999)
self.vault_number_spin.valueChanged.connect(self.on_data_modified)
basic_layout.addWidget(self.vault_number_spin, 1, 1)
# Experience
basic_layout.addWidget(QLabel("Experience:"), 2, 0)
self.experience_spin = QSpinBox()
self.experience_spin.setRange(0, 999999999)
self.experience_spin.valueChanged.connect(self.on_data_modified)
basic_layout.addWidget(self.experience_spin, 2, 1)
scroll_layout.addWidget(basic_group, 0, 0)
scroll.setWidget(scroll_widget)
layout.addWidget(scroll)
self.tab_widget.addTab(vault_widget, "Vault Info")
def create_resources_tab(self):
"""Create the resources editing tab."""
resources_widget = QWidget()
layout = QVBoxLayout(resources_widget)
# Resources group
resources_group = QGroupBox("Vault Resources")
resources_layout = QGridLayout(resources_group)
# Common resources
self.resource_spins = {}
resources = [
("Caps", "caps", 0, 999999999),
("Food", "food", 0, 999999),
("Water", "water", 0, 999999),
("Power", "power", 0, 999999),
("Stimpaks", "stimpaks", 0, 999999),
("RadAway", "radaway", 0, 999999),
("Nuka Cola Quantum", "nuka_quantum", 0, 999999),
]
for i, (display_name, key, min_val, max_val) in enumerate(resources):
resources_layout.addWidget(QLabel(f"{display_name}:"), i, 0)
spin = QSpinBox()
spin.setRange(min_val, max_val)
spin.valueChanged.connect(self.on_data_modified)
self.resource_spins[key] = spin
resources_layout.addWidget(spin, i, 1)
layout.addWidget(resources_group)
layout.addStretch()
self.tab_widget.addTab(resources_widget, "Resources")
def create_dwellers_tab(self):
"""Create the dwellers management tab."""
dwellers_widget = QWidget()
layout = QVBoxLayout(dwellers_widget)
# Dwellers table
self.dwellers_table = QTableWidget()
self.dwellers_table.setColumnCount(8)
self.dwellers_table.setHorizontalHeaderLabels([
"Name", "Level", "Health", "Happiness", "Strength", "Perception", "Endurance", "Charisma"
])
# Make table stretch
header = self.dwellers_table.horizontalHeader()
header.setSectionResizeMode(QHeaderView.Stretch)
layout.addWidget(self.dwellers_table)
# Dwellers controls
controls_layout = QHBoxLayout()
refresh_dwellers_btn = QPushButton("Refresh Dwellers")
refresh_dwellers_btn.clicked.connect(self.refresh_dwellers)
controls_layout.addWidget(refresh_dwellers_btn)
controls_layout.addStretch()
layout.addLayout(controls_layout)
self.tab_widget.addTab(dwellers_widget, "Dwellers")
def create_rooms_tab(self):
"""Create the rooms management tab."""
rooms_widget = QWidget()
layout = QVBoxLayout(rooms_widget)
# Rooms tree
self.rooms_tree = QTreeWidget()
self.rooms_tree.setHeaderLabels(["Room Type", "Level", "Position", "Status"])
layout.addWidget(self.rooms_tree)
# Rooms controls
controls_layout = QHBoxLayout()
refresh_rooms_btn = QPushButton("Refresh Rooms")
refresh_rooms_btn.clicked.connect(self.refresh_rooms)
controls_layout.addWidget(refresh_rooms_btn)
controls_layout.addStretch()
layout.addLayout(controls_layout)
self.tab_widget.addTab(rooms_widget, "Rooms")
def setup_connections(self):
"""Set up signal connections."""
pass
def open_save_file(self):
"""Open a save file."""
filepath, _ = QFileDialog.getOpenFileName(
self,
"Open Fallout Shelter Save File",
"",
"Save Files (*.sav);;All Files (*)"
)
if filepath:
self.load_save_file(filepath)
def load_save_file(self, filepath: str):
"""Load a save file using worker thread."""
self.progress_bar.setVisible(True)
self.progress_bar.setValue(0)
# Create and start worker thread
self.worker = SaveDecryptionWorker(filepath)
self.worker.progress_updated.connect(self.progress_bar.setValue)
self.worker.status_updated.connect(self.status_bar.showMessage)
self.worker.finished_signal.connect(self.on_save_loaded)
self.worker.error_signal.connect(self.on_load_error)
self.worker.start()
def on_save_loaded(self, result: Dict):
"""Handle successful save file loading."""
self.save_data = result["data"]
self.save_filepath = self.worker.filepath
self.save_method = result["method"]
self.is_modified = False
# Update UI
self.update_file_info()
self.populate_data()
self.update_ui_state()
self.progress_bar.setVisible(False)
self.status_bar.showMessage("Save file loaded successfully", 3000)
def on_load_error(self, error_msg: str):
"""Handle save file loading error."""
self.progress_bar.setVisible(False)
self.status_bar.showMessage("Failed to load save file", 3000)
QMessageBox.critical(self, "Error", f"Failed to load save file:\n{error_msg}")
def update_file_info(self):
"""Update the file information display."""
if self.save_filepath:
filename = os.path.basename(self.save_filepath)
self.file_path_label.setText(filename)
file_size = os.path.getsize(self.save_filepath)
self.file_size_label.setText(f"{file_size:,} bytes")
self.method_label.setText(self.save_method or "Unknown")
self.update_status_label()
def update_status_label(self):
"""Update the modification status label."""
if self.is_modified:
self.status_label.setText("Modified")
self.status_label.setStyleSheet("color: orange; font-weight: bold;")
else:
self.status_label.setText("Not modified")
self.status_label.setStyleSheet("color: green;")
def populate_data(self):
"""Populate all tabs with save data."""
if not self.save_data:
return
# Raw data tab
json_text = json.dumps(self.save_data, indent=2)
self.json_editor.setPlainText(json_text)
# Vault info tab
self.populate_vault_info()
# Resources tab
self.populate_resources()
# Dwellers tab
self.refresh_dwellers()
# Rooms tab
self.refresh_rooms()
def populate_vault_info(self):
"""Populate vault information."""
if not self.save_data:
return
# Try to find vault info in common locations
vault_info = self.save_data
# Vault name
name_fields = ["vaultName", "name", "VaultName"]
for field in name_fields:
if field in vault_info:
self.vault_name_edit.setText(str(vault_info[field]))
break
# Vault number
number_fields = ["vaultNumber", "number", "VaultNumber"]
for field in number_fields:
if field in vault_info:
self.vault_number_spin.setValue(int(vault_info[field]))
break
# Experience
exp_fields = ["experience", "exp", "Experience"]
for field in exp_fields:
if field in vault_info:
self.experience_spin.setValue(int(vault_info[field]))
break
def populate_resources(self):
"""Populate resource information."""
if not self.save_data:
return
# Try to find resources in common locations
resources_data = self.save_data.get("resources", self.save_data)
# Map of UI keys to possible data keys
resource_mappings = {
"caps": ["caps", "money", "Caps"],
"food": ["food", "Food"],
"water": ["water", "Water"],
"power": ["power", "electricity", "Power"],
"stimpaks": ["stimpaks", "stimpak", "Stimpaks"],
"radaway": ["radaway", "RadAway", "radAway"],
"nuka_quantum": ["nuka_quantum", "quantum", "NukaQuantum"],
}
for ui_key, data_keys in resource_mappings.items():
if ui_key in self.resource_spins:
for data_key in data_keys:
if data_key in resources_data:
self.resource_spins[ui_key].setValue(int(resources_data[data_key]))
break
def refresh_dwellers(self):
"""Refresh the dwellers table."""
if not self.save_data:
return
# Try to find dwellers data
dwellers_data = self.save_data.get("dwellers", [])
if not isinstance(dwellers_data, list):
dwellers_data = []
self.dwellers_table.setRowCount(len(dwellers_data))
for i, dweller in enumerate(dwellers_data):
if isinstance(dweller, dict):
# Name
name = dweller.get("name", f"Dweller {i+1}")
self.dwellers_table.setItem(i, 0, QTableWidgetItem(str(name)))
# Level
level = dweller.get("level", 1)
self.dwellers_table.setItem(i, 1, QTableWidgetItem(str(level)))
# Health
health = dweller.get("health", 100)
self.dwellers_table.setItem(i, 2, QTableWidgetItem(str(health)))
# Happiness
happiness = dweller.get("happiness", 100)
self.dwellers_table.setItem(i, 3, QTableWidgetItem(str(happiness)))
# SPECIAL stats
special = dweller.get("special", {})
stats = ["strength", "perception", "endurance", "charisma"]
for j, stat in enumerate(stats):
value = special.get(stat, 1)
self.dwellers_table.setItem(i, 4+j, QTableWidgetItem(str(value)))
def refresh_rooms(self):
"""Refresh the rooms tree."""
self.rooms_tree.clear()
if not self.save_data:
return
# Try to find rooms data
rooms_data = self.save_data.get("rooms", [])
if not isinstance(rooms_data, list):
rooms_data = []
for i, room in enumerate(rooms_data):
if isinstance(room, dict):
room_type = room.get("type", "Unknown")
level = room.get("level", 1)
position = f"({room.get('x', 0)}, {room.get('y', 0)})"
status = room.get("status", "Active")
item = QTreeWidgetItem([room_type, str(level), position, status])
self.rooms_tree.addTopLevelItem(item)
def on_data_modified(self):
"""Handle data modification."""
if not self.is_modified:
self.is_modified = True
self.update_status_label()
self.update_ui_state()
def format_json(self):
"""Format the JSON in the raw data tab."""
try:
text = self.json_editor.toPlainText()
data = json.loads(text)
formatted = json.dumps(data, indent=2)
self.json_editor.setPlainText(formatted)
except json.JSONDecodeError as e:
QMessageBox.warning(self, "JSON Error", f"Invalid JSON: {e}")
def save_file(self):
"""Save the current file."""
if not self.save_filepath:
self.save_file_as()
return
if self.write_save_file(self.save_filepath):
self.is_modified = False
self.update_status_label()
self.update_ui_state()
self.status_bar.showMessage("File saved successfully", 3000)
else:
QMessageBox.critical(self, "Error", "Failed to save file")
def save_file_as(self):
"""Save as a new file."""
filepath, _ = QFileDialog.getSaveFileName(
self,
"Save Fallout Shelter Save File",
"",
"Save Files (*.sav);;All Files (*)"
)
if filepath:
if self.write_save_file(filepath):
self.save_filepath = filepath
self.is_modified = False
self.update_file_info()
self.update_ui_state()
self.status_bar.showMessage("File saved successfully", 3000)
else:
QMessageBox.critical(self, "Error", "Failed to save file")
def write_save_file(self, filepath: str) -> bool:
"""Write the save data to file."""
try:
# Get current data from JSON editor
json_text = self.json_editor.toPlainText()
data = json.loads(json_text)
# Convert to JSON string
json_string = json.dumps(data, separators=(',', ':'))
# Compress (try to match original method)
if self.save_method == "Zlib":
compressed = zlib.compress(json_string.encode('utf-8'))
else:
# Default to zlib
compressed = zlib.compress(json_string.encode('utf-8'))
# Encode to base64
encoded = base64.b64encode(compressed)
# Write to file
with open(filepath, 'wb') as f:
f.write(encoded)
return True
except Exception as e:
print(f"Error writing save file: {e}")
traceback.print_exc()
return False
def create_backup(self):
"""Create a backup of the current save file."""
if not self.save_filepath:
QMessageBox.warning(self, "Warning", "No save file loaded")
return
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_path = f"{self.save_filepath}.backup_{timestamp}"
try:
shutil.copy2(self.save_filepath, backup_path)
QMessageBox.information(self, "Success", f"Backup created:\n{os.path.basename(backup_path)}")
except Exception as e:
QMessageBox.critical(self, "Error", f"Failed to create backup:\n{e}")
def refresh_data(self):
"""Refresh all data displays."""
self.populate_data()
def update_ui_state(self):
"""Update UI element states based on current state."""
has_save = self.save_data is not None
self.save_btn.setEnabled(has_save and self.is_modified)
self.backup_btn.setEnabled(has_save)
# Update window title
title = "Fallout Shelter Save Editor"
if self.save_filepath:
filename = os.path.basename(self.save_filepath)
title += f" - {filename}"
if self.is_modified:
title += " *"
self.setWindowTitle(title)
def show_about(self):
"""Show about dialog."""
QMessageBox.about(
self,
"About Fallout Shelter Save Editor",
"Fallout Shelter Save Editor v1.0\n\n"
"A PyQt-based tool for editing Fallout Shelter save files.\n\n"
"Features:\n"
"• Load and save encrypted save files\n"
"• Edit vault information and resources\n"
"• View and manage dwellers\n"
"• Raw JSON editing\n"
"• Automatic backups"
)
def closeEvent(self, event):
"""Handle window close event."""
if self.is_modified:
reply = QMessageBox.question(
self,
"Unsaved Changes",
"You have unsaved changes. Do you want to save before closing?",
QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel
)
if reply == QMessageBox.Save:
self.save_file()
event.accept()
elif reply == QMessageBox.Discard:
event.accept()
else:
event.ignore()
else:
event.accept()
def main():
"""Main function."""
app = QApplication(sys.argv)
app.setApplicationName("Fallout Shelter Save Editor")
app.setApplicationVersion("1.0")
# Set application style
app.setStyle('Fusion')
# Create and show main window
window = FalloutShelterSaveEditor()
window.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

BIN
icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

434
index.html Normal file
View File

@@ -0,0 +1,434 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fallout Shelter Save Editor</title>
<link rel="stylesheet" href="styles.css">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
</head>
<body>
<div class="app-container">
<!-- Header -->
<header class="app-header">
<div class="header-content">
<div class="logo">
<i class="fas fa-radiation"></i>
<h1>Fallout Shelter Save Editor</h1>
</div>
<div class="file-info">
<span id="current-file">No file loaded</span>
</div>
</div>
</header>
<!-- Main Content -->
<main class="main-content">
<!-- Sidebar -->
<aside class="sidebar">
<nav class="nav-tabs">
<button class="nav-tab active" data-tab="overview">
<i class="fas fa-home"></i>
Overview
</button>
<button class="nav-tab" data-tab="resources">
<i class="fas fa-coins"></i>
Resources
</button>
<button class="nav-tab" data-tab="vault">
<i class="fas fa-building"></i>
Vault Info
</button>
<button class="nav-tab" data-tab="dwellers">
<i class="fas fa-users"></i>
Dwellers
</button>
<button class="nav-tab" data-tab="raw">
<i class="fas fa-code"></i>
Raw Data
</button>
</nav>
<div class="quick-actions">
<h3>Quick Actions</h3>
<button class="action-btn" id="open-file-btn">
<i class="fas fa-folder-open"></i>
Open Save
</button>
<button class="action-btn" id="save-btn" disabled>
<i class="fas fa-save"></i>
Save
</button>
<button class="action-btn" id="backup-btn" disabled>
<i class="fas fa-shield-alt"></i>
Backup
</button>
</div>
</aside>
<!-- Content Area -->
<section class="content-area">
<!-- Overview Tab -->
<div class="tab-content active" id="overview-tab">
<div class="welcome-screen">
<div class="welcome-icon">
<i class="fas fa-radiation"></i>
</div>
<h2>Welcome to Fallout Shelter Save Editor</h2>
<p>Load a save file to begin editing your vault's resources, dwellers, and more.</p>
<div class="feature-grid">
<div class="feature-card">
<i class="fas fa-coins"></i>
<h3>Resource Management</h3>
<p>Edit caps, food, water, power, and consumables</p>
</div>
<div class="feature-card">
<i class="fas fa-users"></i>
<h3>Dweller Editor</h3>
<p>Modify dweller stats and equipment</p>
</div>
<div class="feature-card">
<i class="fas fa-building"></i>
<h3>Vault Information</h3>
<p>View and edit vault details</p>
</div>
<div class="feature-card">
<i class="fas fa-shield-alt"></i>
<h3>Safe Editing</h3>
<p>Automatic backups and validation</p>
</div>
</div>
<button class="primary-btn" id="get-started-btn">
<i class="fas fa-folder-open"></i>
Get Started - Open Save File
</button>
</div>
</div>
<!-- Resources Tab -->
<div class="tab-content" id="resources-tab">
<div class="tab-header">
<h2><i class="fas fa-coins"></i> Resource Management</h2>
<div class="tab-actions">
<button class="secondary-btn" id="max-resources-btn">
<i class="fas fa-arrow-up"></i>
Max All
</button>
<button class="secondary-btn" id="reset-resources-btn">
<i class="fas fa-undo"></i>
Reset
</button>
</div>
</div>
<div class="resource-grid">
<div class="resource-card">
<div class="resource-icon caps">
<i class="fas fa-coins"></i>
</div>
<div class="resource-info">
<label for="caps-input">Caps</label>
<input type="number" id="caps-input" class="resource-input" min="0" max="999999999">
</div>
</div>
<div class="resource-card">
<div class="resource-icon food">
<i class="fas fa-apple-alt"></i>
</div>
<div class="resource-info">
<label for="food-input">Food</label>
<input type="number" id="food-input" class="resource-input" min="0" max="999999">
</div>
</div>
<div class="resource-card">
<div class="resource-icon water">
<i class="fas fa-tint"></i>
</div>
<div class="resource-info">
<label for="water-input">Water</label>
<input type="number" id="water-input" class="resource-input" min="0" max="999999">
</div>
</div>
<div class="resource-card">
<div class="resource-icon power">
<i class="fas fa-bolt"></i>
</div>
<div class="resource-info">
<label for="power-input">Power</label>
<input type="number" id="power-input" class="resource-input" min="0" max="999999">
</div>
</div>
<div class="resource-card">
<div class="resource-icon stimpaks">
<i class="fas fa-syringe"></i>
</div>
<div class="resource-info">
<label for="stimpaks-input">Stimpaks</label>
<input type="number" id="stimpaks-input" class="resource-input" min="0" max="999999">
</div>
</div>
<div class="resource-card">
<div class="resource-icon radaway">
<i class="fas fa-radiation-alt"></i>
</div>
<div class="resource-info">
<label for="radaway-input">RadAway</label>
<input type="number" id="radaway-input" class="resource-input" min="0" max="999999">
</div>
</div>
<div class="resource-card">
<div class="resource-icon quantum">
<i class="fas fa-atom"></i>
</div>
<div class="resource-info">
<label for="quantum-input">Nuka Cola Quantum</label>
<input type="number" id="quantum-input" class="resource-input" min="0" max="999">
</div>
</div>
<div class="resource-card">
<div class="resource-icon lunchbox">
<i class="fas fa-gift"></i>
</div>
<div class="resource-info">
<label for="lunchbox-input">Lunchboxes</label>
<input type="number" id="lunchbox-input" class="resource-input" min="0" max="999">
</div>
</div>
<div class="resource-card">
<div class="resource-icon mrhandy">
<i class="fas fa-robot"></i>
</div>
<div class="resource-info">
<label for="mrhandy-input">Mr. Handy</label>
<input type="number" id="mrhandy-input" class="resource-input" min="0" max="99">
</div>
</div>
<div class="resource-card">
<div class="resource-icon petcarrier">
<i class="fas fa-paw"></i>
</div>
<div class="resource-info">
<label for="petcarrier-input">Pet Carriers</label>
<input type="number" id="petcarrier-input" class="resource-input" min="0" max="999">
</div>
</div>
<div class="resource-card">
<div class="resource-icon lunchbox">
<i class="fas fa-gift"></i>
</div>
<div class="resource-info">
<label for="lunchbox-input">Lunchboxes</label>
<input type="number" id="lunchbox-input" class="resource-input" min="0" max="999">
</div>
</div>
<div class="resource-card">
<div class="resource-icon mrhandy">
<i class="fas fa-robot"></i>
</div>
<div class="resource-info">
<label for="mrhandy-input">Mr. Handy</label>
<input type="number" id="mrhandy-input" class="resource-input" min="0" max="99">
</div>
</div>
<div class="resource-card">
<div class="resource-icon petcarrier">
<i class="fas fa-paw"></i>
</div>
<div class="resource-info">
<label for="petcarrier-input">Pet Carriers</label>
<input type="number" id="petcarrier-input" class="resource-input" min="0" max="999">
</div>
</div>
</div>
<div class="action-bar">
<button class="primary-btn" id="apply-resources-btn">
<i class="fas fa-check"></i>
Apply Changes
</button>
</div>
</div>
<!-- Vault Info Tab -->
<div class="tab-content" id="vault-tab">
<div class="tab-header">
<h2><i class="fas fa-building"></i> Vault Information</h2>
</div>
<div class="vault-info-grid">
<div class="info-card">
<label for="vault-name-input">Vault Name</label>
<input type="text" id="vault-name-input" class="text-input">
</div>
<div class="info-card">
<label for="vault-mode-input">Vault Mode</label>
<select id="vault-mode-input" class="select-input">
<option value="Normal">Normal</option>
<option value="Survival">Survival</option>
</select>
</div>
<div class="info-card">
<label for="vault-theme-input">Vault Theme</label>
<input type="number" id="vault-theme-input" class="number-input" min="0" max="10">
</div>
</div>
<div class="action-bar">
<button class="primary-btn" id="apply-vault-btn">
<i class="fas fa-check"></i>
Apply Changes
</button>
</div>
</div>
<!-- Dwellers Tab -->
<div class="tab-content" id="dwellers-tab">
<div class="tab-header">
<h2><i class="fas fa-users"></i> Dwellers</h2>
<div class="tab-actions">
<button class="secondary-btn" id="max-all-dwellers-btn">
<i class="fas fa-arrow-up"></i>
Max All Dwellers
</button>
<button class="secondary-btn" id="refresh-dwellers-btn">
<i class="fas fa-sync"></i>
Refresh
</button>
</div>
</div>
<div class="dwellers-container">
<div class="dwellers-list" id="dwellers-list">
<div class="placeholder">
<i class="fas fa-users"></i>
<p>Load a save file to view dwellers</p>
</div>
</div>
</div>
</div>
<!-- Raw Data Tab -->
<div class="tab-content" id="raw-tab">
<div class="tab-header">
<h2><i class="fas fa-code"></i> Raw Data</h2>
<div class="tab-actions">
<button class="secondary-btn" id="format-json-btn">
<i class="fas fa-indent"></i>
Format
</button>
<button class="secondary-btn" id="copy-json-btn">
<i class="fas fa-copy"></i>
Copy
</button>
</div>
</div>
<div class="raw-data-container">
<textarea id="raw-data-textarea" class="raw-data-textarea" readonly></textarea>
</div>
</div>
</section>
</main>
<!-- Status Bar -->
<footer class="status-bar">
<div class="status-left">
<span id="status-text">Ready</span>
</div>
<div class="status-right">
<span id="file-size">-</span>
<span class="separator">|</span>
<span id="last-modified">-</span>
</div>
</footer>
</div>
<!-- Loading Overlay -->
<div class="loading-overlay" id="loading-overlay">
<div class="loading-spinner">
<i class="fas fa-radiation fa-spin"></i>
<p>Processing save file...</p>
</div>
</div>
<!-- Toast Notifications -->
<div class="toast-container" id="toast-container"></div>
<!-- Startup Animation -->
<div class="startup-overlay" id="startup-overlay">
<div class="startup-content">
<div class="vault-door">
<div class="vault-door-outer">
<div class="vault-door-inner">
<div class="vault-number">111</div>
<div class="vault-spokes">
<div class="spoke"></div>
<div class="spoke"></div>
<div class="spoke"></div>
<div class="spoke"></div>
<div class="spoke"></div>
<div class="spoke"></div>
<div class="spoke"></div>
<div class="spoke"></div>
</div>
</div>
</div>
</div>
<div class="startup-text">
<h1 class="startup-title">FALLOUT SHELTER</h1>
<h2 class="startup-subtitle">SAVE EDITOR</h2>
<div class="version-info">
<span class="version-badge">v1.0.0</span>
<span class="vault-tec-badge">VAULT-TEC APPROVED</span>
</div>
<div class="loading-dots">
<span></span>
<span></span>
<span></span>
</div>
<p class="startup-status" id="startup-status">Initializing Vault-Tec Systems...</p>
</div>
<div class="startup-progress">
<div class="progress-bar">
<div class="progress-fill" id="startup-progress"></div>
</div>
<div class="progress-text">
<span>VAULT-TEC APPROVED</span>
<span id="progress-percent">0%</span>
</div>
</div>
</div>
<div class="radiation-particles">
<div class="particle"></div>
<div class="particle"></div>
<div class="particle"></div>
<div class="particle"></div>
<div class="particle"></div>
<div class="particle"></div>
<div class="particle"></div>
<div class="particle"></div>
</div>
</div>
<script src="renderer.js"></script>
</body>
</html>

139
main.js Normal file
View File

@@ -0,0 +1,139 @@
const { app, BrowserWindow, Menu, dialog, ipcMain } = require('electron');
const path = require('path');
const fs = require('fs');
let mainWindow;
// Disable GPU acceleration to prevent crashes
app.disableHardwareAcceleration();
function createWindow() {
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
minWidth: 800,
minHeight: 600,
webPreferences: {
nodeIntegration: true,
contextIsolation: false
},
show: false,
backgroundColor: '#1a1a2e'
});
mainWindow.loadFile('index.html');
mainWindow.once('ready-to-show', () => {
mainWindow.show();
});
if (process.argv.includes('--dev')) {
mainWindow.webContents.openDevTools();
}
mainWindow.on('closed', () => {
mainWindow = null;
});
createMenu();
}
function createMenu() {
const template = [
{
label: 'File',
submenu: [
{
label: 'Open Save File',
accelerator: 'CmdOrCtrl+O',
click: () => openSaveFile()
},
{
label: 'Save',
accelerator: 'CmdOrCtrl+S',
click: () => mainWindow.webContents.send('menu-save')
},
{ type: 'separator' },
{
label: 'Exit',
click: () => app.quit()
}
]
}
];
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
}
async function openSaveFile() {
try {
const result = await dialog.showOpenDialog(mainWindow, {
title: 'Open Fallout Shelter Save File',
filters: [
{ name: 'Save Files', extensions: ['sav'] },
{ name: 'All Files', extensions: ['*'] }
],
properties: ['openFile']
});
if (!result.canceled && result.filePaths.length > 0) {
const filePath = result.filePaths[0];
const fileData = fs.readFileSync(filePath, 'utf8');
mainWindow.webContents.send('file-opened', {
path: filePath,
data: fileData,
name: path.basename(filePath)
});
}
} catch (error) {
console.error('Error:', error);
}
}
// IPC handlers
ipcMain.on('open-file-dialog', () => {
openSaveFile();
});
ipcMain.handle('save-file', async (event, filePath, data) => {
try {
console.log('Main process: Saving file to:', filePath);
console.log('Main process: Data length:', data.length);
fs.writeFileSync(filePath, data, 'utf8');
console.log('Main process: File saved successfully');
return { success: true };
} catch (error) {
console.error('Main process: Save error:', error);
return { success: false, error: error.message };
}
});
ipcMain.handle('create-backup', async (event, originalPath) => {
try {
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const backupPath = `${originalPath}.backup_${timestamp}`;
fs.copyFileSync(originalPath, backupPath);
return { success: true, backupPath };
} catch (error) {
return { success: false, error: error.message };
}
});
app.whenReady().then(() => {
createWindow();
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});

4125
package-lock.json generated Normal file
View File

@@ -0,0 +1,4125 @@
{
"name": "fallout-shelter-save-editor",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "fallout-shelter-save-editor",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"crypto-js": "^4.2.0"
},
"devDependencies": {
"electron": "^27.0.0",
"electron-builder": "^24.6.4"
}
},
"node_modules/@develar/schema-utils": {
"version": "2.6.5",
"resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz",
"integrity": "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==",
"dev": true,
"license": "MIT",
"dependencies": {
"ajv": "^6.12.0",
"ajv-keywords": "^3.4.1"
},
"engines": {
"node": ">= 8.9.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
}
},
"node_modules/@electron/asar": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.4.1.tgz",
"integrity": "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA==",
"dev": true,
"license": "MIT",
"dependencies": {
"commander": "^5.0.0",
"glob": "^7.1.6",
"minimatch": "^3.0.4"
},
"bin": {
"asar": "bin/asar.js"
},
"engines": {
"node": ">=10.12.0"
}
},
"node_modules/@electron/asar/node_modules/brace-expansion": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/@electron/asar/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/@electron/get": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz",
"integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"debug": "^4.1.1",
"env-paths": "^2.2.0",
"fs-extra": "^8.1.0",
"got": "^11.8.5",
"progress": "^2.0.3",
"semver": "^6.2.0",
"sumchecker": "^3.0.1"
},
"engines": {
"node": ">=12"
},
"optionalDependencies": {
"global-agent": "^3.0.0"
}
},
"node_modules/@electron/notarize": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.2.1.tgz",
"integrity": "sha512-aL+bFMIkpR0cmmj5Zgy0LMKEpgy43/hw5zadEArgmAMWWlKc5buwFvFT9G/o/YJkvXAJm5q3iuTuLaiaXW39sg==",
"dev": true,
"license": "MIT",
"dependencies": {
"debug": "^4.1.1",
"fs-extra": "^9.0.1",
"promise-retry": "^2.0.1"
},
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/@electron/notarize/node_modules/fs-extra": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
"integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"at-least-node": "^1.0.0",
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@electron/notarize/node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"universalify": "^2.0.0"
},
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/@electron/notarize/node_modules/universalify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/@electron/osx-sign": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.0.5.tgz",
"integrity": "sha512-k9ZzUQtamSoweGQDV2jILiRIHUu7lYlJ3c6IEmjv1hC17rclE+eb9U+f6UFlOOETo0JzY1HNlXy4YOlCvl+Lww==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"compare-version": "^0.1.2",
"debug": "^4.3.4",
"fs-extra": "^10.0.0",
"isbinaryfile": "^4.0.8",
"minimist": "^1.2.6",
"plist": "^3.0.5"
},
"bin": {
"electron-osx-flat": "bin/electron-osx-flat.js",
"electron-osx-sign": "bin/electron-osx-sign.js"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/@electron/osx-sign/node_modules/fs-extra": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@electron/osx-sign/node_modules/isbinaryfile": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz",
"integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 8.0.0"
},
"funding": {
"url": "https://github.com/sponsors/gjtorikian/"
}
},
"node_modules/@electron/osx-sign/node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"universalify": "^2.0.0"
},
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/@electron/osx-sign/node_modules/universalify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/@electron/universal": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.5.1.tgz",
"integrity": "sha512-kbgXxyEauPJiQQUNG2VgUeyfQNFk6hBF11ISN2PNI6agUgPl55pv4eQmaqHzTAzchBvqZ2tQuRVaPStGf0mxGw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@electron/asar": "^3.2.1",
"@malept/cross-spawn-promise": "^1.1.0",
"debug": "^4.3.1",
"dir-compare": "^3.0.0",
"fs-extra": "^9.0.1",
"minimatch": "^3.0.4",
"plist": "^3.0.4"
},
"engines": {
"node": ">=8.6"
}
},
"node_modules/@electron/universal/node_modules/brace-expansion": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/@electron/universal/node_modules/fs-extra": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
"integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"at-least-node": "^1.0.0",
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@electron/universal/node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"universalify": "^2.0.0"
},
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/@electron/universal/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/@electron/universal/node_modules/universalify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
"dev": true,
"license": "ISC",
"dependencies": {
"string-width": "^5.1.2",
"string-width-cjs": "npm:string-width@^4.2.0",
"strip-ansi": "^7.0.1",
"strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
"wrap-ansi": "^8.1.0",
"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@isaacs/cliui/node_modules/ansi-regex": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
}
},
"node_modules/@isaacs/cliui/node_modules/ansi-styles": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/@isaacs/cliui/node_modules/emoji-regex": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
"dev": true,
"license": "MIT"
},
"node_modules/@isaacs/cliui/node_modules/string-width": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
"dev": true,
"license": "MIT",
"dependencies": {
"eastasianwidth": "^0.2.0",
"emoji-regex": "^9.2.2",
"strip-ansi": "^7.0.1"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@isaacs/cliui/node_modules/strip-ansi": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^6.0.1"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
}
},
"node_modules/@isaacs/cliui/node_modules/wrap-ansi": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^6.1.0",
"string-width": "^5.0.1",
"strip-ansi": "^7.0.1"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/@malept/cross-spawn-promise": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz",
"integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/malept"
},
{
"type": "tidelift",
"url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund"
}
],
"license": "Apache-2.0",
"dependencies": {
"cross-spawn": "^7.0.1"
},
"engines": {
"node": ">= 10"
}
},
"node_modules/@malept/flatpak-bundler": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz",
"integrity": "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"debug": "^4.1.1",
"fs-extra": "^9.0.0",
"lodash": "^4.17.15",
"tmp-promise": "^3.0.2"
},
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/@malept/flatpak-bundler/node_modules/fs-extra": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
"integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"at-least-node": "^1.0.0",
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@malept/flatpak-bundler/node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"universalify": "^2.0.0"
},
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/@malept/flatpak-bundler/node_modules/universalify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">=14"
}
},
"node_modules/@sindresorhus/is": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
"integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sindresorhus/is?sponsor=1"
}
},
"node_modules/@szmarczak/http-timer": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
"integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==",
"dev": true,
"license": "MIT",
"dependencies": {
"defer-to-connect": "^2.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@tootallnate/once": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
"integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 10"
}
},
"node_modules/@types/cacheable-request": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz",
"integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/http-cache-semantics": "*",
"@types/keyv": "^3.1.4",
"@types/node": "*",
"@types/responselike": "^1.0.0"
}
},
"node_modules/@types/debug": {
"version": "4.1.12",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
"integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/ms": "*"
}
},
"node_modules/@types/fs-extra": {
"version": "9.0.13",
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz",
"integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/http-cache-semantics": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz",
"integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/keyv": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
"integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/ms": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
"integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": {
"version": "18.19.121",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.121.tgz",
"integrity": "sha512-bHOrbyztmyYIi4f1R0s17QsPs1uyyYnGcXeZoGEd227oZjry0q6XQBQxd82X1I57zEfwO8h9Xo+Kl5gX1d9MwQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/@types/plist": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz",
"integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@types/node": "*",
"xmlbuilder": ">=11.0.1"
}
},
"node_modules/@types/responselike": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz",
"integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/verror": {
"version": "1.10.11",
"resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.11.tgz",
"integrity": "sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/@types/yauzl": {
"version": "2.10.3",
"resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
"integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@xmldom/xmldom": {
"version": "0.8.10",
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz",
"integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/7zip-bin": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.2.0.tgz",
"integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==",
"dev": true,
"license": "MIT"
},
"node_modules/agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"debug": "4"
},
"engines": {
"node": ">= 6.0.0"
}
},
"node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/ajv-keywords": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"ajv": "^6.9.1"
}
},
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/app-builder-bin": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-4.0.0.tgz",
"integrity": "sha512-xwdG0FJPQMe0M0UA4Tz0zEB8rBJTRA5a476ZawAqiBkMv16GRK5xpXThOjMaEOFnZ6zabejjG4J3da0SXG63KA==",
"dev": true,
"license": "MIT"
},
"node_modules/app-builder-lib": {
"version": "24.13.3",
"resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-24.13.3.tgz",
"integrity": "sha512-FAzX6IBit2POXYGnTCT8YHFO/lr5AapAII6zzhQO3Rw4cEDOgK+t1xhLc5tNcKlicTHlo9zxIwnYCX9X2DLkig==",
"dev": true,
"license": "MIT",
"dependencies": {
"@develar/schema-utils": "~2.6.5",
"@electron/notarize": "2.2.1",
"@electron/osx-sign": "1.0.5",
"@electron/universal": "1.5.1",
"@malept/flatpak-bundler": "^0.4.0",
"@types/fs-extra": "9.0.13",
"async-exit-hook": "^2.0.1",
"bluebird-lst": "^1.0.9",
"builder-util": "24.13.1",
"builder-util-runtime": "9.2.4",
"chromium-pickle-js": "^0.2.0",
"debug": "^4.3.4",
"ejs": "^3.1.8",
"electron-publish": "24.13.1",
"form-data": "^4.0.0",
"fs-extra": "^10.1.0",
"hosted-git-info": "^4.1.0",
"is-ci": "^3.0.0",
"isbinaryfile": "^5.0.0",
"js-yaml": "^4.1.0",
"lazy-val": "^1.0.5",
"minimatch": "^5.1.1",
"read-config-file": "6.3.2",
"sanitize-filename": "^1.6.3",
"semver": "^7.3.8",
"tar": "^6.1.12",
"temp-file": "^3.4.0"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"dmg-builder": "24.13.3",
"electron-builder-squirrel-windows": "24.13.3"
}
},
"node_modules/app-builder-lib/node_modules/fs-extra": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/app-builder-lib/node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"universalify": "^2.0.0"
},
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/app-builder-lib/node_modules/semver": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"dev": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/app-builder-lib/node_modules/universalify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/archiver": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz",
"integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"archiver-utils": "^2.1.0",
"async": "^3.2.4",
"buffer-crc32": "^0.2.1",
"readable-stream": "^3.6.0",
"readdir-glob": "^1.1.2",
"tar-stream": "^2.2.0",
"zip-stream": "^4.1.0"
},
"engines": {
"node": ">= 10"
}
},
"node_modules/archiver-utils": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz",
"integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"glob": "^7.1.4",
"graceful-fs": "^4.2.0",
"lazystream": "^1.0.0",
"lodash.defaults": "^4.2.0",
"lodash.difference": "^4.5.0",
"lodash.flatten": "^4.4.0",
"lodash.isplainobject": "^4.0.6",
"lodash.union": "^4.6.0",
"normalize-path": "^3.0.0",
"readable-stream": "^2.0.0"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/archiver-utils/node_modules/readable-stream": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"node_modules/archiver-utils/node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/archiver-utils/node_modules/string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"safe-buffer": "~5.1.0"
}
},
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true,
"license": "Python-2.0"
},
"node_modules/assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">=0.8"
}
},
"node_modules/astral-regex": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
"integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">=8"
}
},
"node_modules/async": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
"dev": true,
"license": "MIT"
},
"node_modules/async-exit-hook": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz",
"integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"dev": true,
"license": "MIT"
},
"node_modules/at-least-node": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
"integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">= 4.0.0"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true,
"license": "MIT"
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/bl": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"buffer": "^5.5.0",
"inherits": "^2.0.4",
"readable-stream": "^3.4.0"
}
},
"node_modules/bluebird": {
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
"dev": true,
"license": "MIT"
},
"node_modules/bluebird-lst": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/bluebird-lst/-/bluebird-lst-1.0.9.tgz",
"integrity": "sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw==",
"dev": true,
"license": "MIT",
"dependencies": {
"bluebird": "^3.5.5"
}
},
"node_modules/boolean": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz",
"integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==",
"deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/brace-expansion": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT",
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
},
"node_modules/buffer-crc32": {
"version": "0.2.13",
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
"integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": "*"
}
},
"node_modules/buffer-equal": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz",
"integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true,
"license": "MIT"
},
"node_modules/builder-util": {
"version": "24.13.1",
"resolved": "https://registry.npmjs.org/builder-util/-/builder-util-24.13.1.tgz",
"integrity": "sha512-NhbCSIntruNDTOVI9fdXz0dihaqX2YuE1D6zZMrwiErzH4ELZHE6mdiB40wEgZNprDia+FghRFgKoAqMZRRjSA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/debug": "^4.1.6",
"7zip-bin": "~5.2.0",
"app-builder-bin": "4.0.0",
"bluebird-lst": "^1.0.9",
"builder-util-runtime": "9.2.4",
"chalk": "^4.1.2",
"cross-spawn": "^7.0.3",
"debug": "^4.3.4",
"fs-extra": "^10.1.0",
"http-proxy-agent": "^5.0.0",
"https-proxy-agent": "^5.0.1",
"is-ci": "^3.0.0",
"js-yaml": "^4.1.0",
"source-map-support": "^0.5.19",
"stat-mode": "^1.0.0",
"temp-file": "^3.4.0"
}
},
"node_modules/builder-util-runtime": {
"version": "9.2.4",
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.4.tgz",
"integrity": "sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA==",
"dev": true,
"license": "MIT",
"dependencies": {
"debug": "^4.3.4",
"sax": "^1.2.4"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/builder-util/node_modules/fs-extra": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/builder-util/node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"universalify": "^2.0.0"
},
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/builder-util/node_modules/universalify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/cacheable-lookup": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
"integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10.6.0"
}
},
"node_modules/cacheable-request": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz",
"integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==",
"dev": true,
"license": "MIT",
"dependencies": {
"clone-response": "^1.0.2",
"get-stream": "^5.1.0",
"http-cache-semantics": "^4.0.0",
"keyv": "^4.0.0",
"lowercase-keys": "^2.0.0",
"normalize-url": "^6.0.1",
"responselike": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/chownr": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=10"
}
},
"node_modules/chromium-pickle-js": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz",
"integrity": "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==",
"dev": true,
"license": "MIT"
},
"node_modules/ci-info": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
"integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/sibiraj-s"
}
],
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/cli-truncate": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz",
"integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"slice-ansi": "^3.0.0",
"string-width": "^4.2.0"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
"dev": true,
"license": "ISC",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.1",
"wrap-ansi": "^7.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/clone-response": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz",
"integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==",
"dev": true,
"license": "MIT",
"dependencies": {
"mimic-response": "^1.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"license": "MIT"
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dev": true,
"license": "MIT",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/commander": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz",
"integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 6"
}
},
"node_modules/compare-version": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz",
"integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/compress-commons": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz",
"integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"buffer-crc32": "^0.2.13",
"crc32-stream": "^4.0.2",
"normalize-path": "^3.0.0",
"readable-stream": "^3.6.0"
},
"engines": {
"node": ">= 10"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true,
"license": "MIT"
},
"node_modules/config-file-ts": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/config-file-ts/-/config-file-ts-0.2.6.tgz",
"integrity": "sha512-6boGVaglwblBgJqGyxm4+xCmEGcWgnWHSWHY5jad58awQhB6gftq0G8HbzU39YqCIYHMLAiL1yjwiZ36m/CL8w==",
"dev": true,
"license": "MIT",
"dependencies": {
"glob": "^10.3.10",
"typescript": "^5.3.3"
}
},
"node_modules/config-file-ts/node_modules/glob": {
"version": "10.4.5",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
"integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
"dev": true,
"license": "ISC",
"dependencies": {
"foreground-child": "^3.1.0",
"jackspeak": "^3.1.2",
"minimatch": "^9.0.4",
"minipass": "^7.1.2",
"package-json-from-dist": "^1.0.0",
"path-scurry": "^1.11.1"
},
"bin": {
"glob": "dist/esm/bin.mjs"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/config-file-ts/node_modules/minimatch": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=16 || 14 >=14.17"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/config-file-ts/node_modules/minipass": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
"dev": true,
"license": "MIT"
},
"node_modules/crc": {
"version": "3.8.0",
"resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz",
"integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"buffer": "^5.1.0"
}
},
"node_modules/crc-32": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"crc32": "bin/crc32.njs"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/crc32-stream": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz",
"integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"crc-32": "^1.2.0",
"readable-stream": "^3.4.0"
},
"engines": {
"node": ">= 10"
}
},
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"dev": true,
"license": "MIT",
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
"which": "^2.0.1"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/crypto-js": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
"license": "MIT"
},
"node_modules/debug": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/decompress-response": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"mimic-response": "^3.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/decompress-response/node_modules/mimic-response": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/defer-to-connect": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
"integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/define-data-property": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"gopd": "^1.0.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/define-properties": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
"integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"define-data-property": "^1.0.1",
"has-property-descriptors": "^1.0.0",
"object-keys": "^1.1.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/detect-node": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
"integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/dir-compare": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-3.3.0.tgz",
"integrity": "sha512-J7/et3WlGUCxjdnD3HAAzQ6nsnc0WL6DD7WcwJb7c39iH1+AWfg+9OqzJNaI6PkBwBvm1mhZNL9iY/nRiZXlPg==",
"dev": true,
"license": "MIT",
"dependencies": {
"buffer-equal": "^1.0.0",
"minimatch": "^3.0.4"
}
},
"node_modules/dir-compare/node_modules/brace-expansion": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/dir-compare/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/dmg-builder": {
"version": "24.13.3",
"resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-24.13.3.tgz",
"integrity": "sha512-rcJUkMfnJpfCboZoOOPf4L29TRtEieHNOeAbYPWPxlaBw/Z1RKrRA86dOI9rwaI4tQSc/RD82zTNHprfUHXsoQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"app-builder-lib": "24.13.3",
"builder-util": "24.13.1",
"builder-util-runtime": "9.2.4",
"fs-extra": "^10.1.0",
"iconv-lite": "^0.6.2",
"js-yaml": "^4.1.0"
},
"optionalDependencies": {
"dmg-license": "^1.0.11"
}
},
"node_modules/dmg-builder/node_modules/fs-extra": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/dmg-builder/node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"universalify": "^2.0.0"
},
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/dmg-builder/node_modules/universalify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/dmg-license": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz",
"integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==",
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"dependencies": {
"@types/plist": "^3.0.1",
"@types/verror": "^1.10.3",
"ajv": "^6.10.0",
"crc": "^3.8.0",
"iconv-corefoundation": "^1.1.7",
"plist": "^3.0.4",
"smart-buffer": "^4.0.2",
"verror": "^1.10.0"
},
"bin": {
"dmg-license": "bin/dmg-license.js"
},
"engines": {
"node": ">=8"
}
},
"node_modules/dotenv": {
"version": "9.0.2",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-9.0.2.tgz",
"integrity": "sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=10"
}
},
"node_modules/dotenv-expand": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz",
"integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==",
"dev": true,
"license": "BSD-2-Clause"
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"dev": true,
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
"gopd": "^1.2.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/eastasianwidth": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
"dev": true,
"license": "MIT"
},
"node_modules/ejs": {
"version": "3.1.10",
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
"integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"jake": "^10.8.5"
},
"bin": {
"ejs": "bin/cli.js"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/electron": {
"version": "27.3.11",
"resolved": "https://registry.npmjs.org/electron/-/electron-27.3.11.tgz",
"integrity": "sha512-E1SiyEoI8iW5LW/MigCr7tJuQe7+0105UjqY7FkmCD12e2O6vtUbQ0j05HaBh2YgvkcEVgvQ2A8suIq5b5m6Gw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@electron/get": "^2.0.0",
"@types/node": "^18.11.18",
"extract-zip": "^2.0.1"
},
"bin": {
"electron": "cli.js"
},
"engines": {
"node": ">= 12.20.55"
}
},
"node_modules/electron-builder": {
"version": "24.13.3",
"resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-24.13.3.tgz",
"integrity": "sha512-yZSgVHft5dNVlo31qmJAe4BVKQfFdwpRw7sFp1iQglDRCDD6r22zfRJuZlhtB5gp9FHUxCMEoWGq10SkCnMAIg==",
"dev": true,
"license": "MIT",
"dependencies": {
"app-builder-lib": "24.13.3",
"builder-util": "24.13.1",
"builder-util-runtime": "9.2.4",
"chalk": "^4.1.2",
"dmg-builder": "24.13.3",
"fs-extra": "^10.1.0",
"is-ci": "^3.0.0",
"lazy-val": "^1.0.5",
"read-config-file": "6.3.2",
"simple-update-notifier": "2.0.0",
"yargs": "^17.6.2"
},
"bin": {
"electron-builder": "cli.js",
"install-app-deps": "install-app-deps.js"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/electron-builder-squirrel-windows": {
"version": "24.13.3",
"resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-24.13.3.tgz",
"integrity": "sha512-oHkV0iogWfyK+ah9ZIvMDpei1m9ZRpdXcvde1wTpra2U8AFDNNpqJdnin5z+PM1GbQ5BoaKCWas2HSjtR0HwMg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"app-builder-lib": "24.13.3",
"archiver": "^5.3.1",
"builder-util": "24.13.1",
"fs-extra": "^10.1.0"
}
},
"node_modules/electron-builder-squirrel-windows/node_modules/fs-extra": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/electron-builder-squirrel-windows/node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"universalify": "^2.0.0"
},
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/electron-builder-squirrel-windows/node_modules/universalify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/electron-builder/node_modules/fs-extra": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/electron-builder/node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"universalify": "^2.0.0"
},
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/electron-builder/node_modules/universalify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/electron-publish": {
"version": "24.13.1",
"resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-24.13.1.tgz",
"integrity": "sha512-2ZgdEqJ8e9D17Hwp5LEq5mLQPjqU3lv/IALvgp+4W8VeNhryfGhYEQC/PgDPMrnWUp+l60Ou5SJLsu+k4mhQ8A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/fs-extra": "^9.0.11",
"builder-util": "24.13.1",
"builder-util-runtime": "9.2.4",
"chalk": "^4.1.2",
"fs-extra": "^10.1.0",
"lazy-val": "^1.0.5",
"mime": "^2.5.2"
}
},
"node_modules/electron-publish/node_modules/fs-extra": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/electron-publish/node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"universalify": "^2.0.0"
},
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/electron-publish/node_modules/universalify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true,
"license": "MIT"
},
"node_modules/end-of-stream": {
"version": "1.4.5",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
"integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
"dev": true,
"license": "MIT",
"dependencies": {
"once": "^1.4.0"
}
},
"node_modules/env-paths": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
"integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/err-code": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
"integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
"dev": true,
"license": "MIT"
},
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-set-tostringtag": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.6",
"has-tostringtag": "^1.0.2",
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es6-error": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
"integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/escalade": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/extract-zip": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
"integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"debug": "^4.1.1",
"get-stream": "^5.1.0",
"yauzl": "^2.10.0"
},
"bin": {
"extract-zip": "cli.js"
},
"engines": {
"node": ">= 10.17.0"
},
"optionalDependencies": {
"@types/yauzl": "^2.9.1"
}
},
"node_modules/extsprintf": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz",
"integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==",
"dev": true,
"engines": [
"node >=0.6.0"
],
"license": "MIT",
"optional": true
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true,
"license": "MIT"
},
"node_modules/fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"dev": true,
"license": "MIT"
},
"node_modules/fd-slicer": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
"integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
"dev": true,
"license": "MIT",
"dependencies": {
"pend": "~1.2.0"
}
},
"node_modules/filelist": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
"integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"minimatch": "^5.0.1"
}
},
"node_modules/foreground-child": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
"integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
"dev": true,
"license": "ISC",
"dependencies": {
"cross-spawn": "^7.0.6",
"signal-exit": "^4.0.1"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/form-data": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
"dev": true,
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/fs-extra": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
"dev": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
},
"engines": {
"node": ">=6 <7 || >=8"
}
},
"node_modules/fs-minipass": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
"integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
"dev": true,
"license": "ISC",
"dependencies": {
"minipass": "^3.0.0"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/fs-minipass/node_modules/minipass": {
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
"dev": true,
"license": "ISC",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true,
"license": "ISC"
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true,
"license": "ISC",
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2",
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"dev": true,
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/get-stream": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
"integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
"dev": true,
"license": "MIT",
"dependencies": {
"pump": "^3.0.0"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
"license": "ISC",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/glob/node_modules/brace-expansion": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/glob/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/global-agent": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz",
"integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==",
"dev": true,
"license": "BSD-3-Clause",
"optional": true,
"dependencies": {
"boolean": "^3.0.1",
"es6-error": "^4.1.1",
"matcher": "^3.0.0",
"roarr": "^2.15.3",
"semver": "^7.3.2",
"serialize-error": "^7.0.1"
},
"engines": {
"node": ">=10.0"
}
},
"node_modules/global-agent/node_modules/semver": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"dev": true,
"license": "ISC",
"optional": true,
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/globalthis": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
"integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"define-properties": "^1.2.1",
"gopd": "^1.0.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/got": {
"version": "11.8.6",
"resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz",
"integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@sindresorhus/is": "^4.0.0",
"@szmarczak/http-timer": "^4.0.5",
"@types/cacheable-request": "^6.0.1",
"@types/responselike": "^1.0.0",
"cacheable-lookup": "^5.0.3",
"cacheable-request": "^7.0.2",
"decompress-response": "^6.0.0",
"http2-wrapper": "^1.0.0-beta.5.2",
"lowercase-keys": "^2.0.0",
"p-cancelable": "^2.0.0",
"responselike": "^2.0.0"
},
"engines": {
"node": ">=10.19.0"
},
"funding": {
"url": "https://github.com/sindresorhus/got?sponsor=1"
}
},
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"dev": true,
"license": "ISC"
},
"node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/has-property-descriptors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"es-define-property": "^1.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-tostringtag": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"dev": true,
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/hosted-git-info": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz",
"integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==",
"dev": true,
"license": "ISC",
"dependencies": {
"lru-cache": "^6.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/http-cache-semantics": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
"integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==",
"dev": true,
"license": "BSD-2-Clause"
},
"node_modules/http-proxy-agent": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
"integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@tootallnate/once": "2",
"agent-base": "6",
"debug": "4"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/http2-wrapper": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
"integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==",
"dev": true,
"license": "MIT",
"dependencies": {
"quick-lru": "^5.1.1",
"resolve-alpn": "^1.0.0"
},
"engines": {
"node": ">=10.19.0"
}
},
"node_modules/https-proxy-agent": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
"dev": true,
"license": "MIT",
"dependencies": {
"agent-base": "6",
"debug": "4"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/iconv-corefoundation": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz",
"integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==",
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"dependencies": {
"cli-truncate": "^2.1.0",
"node-addon-api": "^1.6.3"
},
"engines": {
"node": "^8.11.2 || >=10"
}
},
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"dev": true,
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "BSD-3-Clause"
},
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
"dev": true,
"license": "ISC",
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true,
"license": "ISC"
},
"node_modules/is-ci": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz",
"integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"ci-info": "^3.2.0"
},
"bin": {
"is-ci": "bin.js"
}
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/isbinaryfile": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.4.tgz",
"integrity": "sha512-YKBKVkKhty7s8rxddb40oOkuP0NbaeXrQvLin6QMHL7Ypiy2RW9LwOVrVgZRyOrhQlayMd9t+D8yDy8MKFTSDQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 18.0.0"
},
"funding": {
"url": "https://github.com/sponsors/gjtorikian/"
}
},
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
"dev": true,
"license": "ISC"
},
"node_modules/jackspeak": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"@isaacs/cliui": "^8.0.2"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
},
"optionalDependencies": {
"@pkgjs/parseargs": "^0.11.0"
}
},
"node_modules/jake": {
"version": "10.9.2",
"resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz",
"integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"async": "^3.2.3",
"chalk": "^4.0.2",
"filelist": "^1.0.4",
"minimatch": "^3.1.2"
},
"bin": {
"jake": "bin/cli.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/jake/node_modules/brace-expansion": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/jake/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dev": true,
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
},
"bin": {
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
"dev": true,
"license": "MIT"
},
"node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true,
"license": "MIT"
},
"node_modules/json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
"integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
"dev": true,
"license": "ISC",
"optional": true
},
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"dev": true,
"license": "MIT",
"bin": {
"json5": "lib/cli.js"
},
"engines": {
"node": ">=6"
}
},
"node_modules/jsonfile": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
"integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
"dev": true,
"license": "MIT",
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/keyv": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
"integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
"dev": true,
"license": "MIT",
"dependencies": {
"json-buffer": "3.0.1"
}
},
"node_modules/lazy-val": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz",
"integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==",
"dev": true,
"license": "MIT"
},
"node_modules/lazystream": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz",
"integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"readable-stream": "^2.0.5"
},
"engines": {
"node": ">= 0.6.3"
}
},
"node_modules/lazystream/node_modules/readable-stream": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"node_modules/lazystream/node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/lazystream/node_modules/string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"safe-buffer": "~5.1.0"
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true,
"license": "MIT"
},
"node_modules/lodash.defaults": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/lodash.difference": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz",
"integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/lodash.flatten": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
"integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/lodash.union": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
"integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/lowercase-keys": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
"integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"license": "ISC",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/matcher": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz",
"integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"escape-string-regexp": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/mime": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
"integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
"dev": true,
"license": "MIT",
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dev": true,
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mimic-response": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
"integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/minimatch": {
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=10"
}
},
"node_modules/minimist": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/minipass": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
"integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=8"
}
},
"node_modules/minizlib": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
"integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
"dev": true,
"license": "MIT",
"dependencies": {
"minipass": "^3.0.0",
"yallist": "^4.0.0"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/minizlib/node_modules/minipass": {
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
"dev": true,
"license": "ISC",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/mkdirp": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
"dev": true,
"license": "MIT",
"bin": {
"mkdirp": "bin/cmd.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true,
"license": "MIT"
},
"node_modules/node-addon-api": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz",
"integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/normalize-url": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
"integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/object-keys": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">= 0.4"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dev": true,
"license": "ISC",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/p-cancelable": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
"integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/package-json-from-dist": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
"dev": true,
"license": "BlueOak-1.0.0"
},
"node_modules/path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/path-scurry": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"lru-cache": "^10.2.0",
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
},
"engines": {
"node": ">=16 || 14 >=14.18"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/path-scurry/node_modules/lru-cache": {
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
"dev": true,
"license": "ISC"
},
"node_modules/pend": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
"integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
"dev": true,
"license": "MIT"
},
"node_modules/plist": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz",
"integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@xmldom/xmldom": "^0.8.8",
"base64-js": "^1.5.1",
"xmlbuilder": "^15.1.1"
},
"engines": {
"node": ">=10.4.0"
}
},
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/progress": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/promise-retry": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz",
"integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==",
"dev": true,
"license": "MIT",
"dependencies": {
"err-code": "^2.0.2",
"retry": "^0.12.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/pump": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
"integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
"dev": true,
"license": "MIT",
"dependencies": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/quick-lru": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
"integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/read-config-file": {
"version": "6.3.2",
"resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.3.2.tgz",
"integrity": "sha512-M80lpCjnE6Wt6zb98DoW8WHR09nzMSpu8XHtPkiTHrJ5Az9CybfeQhTJ8D7saeBHpGhLPIVyA8lcL6ZmdKwY6Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"config-file-ts": "^0.2.4",
"dotenv": "^9.0.2",
"dotenv-expand": "^5.1.0",
"js-yaml": "^4.1.0",
"json5": "^2.2.0",
"lazy-val": "^1.0.4"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/readdir-glob": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz",
"integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"minimatch": "^5.1.0"
}
},
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/resolve-alpn": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
"integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==",
"dev": true,
"license": "MIT"
},
"node_modules/responselike": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz",
"integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==",
"dev": true,
"license": "MIT",
"dependencies": {
"lowercase-keys": "^2.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/retry": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
"integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 4"
}
},
"node_modules/roarr": {
"version": "2.15.4",
"resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz",
"integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==",
"dev": true,
"license": "BSD-3-Clause",
"optional": true,
"dependencies": {
"boolean": "^3.0.1",
"detect-node": "^2.0.4",
"globalthis": "^1.0.1",
"json-stringify-safe": "^5.0.1",
"semver-compare": "^1.0.0",
"sprintf-js": "^1.1.2"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT",
"peer": true
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true,
"license": "MIT"
},
"node_modules/sanitize-filename": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz",
"integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==",
"dev": true,
"license": "WTFPL OR ISC",
"dependencies": {
"truncate-utf8-bytes": "^1.0.0"
}
},
"node_modules/sax": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz",
"integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
"dev": true,
"license": "ISC"
},
"node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/semver-compare": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
"integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/serialize-error": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz",
"integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"type-fest": "^0.13.1"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"dev": true,
"license": "MIT",
"dependencies": {
"shebang-regex": "^3.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/shebang-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/signal-exit": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/simple-update-notifier": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
"integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
"dev": true,
"license": "MIT",
"dependencies": {
"semver": "^7.5.3"
},
"engines": {
"node": ">=10"
}
},
"node_modules/simple-update-notifier/node_modules/semver": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"dev": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/slice-ansi": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz",
"integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"ansi-styles": "^4.0.0",
"astral-regex": "^2.0.0",
"is-fullwidth-code-point": "^3.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/smart-buffer": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
"integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">= 6.0.0",
"npm": ">= 3.0.0"
}
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/source-map-support": {
"version": "0.5.21",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
"dev": true,
"license": "MIT",
"dependencies": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
}
},
"node_modules/sprintf-js": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
"integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
"dev": true,
"license": "BSD-3-Clause",
"optional": true
},
"node_modules/stat-mode": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz",
"integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 6"
}
},
"node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"safe-buffer": "~5.2.0"
}
},
"node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/string-width-cjs": {
"name": "string-width",
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-ansi-cjs": {
"name": "strip-ansi",
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/sumchecker": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz",
"integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"debug": "^4.1.0"
},
"engines": {
"node": ">= 8.0"
}
},
"node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/tar": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
"integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
"dev": true,
"license": "ISC",
"dependencies": {
"chownr": "^2.0.0",
"fs-minipass": "^2.0.0",
"minipass": "^5.0.0",
"minizlib": "^2.1.1",
"mkdirp": "^1.0.3",
"yallist": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/tar-stream": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"bl": "^4.0.3",
"end-of-stream": "^1.4.1",
"fs-constants": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^3.1.1"
},
"engines": {
"node": ">=6"
}
},
"node_modules/temp-file": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz",
"integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==",
"dev": true,
"license": "MIT",
"dependencies": {
"async-exit-hook": "^2.0.1",
"fs-extra": "^10.0.0"
}
},
"node_modules/temp-file/node_modules/fs-extra": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/temp-file/node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"universalify": "^2.0.0"
},
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/temp-file/node_modules/universalify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/tmp": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz",
"integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=14.14"
}
},
"node_modules/tmp-promise": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz",
"integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"tmp": "^0.2.0"
}
},
"node_modules/truncate-utf8-bytes": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz",
"integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==",
"dev": true,
"license": "WTFPL",
"dependencies": {
"utf8-byte-length": "^1.0.1"
}
},
"node_modules/type-fest": {
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz",
"integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==",
"dev": true,
"license": "(MIT OR CC0-1.0)",
"optional": true,
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/typescript": {
"version": "5.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true,
"license": "MIT"
},
"node_modules/universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 4.0.0"
}
},
"node_modules/uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"punycode": "^2.1.0"
}
},
"node_modules/utf8-byte-length": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz",
"integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==",
"dev": true,
"license": "(WTFPL OR MIT)"
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/verror": {
"version": "1.10.1",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz",
"integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"assert-plus": "^1.0.0",
"core-util-is": "1.0.2",
"extsprintf": "^1.2.0"
},
"engines": {
"node": ">=0.6.0"
}
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"license": "ISC",
"dependencies": {
"isexe": "^2.0.0"
},
"bin": {
"node-which": "bin/node-which"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/wrap-ansi-cjs": {
"name": "wrap-ansi",
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"dev": true,
"license": "ISC"
},
"node_modules/xmlbuilder": {
"version": "15.1.1",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz",
"integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8.0"
}
},
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=10"
}
},
"node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true,
"license": "ISC"
},
"node_modules/yargs": {
"version": "17.7.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
"dev": true,
"license": "MIT",
"dependencies": {
"cliui": "^8.0.1",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.3",
"y18n": "^5.0.5",
"yargs-parser": "^21.1.1"
},
"engines": {
"node": ">=12"
}
},
"node_modules/yargs-parser": {
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/yauzl": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
"integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
"dev": true,
"license": "MIT",
"dependencies": {
"buffer-crc32": "~0.2.3",
"fd-slicer": "~1.1.0"
}
},
"node_modules/zip-stream": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz",
"integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"archiver-utils": "^3.0.4",
"compress-commons": "^4.1.2",
"readable-stream": "^3.6.0"
},
"engines": {
"node": ">= 10"
}
},
"node_modules/zip-stream/node_modules/archiver-utils": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz",
"integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"glob": "^7.2.3",
"graceful-fs": "^4.2.0",
"lazystream": "^1.0.0",
"lodash.defaults": "^4.2.0",
"lodash.difference": "^4.5.0",
"lodash.flatten": "^4.4.0",
"lodash.isplainobject": "^4.0.6",
"lodash.union": "^4.6.0",
"normalize-path": "^3.0.0",
"readable-stream": "^3.6.0"
},
"engines": {
"node": ">= 10"
}
}
}
}

57
package.json Normal file
View File

@@ -0,0 +1,57 @@
{
"name": "fallout-shelter-save-editor",
"version": "1.0.0",
"description": "A modern save editor for Fallout Shelter with AES decryption support",
"main": "main.js",
"scripts": {
"start": "electron .",
"dev": "electron . --dev",
"build": "electron-builder",
"build-win": "electron-builder --win",
"build-mac": "electron-builder --mac",
"build-linux": "electron-builder --linux",
"pack": "electron-builder --dir",
"dist": "electron-builder --publish=never"
},
"keywords": [
"fallout-shelter",
"save-editor",
"game-modding",
"electron"
],
"author": "Kiro AI Assistant",
"license": "MIT",
"devDependencies": {
"electron": "^27.0.0",
"electron-builder": "^24.6.4"
},
"dependencies": {
"crypto-js": "^4.2.0"
},
"build": {
"appId": "com.kiro.fallout-shelter-editor",
"productName": "Fallout Shelter Save Editor",
"directories": {
"output": "dist"
},
"files": [
"main.js",
"renderer.js",
"index.html",
"styles.css",
"assets/**/*"
],
"win": {
"target": "nsis",
"icon": "assets/icon.ico"
},
"mac": {
"target": "dmg",
"icon": "assets/icon.icns"
},
"linux": {
"target": "AppImage",
"icon": "assets/icon.png"
}
}
}

824
renderer.js Normal file
View File

@@ -0,0 +1,824 @@
const { ipcRenderer } = require('electron');
const CryptoJS = require('crypto-js');
// Application state
let currentSaveData = null;
let currentFilePath = null;
let isModified = false;
// AES encryption constants
const AES_KEY_WORDS = [2815074099, 1725469378, 4039046167, 874293617, 3063605751, 3133984764, 4097598161, 3620741625];
const AES_KEY = CryptoJS.lib.WordArray.create(AES_KEY_WORDS);
const AES_IV = CryptoJS.enc.Hex.parse('7475383967656A693334307438397532');
// Initialize the application
document.addEventListener('DOMContentLoaded', () => {
setTimeout(() => {
startupAnimation();
}, 100);
});
function startupAnimation() {
const overlay = document.getElementById('startup-overlay');
const progressFill = document.getElementById('startup-progress');
const progressPercent = document.getElementById('progress-percent');
const statusText = document.getElementById('startup-status');
if (!overlay || !progressFill || !progressPercent || !statusText) {
initializeApp();
return;
}
// Initialize
progressFill.style.width = '0%';
progressPercent.textContent = '0%';
const steps = [
{ progress: 25, status: 'Loading Vault-Tec Database...', delay: 300 },
{ progress: 50, status: 'Initializing Encryption...', delay: 300 },
{ progress: 75, status: 'Connecting Systems...', delay: 300 },
{ progress: 100, status: 'Welcome to Vault-Tec!', delay: 400 }
];
let currentStep = 0;
function updateProgress() {
if (currentStep < steps.length) {
const step = steps[currentStep];
statusText.textContent = step.status;
progressFill.style.width = step.progress + '%';
progressPercent.textContent = step.progress + '%';
currentStep++;
setTimeout(updateProgress, step.delay);
} else {
setTimeout(() => {
overlay.classList.add('fade-out');
setTimeout(() => {
overlay.style.display = 'none';
initializeApp();
}, 800);
}, 200);
}
}
setTimeout(updateProgress, 500);
}
function initializeApp() {
initializeEventListeners();
initializeTabSystem();
updateUI();
setStatus('Ready');
setTimeout(() => {
showToast('Vault-Tec Save Editor ready!', 'success');
}, 200);
}
function initializeEventListeners() {
// Navigation tabs
document.querySelectorAll('.nav-tab').forEach(tab => {
tab.addEventListener('click', () => switchTab(tab.dataset.tab));
});
// File operations
const openFileBtn = document.getElementById('open-file-btn');
const getStartedBtn = document.getElementById('get-started-btn');
const saveBtn = document.getElementById('save-btn');
const backupBtn = document.getElementById('backup-btn');
if (openFileBtn) openFileBtn.addEventListener('click', openFile);
if (getStartedBtn) getStartedBtn.addEventListener('click', openFile);
if (saveBtn) saveBtn.addEventListener('click', saveFile);
if (backupBtn) backupBtn.addEventListener('click', createBackup);
// Resource operations
const maxResourcesBtn = document.getElementById('max-resources-btn');
const resetResourcesBtn = document.getElementById('reset-resources-btn');
const applyResourcesBtn = document.getElementById('apply-resources-btn');
if (maxResourcesBtn) maxResourcesBtn.addEventListener('click', maxAllResources);
if (resetResourcesBtn) resetResourcesBtn.addEventListener('click', resetResources);
if (applyResourcesBtn) applyResourcesBtn.addEventListener('click', applyResourceChanges);
// Vault operations
const applyVaultBtn = document.getElementById('apply-vault-btn');
if (applyVaultBtn) applyVaultBtn.addEventListener('click', applyVaultChanges);
// Raw data operations
const formatJsonBtn = document.getElementById('format-json-btn');
const copyJsonBtn = document.getElementById('copy-json-btn');
if (formatJsonBtn) formatJsonBtn.addEventListener('click', formatJSON);
if (copyJsonBtn) copyJsonBtn.addEventListener('click', copyJSON);
// Dweller operations
const maxAllDwellersBtn = document.getElementById('max-all-dwellers-btn');
const refreshDwellersBtn = document.getElementById('refresh-dwellers-btn');
if (maxAllDwellersBtn) maxAllDwellersBtn.addEventListener('click', maxAllDwellers);
if (refreshDwellersBtn) refreshDwellersBtn.addEventListener('click', populateDwellers);
// IPC listeners
ipcRenderer.on('file-opened', handleFileOpened);
ipcRenderer.on('menu-save', saveFile);
}
function initializeTabSystem() {
switchTab('overview');
}
function switchTab(tabName) {
document.querySelectorAll('.nav-tab').forEach(tab => {
tab.classList.toggle('active', tab.dataset.tab === tabName);
});
document.querySelectorAll('.tab-content').forEach(content => {
content.classList.toggle('active', content.id === `${tabName}-tab`);
});
}
function openFile() {
ipcRenderer.send('open-file-dialog');
}
async function handleFileOpened(event, fileData) {
showLoading(true);
setStatus('Loading save file...');
try {
currentFilePath = fileData.path;
document.getElementById('current-file').textContent = fileData.name;
const decryptedData = decryptSaveData(fileData.data.trim());
currentSaveData = JSON.parse(decryptedData);
populateResourceInputs();
populateVaultInfo();
populateDwellers();
updateRawData();
document.getElementById('save-btn').disabled = false;
document.getElementById('backup-btn').disabled = false;
switchTab('resources');
setStatus('Save file loaded successfully');
showToast('Save file loaded successfully', 'success');
setModified(false);
} catch (error) {
console.error('Error loading save file:', error);
showToast(`Error loading save file: ${error.message}`, 'error');
setStatus('Error loading save file');
} finally {
showLoading(false);
}
}
function decryptSaveData(base64Data) {
try {
const ciphertext = CryptoJS.enc.Base64.parse(base64Data);
const cipherParams = CryptoJS.lib.CipherParams.create({
ciphertext: ciphertext
});
const decrypted = CryptoJS.AES.decrypt(cipherParams, AES_KEY, {
iv: AES_IV,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
const decryptedString = decrypted.toString(CryptoJS.enc.Utf8);
if (!decryptedString) {
throw new Error('Decryption failed - invalid key or corrupted data');
}
return decryptedString;
} catch (error) {
throw new Error(`Decryption failed: ${error.message}`);
}
}
function encryptSaveData(jsonString) {
try {
const encrypted = CryptoJS.AES.encrypt(jsonString, AES_KEY, {
iv: AES_IV,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
return encrypted.toString();
} catch (error) {
throw new Error(`Encryption failed: ${error.message}`);
}
}
function populateResourceInputs() {
if (!currentSaveData || !currentSaveData.vault || !currentSaveData.vault.storage) {
console.log('No vault storage data found');
return;
}
const resources = currentSaveData.vault.storage.resources;
console.log('Available resources:', Object.keys(resources));
const resourceMap = {
'caps-input': 'Nuka',
'food-input': 'Food',
'water-input': 'Water',
'power-input': 'Energy',
'stimpaks-input': 'StimPack',
'radaway-input': 'RadAway',
'quantum-input': 'NukaColaQuantum',
'lunchbox-input': 'Lunchbox',
'mrhandy-input': 'MrHandy',
'petcarrier-input': 'PetCarrier'
};
Object.entries(resourceMap).forEach(([inputId, resourceKey]) => {
const input = document.getElementById(inputId);
if (input && resources[resourceKey] !== undefined) {
input.value = Math.floor(resources[resourceKey]);
console.log(`Set ${inputId} to ${resources[resourceKey]}`);
} else {
console.log(`Could not find ${inputId} or ${resourceKey}`);
}
});
}
function populateVaultInfo() {
if (!currentSaveData || !currentSaveData.vault) {
return;
}
const vault = currentSaveData.vault;
const vaultNameInput = document.getElementById('vault-name-input');
if (vaultNameInput && vault.VaultName !== undefined) {
vaultNameInput.value = vault.VaultName;
}
const vaultModeInput = document.getElementById('vault-mode-input');
if (vaultModeInput && vault.VaultMode !== undefined) {
vaultModeInput.value = vault.VaultMode;
}
const vaultThemeInput = document.getElementById('vault-theme-input');
if (vaultThemeInput && vault.VaultTheme !== undefined) {
vaultThemeInput.value = vault.VaultTheme;
}
}
function applyResourceChanges() {
if (!currentSaveData || !currentSaveData.vault || !currentSaveData.vault.storage) {
showToast('No save data loaded', 'warning');
return;
}
console.log('Applying resource changes...');
const resources = currentSaveData.vault.storage.resources;
console.log('Current resources before changes:', resources);
const resourceMap = {
'caps-input': 'Nuka',
'food-input': 'Food',
'water-input': 'Water',
'power-input': 'Energy',
'stimpaks-input': 'StimPack',
'radaway-input': 'RadAway',
'quantum-input': 'NukaColaQuantum',
'lunchbox-input': 'Lunchbox',
'mrhandy-input': 'MrHandy',
'petcarrier-input': 'PetCarrier'
};
let changesApplied = 0;
Object.entries(resourceMap).forEach(([inputId, resourceKey]) => {
const input = document.getElementById(inputId);
if (input && input.value.trim()) {
const value = parseFloat(input.value);
if (!isNaN(value) && value >= 0) {
console.log(`Changing ${resourceKey} from ${resources[resourceKey]} to ${value}`);
resources[resourceKey] = value;
changesApplied++;
}
}
});
console.log('Resources after changes:', resources);
console.log(`Applied ${changesApplied} changes`);
if (changesApplied > 0) {
setModified(true);
updateRawData();
showToast(`Applied ${changesApplied} resource changes`, 'success');
} else {
showToast('No valid changes to apply', 'warning');
}
}
function maxAllResources() {
const maxValues = {
'caps-input': 999999,
'food-input': 999999,
'water-input': 999999,
'power-input': 999999,
'stimpaks-input': 999999,
'radaway-input': 999999,
'quantum-input': 999,
'lunchbox-input': 999,
'mrhandy-input': 99,
'petcarrier-input': 999
};
Object.entries(maxValues).forEach(([inputId, value]) => {
const input = document.getElementById(inputId);
if (input) {
input.value = value;
}
});
applyResourceChanges();
}
function resetResources() {
populateResourceInputs();
showToast('Resources reset to saved values', 'info');
}
function applyVaultChanges() {
if (!currentSaveData || !currentSaveData.vault) {
showToast('No save data loaded', 'warning');
return;
}
const vault = currentSaveData.vault;
let changesApplied = 0;
const vaultNameInput = document.getElementById('vault-name-input');
if (vaultNameInput && vaultNameInput.value.trim()) {
vault.VaultName = vaultNameInput.value.trim();
changesApplied++;
}
const vaultModeInput = document.getElementById('vault-mode-input');
if (vaultModeInput && vaultModeInput.value) {
vault.VaultMode = vaultModeInput.value;
changesApplied++;
}
const vaultThemeInput = document.getElementById('vault-theme-input');
if (vaultThemeInput && vaultThemeInput.value.trim()) {
const theme = parseInt(vaultThemeInput.value);
if (!isNaN(theme) && theme >= 0) {
vault.VaultTheme = theme;
changesApplied++;
}
}
if (changesApplied > 0) {
setModified(true);
updateRawData();
showToast(`Applied ${changesApplied} vault changes`, 'success');
} else {
showToast('No valid changes to apply', 'warning');
}
}
function updateRawData() {
const textarea = document.getElementById('raw-data-textarea');
if (currentSaveData && textarea) {
textarea.value = JSON.stringify(currentSaveData, null, 2);
}
}
function formatJSON() {
const textarea = document.getElementById('raw-data-textarea');
if (!textarea) return;
try {
const parsed = JSON.parse(textarea.value);
textarea.value = JSON.stringify(parsed, null, 2);
showToast('JSON formatted successfully', 'success');
} catch (error) {
showToast('Invalid JSON format', 'error');
}
}
function copyJSON() {
const textarea = document.getElementById('raw-data-textarea');
if (!textarea) return;
textarea.select();
document.execCommand('copy');
showToast('JSON copied to clipboard', 'success');
}
async function saveFile() {
if (!currentSaveData || !currentFilePath) {
showToast('No file loaded to save', 'warning');
return;
}
console.log('Starting save process...');
console.log('Current file path:', currentFilePath);
console.log('Save data exists:', !!currentSaveData);
showLoading(true);
setStatus('Saving file...');
try {
console.log('Converting to JSON...');
const jsonString = JSON.stringify(currentSaveData);
console.log('JSON string length:', jsonString.length);
console.log('Encrypting data...');
const encryptedData = encryptSaveData(jsonString);
console.log('Encrypted data length:', encryptedData.length);
console.log('Sending to main process...');
const result = await ipcRenderer.invoke('save-file', currentFilePath, encryptedData);
console.log('Save result:', result);
if (result.success) {
setModified(false);
setStatus('File saved successfully');
showToast('File saved successfully', 'success');
console.log('Save completed successfully');
} else {
throw new Error(result.error);
}
} catch (error) {
console.error('Error saving file:', error);
showToast(`Error saving file: ${error.message}`, 'error');
setStatus('Error saving file');
} finally {
showLoading(false);
}
}
async function createBackup() {
if (!currentFilePath) {
showToast('No file loaded to backup', 'warning');
return;
}
try {
const result = await ipcRenderer.invoke('create-backup', currentFilePath);
if (result.success) {
showToast(`Backup created: ${require('path').basename(result.backupPath)}`, 'success');
} else {
throw new Error(result.error);
}
} catch (error) {
console.error('Error creating backup:', error);
showToast(`Error creating backup: ${error.message}`, 'error');
}
}
function setStatus(message) {
const statusText = document.getElementById('status-text');
if (statusText) {
statusText.textContent = message;
}
}
function setModified(modified) {
isModified = modified;
updateUI();
}
function updateUI() {
if (isModified) {
document.title = 'Fallout Shelter Save Editor - Modified';
} else {
document.title = 'Fallout Shelter Save Editor';
}
}
function showLoading(show) {
const overlay = document.getElementById('loading-overlay');
if (overlay) {
overlay.classList.toggle('show', show);
}
}
function showToast(message, type = 'info') {
const container = document.getElementById('toast-container');
if (!container) return;
const toast = document.createElement('div');
toast.className = `toast ${type}`;
toast.textContent = message;
container.appendChild(toast);
setTimeout(() => {
toast.remove();
}, 4000);
}
// Dwellers Management
function populateDwellers() {
const dwellersList = document.getElementById('dwellers-list');
if (!currentSaveData || !currentSaveData.dwellers || !currentSaveData.dwellers.dwellers) {
if (dwellersList) {
dwellersList.innerHTML = `
<div class="placeholder">
<i class="fas fa-users"></i>
<p>No dwellers found in save file</p>
</div>
`;
}
return;
}
const dwellers = currentSaveData.dwellers.dwellers;
if (dwellers.length === 0) {
dwellersList.innerHTML = `
<div class="placeholder">
<i class="fas fa-users"></i>
<p>No dwellers in vault</p>
</div>
`;
return;
}
console.log(`Found ${dwellers.length} dwellers`);
dwellersList.innerHTML = dwellers.map((dweller, index) => {
const name = dweller.name || `Dweller ${index + 1}`;
const level = dweller.experience?.currentLevel || 1;
const happiness = Math.round(dweller.happiness?.happinessValue || 0);
const health = Math.round(dweller.health?.healthValue || 100);
const gender = dweller.gender === 2 ? 'Female' : 'Male';
const pregnant = dweller.relations?.pregnant || false;
// SPECIAL stats
const special = dweller.serializeableSpecialStats || {};
const strength = special.stats?.[1] || 1;
const perception = special.stats?.[2] || 1;
const endurance = special.stats?.[3] || 1;
const charisma = special.stats?.[4] || 1;
const intelligence = special.stats?.[5] || 1;
const agility = special.stats?.[6] || 1;
const luck = special.stats?.[7] || 1;
return `
<div class="dweller-card" data-index="${index}">
<div class="dweller-header">
<h3 class="dweller-name">${name}</h3>
<span class="dweller-gender ${gender.toLowerCase()}">${gender}</span>
${pregnant ? '<span class="pregnant-badge">Pregnant</span>' : ''}
</div>
<div class="dweller-stats">
<div class="stat-row">
<span>Level:</span>
<input type="number" class="dweller-input" data-field="experience.currentLevel" value="${level}" min="1" max="50">
</div>
<div class="stat-row">
<span>Happiness:</span>
<input type="number" class="dweller-input" data-field="happiness.happinessValue" value="${happiness}" min="0" max="100">%
</div>
<div class="stat-row">
<span>Health:</span>
<input type="number" class="dweller-input" data-field="health.healthValue" value="${health}" min="0" max="100">%
</div>
</div>
<div class="special-stats">
<h4>SPECIAL</h4>
<div class="special-grid">
<div class="special-stat">
<label>S</label>
<input type="number" class="special-input" data-stat="1" value="${strength}" min="1" max="10">
</div>
<div class="special-stat">
<label>P</label>
<input type="number" class="special-input" data-stat="2" value="${perception}" min="1" max="10">
</div>
<div class="special-stat">
<label>E</label>
<input type="number" class="special-input" data-stat="3" value="${endurance}" min="1" max="10">
</div>
<div class="special-stat">
<label>C</label>
<input type="number" class="special-input" data-stat="4" value="${charisma}" min="1" max="10">
</div>
<div class="special-stat">
<label>I</label>
<input type="number" class="special-input" data-stat="5" value="${intelligence}" min="1" max="10">
</div>
<div class="special-stat">
<label>A</label>
<input type="number" class="special-input" data-stat="6" value="${agility}" min="1" max="10">
</div>
<div class="special-stat">
<label>L</label>
<input type="number" class="special-input" data-stat="7" value="${luck}" min="1" max="10">
</div>
</div>
</div>
<div class="dweller-actions">
<button class="secondary-btn apply-dweller-btn" data-index="${index}">
<i class="fas fa-check"></i>
Apply Changes
</button>
<button class="secondary-btn max-special-btn" data-index="${index}">
<i class="fas fa-arrow-up"></i>
Max SPECIAL
</button>
</div>
</div>
`;
}).join('');
// Add event listeners for dweller editing
addDwellerEventListeners();
}
function addDwellerEventListeners() {
// Apply changes buttons
document.querySelectorAll('.apply-dweller-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
const index = parseInt(e.target.closest('.apply-dweller-btn').dataset.index);
applyDwellerChanges(index);
});
});
// Max SPECIAL buttons
document.querySelectorAll('.max-special-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
const index = parseInt(e.target.closest('.max-special-btn').dataset.index);
maxDwellerSpecial(index);
});
});
}
function applyDwellerChanges(dwellerIndex) {
if (!currentSaveData || !currentSaveData.dwellers || !currentSaveData.dwellers.dwellers[dwellerIndex]) {
showToast('Dweller not found', 'error');
return;
}
const dweller = currentSaveData.dwellers.dwellers[dwellerIndex];
const card = document.querySelector(`.dweller-card[data-index="${dwellerIndex}"]`);
if (!card) return;
let changesApplied = 0;
// Apply basic stats
card.querySelectorAll('.dweller-input').forEach(input => {
const field = input.dataset.field;
const value = parseFloat(input.value);
if (!isNaN(value)) {
setNestedValue(dweller, field, value);
changesApplied++;
}
});
// Apply SPECIAL stats
card.querySelectorAll('.special-input').forEach(input => {
const statIndex = parseInt(input.dataset.stat);
const value = parseInt(input.value);
if (!isNaN(value) && value >= 1 && value <= 10) {
if (!dweller.serializeableSpecialStats) {
dweller.serializeableSpecialStats = { stats: {} };
}
if (!dweller.serializeableSpecialStats.stats) {
dweller.serializeableSpecialStats.stats = {};
}
dweller.serializeableSpecialStats.stats[statIndex] = value;
changesApplied++;
}
});
if (changesApplied > 0) {
setModified(true);
updateRawData();
showToast(`Applied ${changesApplied} changes to dweller`, 'success');
} else {
showToast('No valid changes to apply', 'warning');
}
}
function maxDwellerSpecial(dwellerIndex) {
const card = document.querySelector(`.dweller-card[data-index="${dwellerIndex}"]`);
if (!card) return;
// Set all SPECIAL stats to 10
card.querySelectorAll('.special-input').forEach(input => {
input.value = 10;
});
applyDwellerChanges(dwellerIndex);
}
function setNestedValue(obj, path, value) {
const keys = path.split('.');
let current = obj;
for (let i = 0; i < keys.length - 1; i++) {
if (!current[keys[i]]) {
current[keys[i]] = {};
}
current = current[keys[i]];
}
current[keys[keys.length - 1]] = value;
}
// Advanced Editing Functions
function addAdvancedResourceOptions() {
// Add lunchbox and other premium resources
const advancedResources = {
'lunchbox-input': 'Lunchbox',
'mrhandy-input': 'MrHandy',
'petcarrier-input': 'PetCarrier',
'craftedoutfit-input': 'CraftedOutfit',
'craftedweapon-input': 'CraftedWeapon',
'craftedtheme-input': 'CraftedTheme'
};
if (currentSaveData && currentSaveData.vault && currentSaveData.vault.storage) {
const resources = currentSaveData.vault.storage.resources;
Object.entries(advancedResources).forEach(([inputId, resourceKey]) => {
const input = document.getElementById(inputId);
if (input && resources[resourceKey] !== undefined) {
input.value = Math.floor(resources[resourceKey]);
}
});
}
}
function maxAllDwellers() {
if (!currentSaveData || !currentSaveData.dwellers || !currentSaveData.dwellers.dwellers) {
showToast('No dwellers found', 'warning');
return;
}
const dwellers = currentSaveData.dwellers.dwellers;
let modifiedCount = 0;
dwellers.forEach((dweller, index) => {
// Max level
if (dweller.experience) {
dweller.experience.currentLevel = 50;
dweller.experience.experienceValue = 2916000; // Max XP for level 50
}
// Max happiness and health
if (dweller.happiness) {
dweller.happiness.happinessValue = 100;
}
if (dweller.health) {
dweller.health.healthValue = 100;
}
// Max SPECIAL
if (!dweller.serializeableSpecialStats) {
dweller.serializeableSpecialStats = { stats: {} };
}
if (!dweller.serializeableSpecialStats.stats) {
dweller.serializeableSpecialStats.stats = {};
}
for (let i = 1; i <= 7; i++) {
dweller.serializeableSpecialStats.stats[i] = 10;
}
modifiedCount++;
});
if (modifiedCount > 0) {
setModified(true);
updateRawData();
populateDwellers(); // Refresh the display
showToast(`Maxed out ${modifiedCount} dwellers`, 'success');
}
}
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}

3
requirements.txt Normal file
View File

@@ -0,0 +1,3 @@
PyQt5>=5.15.0
cryptography>=3.4.0
pycryptodome>=3.15.0

73
run_editor.py Normal file
View File

@@ -0,0 +1,73 @@
#!/usr/bin/env python3
"""
Simple launcher for the Fallout Shelter Save Editor
"""
import sys
import subprocess
import os
def check_requirements():
"""Check if required packages are installed."""
required_packages = ['PyQt5', 'cryptography', 'pycryptodome']
missing_packages = []
for package in required_packages:
try:
__import__(package)
except ImportError:
missing_packages.append(package)
return missing_packages
def install_requirements():
"""Install missing requirements."""
print("Installing required packages...")
try:
subprocess.check_call([sys.executable, '-m', 'pip', 'install', '-r', 'requirements.txt'])
return True
except subprocess.CalledProcessError:
return False
def main():
"""Main launcher function."""
print("Fallout Shelter Save Editor Launcher")
print("=" * 40)
# Check for missing packages
missing = check_requirements()
if missing:
print(f"Missing required packages: {', '.join(missing)}")
if os.path.exists('requirements.txt'):
response = input("Would you like to install them now? (y/n): ").lower().strip()
if response == 'y':
if install_requirements():
print("Packages installed successfully!")
else:
print("Failed to install packages. Please install manually:")
print("pip install -r requirements.txt")
return
else:
print("Please install the required packages manually:")
print("pip install -r requirements.txt")
return
else:
print("requirements.txt not found. Please install manually:")
print("pip install PyQt5 cryptography pycryptodome")
return
# Launch the editor
print("Starting Fallout Shelter Save Editor...")
try:
from fallout_shelter_qt_editor import main as editor_main
editor_main()
except ImportError as e:
print(f"Error importing editor: {e}")
print("Make sure fallout_shelter_qt_editor.py is in the same directory.")
except Exception as e:
print(f"Error starting editor: {e}")
if __name__ == "__main__":
main()

4
start-dev.bat Normal file
View File

@@ -0,0 +1,4 @@
@echo off
echo Starting Fallout Shelter Save Editor in Development Mode...
npm run dev
pause

4
start.bat Normal file
View File

@@ -0,0 +1,4 @@
@echo off
echo Starting Fallout Shelter Save Editor...
npm run start
pause

4
steam_autocloud.vdf Normal file
View File

@@ -0,0 +1,4 @@
"steam_autocloud.vdf"
{
"accountid" "1046964385"
}

1167
styles.css Normal file
View File

@@ -0,0 +1,1167 @@
/* Reset and Base Styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
color: #e0e0e0;
overflow: hidden;
}
/* App Container */
.app-container {
display: flex;
flex-direction: column;
height: 100vh;
}
/* Header */
.app-header {
background: rgba(0, 0, 0, 0.3);
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
padding: 12px 20px;
backdrop-filter: blur(10px);
}
.header-content {
display: flex;
justify-content: space-between;
align-items: center;
}
.logo {
display: flex;
align-items: center;
gap: 12px;
}
.logo i {
font-size: 24px;
color: #ffd700;
animation: glow 2s ease-in-out infinite alternate;
}
@keyframes glow {
from { text-shadow: 0 0 5px #ffd700, 0 0 10px #ffd700; }
to { text-shadow: 0 0 10px #ffd700, 0 0 20px #ffd700, 0 0 30px #ffd700; }
}
.logo h1 {
font-size: 20px;
font-weight: 600;
color: #ffffff;
}
.file-info {
font-size: 14px;
color: #b0b0b0;
}
/* Main Content */
.main-content {
display: flex;
flex: 1;
overflow: hidden;
}
/* Sidebar */
.sidebar {
width: 250px;
background: rgba(0, 0, 0, 0.2);
border-right: 1px solid rgba(255, 255, 255, 0.1);
display: flex;
flex-direction: column;
backdrop-filter: blur(5px);
}
.nav-tabs {
padding: 20px 0;
}
.nav-tab {
display: flex;
align-items: center;
gap: 12px;
width: 100%;
padding: 12px 20px;
background: none;
border: none;
color: #b0b0b0;
font-size: 14px;
cursor: pointer;
transition: all 0.3s ease;
border-left: 3px solid transparent;
}
.nav-tab:hover {
background: rgba(255, 255, 255, 0.05);
color: #ffffff;
}
.nav-tab.active {
background: rgba(255, 215, 0, 0.1);
color: #ffd700;
border-left-color: #ffd700;
}
.nav-tab i {
width: 16px;
text-align: center;
}
.quick-actions {
margin-top: auto;
padding: 20px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.quick-actions h3 {
font-size: 14px;
color: #ffffff;
margin-bottom: 15px;
font-weight: 500;
}
.action-btn {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
padding: 10px 12px;
margin-bottom: 8px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 6px;
color: #e0e0e0;
font-size: 13px;
cursor: pointer;
transition: all 0.3s ease;
}
.action-btn:hover:not(:disabled) {
background: rgba(255, 255, 255, 0.1);
border-color: rgba(255, 255, 255, 0.2);
}
.action-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* Content Area */
.content-area {
flex: 1;
overflow: auto;
padding: 20px;
}
.tab-content {
display: none;
height: 100%;
}
.tab-content.active {
display: block;
}
.tab-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
padding-bottom: 15px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.tab-header h2 {
display: flex;
align-items: center;
gap: 12px;
font-size: 24px;
color: #ffffff;
font-weight: 500;
}
.tab-actions {
display: flex;
gap: 10px;
}
/* Welcome Screen */
.welcome-screen {
text-align: center;
max-width: 800px;
margin: 0 auto;
padding: 40px 20px;
}
.welcome-icon {
font-size: 64px;
color: #ffd700;
margin-bottom: 20px;
animation: glow 2s ease-in-out infinite alternate;
}
.welcome-screen h2 {
font-size: 32px;
color: #ffffff;
margin-bottom: 15px;
font-weight: 300;
}
.welcome-screen p {
font-size: 16px;
color: #b0b0b0;
margin-bottom: 40px;
line-height: 1.6;
}
.feature-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 40px;
}
.feature-card {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
padding: 25px 20px;
text-align: center;
transition: all 0.3s ease;
}
.feature-card:hover {
background: rgba(255, 255, 255, 0.08);
border-color: rgba(255, 215, 0, 0.3);
transform: translateY(-2px);
}
.feature-card i {
font-size: 32px;
color: #ffd700;
margin-bottom: 15px;
}
.feature-card h3 {
font-size: 16px;
color: #ffffff;
margin-bottom: 10px;
font-weight: 500;
}
.feature-card p {
font-size: 14px;
color: #b0b0b0;
line-height: 1.4;
}
/* Resource Grid */
.resource-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.resource-card {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
padding: 20px;
display: flex;
align-items: center;
gap: 15px;
transition: all 0.3s ease;
}
.resource-card:hover {
background: rgba(255, 255, 255, 0.08);
border-color: rgba(255, 255, 255, 0.2);
}
.resource-icon {
width: 50px;
height: 50px;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
color: #ffffff;
}
.resource-icon.caps { background: linear-gradient(135deg, #ffd700, #ffed4e); }
.resource-icon.food { background: linear-gradient(135deg, #ff6b6b, #ff8e8e); }
.resource-icon.water { background: linear-gradient(135deg, #4ecdc4, #44a08d); }
.resource-icon.power { background: linear-gradient(135deg, #45b7d1, #96c93d); }
.resource-icon.stimpaks { background: linear-gradient(135deg, #f093fb, #f5576c); }
.resource-icon.radaway { background: linear-gradient(135deg, #4facfe, #00f2fe); }
.resource-icon.quantum { background: linear-gradient(135deg, #a8edea, #fed6e3); }
.resource-icon.lunchbox { background: linear-gradient(135deg, #ff9a9e, #fecfef); }
.resource-icon.mrhandy { background: linear-gradient(135deg, #667eea, #764ba2); }
.resource-icon.petcarrier { background: linear-gradient(135deg, #f093fb, #f5576c); }
.resource-icon.lunchbox { background: linear-gradient(135deg, #ff9a9e, #fecfef); }
.resource-icon.mrhandy { background: linear-gradient(135deg, #667eea, #764ba2); }
.resource-icon.petcarrier { background: linear-gradient(135deg, #f093fb, #f5576c); }
.resource-info {
flex: 1;
}
.resource-info label {
display: block;
font-size: 14px;
color: #ffffff;
margin-bottom: 8px;
font-weight: 500;
}
.resource-input {
width: 100%;
padding: 10px 12px;
background: rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 6px;
color: #ffffff;
font-size: 16px;
transition: all 0.3s ease;
}
.resource-input:focus {
outline: none;
border-color: #ffd700;
box-shadow: 0 0 0 2px rgba(255, 215, 0, 0.2);
}
/* Vault Info Grid */
.vault-info-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.info-card {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
padding: 20px;
}
.info-card label {
display: block;
font-size: 14px;
color: #ffffff;
margin-bottom: 10px;
font-weight: 500;
}
.text-input, .number-input, .select-input {
width: 100%;
padding: 12px;
background: rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 6px;
color: #ffffff;
font-size: 16px;
transition: all 0.3s ease;
}
.text-input:focus, .number-input:focus, .select-input:focus {
outline: none;
border-color: #ffd700;
box-shadow: 0 0 0 2px rgba(255, 215, 0, 0.2);
}
/* Dwellers */
.dwellers-container {
height: calc(100vh - 200px);
overflow: auto;
}
.dwellers-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
}
.dweller-card {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
padding: 20px;
transition: all 0.3s ease;
}
.dweller-card:hover {
background: rgba(255, 255, 255, 0.08);
border-color: rgba(255, 255, 255, 0.2);
}
.dweller-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.dweller-name {
font-size: 16px;
color: #ffffff;
margin: 0;
}
.dweller-gender {
padding: 2px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: bold;
}
.dweller-gender.male {
background: rgba(0, 150, 255, 0.2);
color: #0096ff;
border: 1px solid #0096ff;
}
.dweller-gender.female {
background: rgba(255, 100, 150, 0.2);
color: #ff6496;
border: 1px solid #ff6496;
}
.pregnant-badge {
padding: 2px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: bold;
background: rgba(255, 215, 0, 0.2);
color: #ffd700;
border: 1px solid #ffd700;
}
.dweller-stats {
margin-bottom: 15px;
}
.stat-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.stat-row span {
font-size: 14px;
color: #b0b0b0;
min-width: 80px;
}
.dweller-input {
width: 80px;
padding: 4px 8px;
background: rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 4px;
color: #ffffff;
font-size: 14px;
}
.dweller-input:focus {
outline: none;
border-color: #ffd700;
}
.special-stats {
margin-bottom: 15px;
}
.special-stats h4 {
font-size: 14px;
color: #ffd700;
margin-bottom: 10px;
text-align: center;
}
.special-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 8px;
}
.special-stat {
text-align: center;
}
.special-stat label {
display: block;
font-size: 12px;
color: #ffd700;
font-weight: bold;
margin-bottom: 4px;
}
.special-input {
width: 100%;
padding: 4px;
background: rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 215, 0, 0.3);
border-radius: 4px;
color: #ffffff;
font-size: 12px;
text-align: center;
}
.special-input:focus {
outline: none;
border-color: #ffd700;
}
.dweller-actions {
display: flex;
gap: 8px;
justify-content: center;
}
.dweller-actions .secondary-btn {
padding: 6px 12px;
font-size: 12px;
}
.placeholder {
grid-column: 1 / -1;
text-align: center;
padding: 60px 20px;
color: #666;
}
.placeholder i {
font-size: 48px;
margin-bottom: 20px;
opacity: 0.5;
}
/* Raw Data */
.raw-data-container {
height: calc(100vh - 200px);
}
.raw-data-textarea {
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
color: #e0e0e0;
font-family: 'Courier New', monospace;
font-size: 12px;
padding: 15px;
resize: none;
line-height: 1.4;
}
.raw-data-textarea:focus {
outline: none;
border-color: #ffd700;
}
/* Buttons */
.primary-btn, .secondary-btn {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 12px 24px;
border: none;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
}
.primary-btn {
background: linear-gradient(135deg, #ffd700, #ffed4e);
color: #000000;
}
.primary-btn:hover {
background: linear-gradient(135deg, #ffed4e, #ffd700);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(255, 215, 0, 0.3);
}
.secondary-btn {
background: rgba(255, 255, 255, 0.1);
color: #ffffff;
border: 1px solid rgba(255, 255, 255, 0.2);
}
.secondary-btn:hover {
background: rgba(255, 255, 255, 0.15);
border-color: rgba(255, 255, 255, 0.3);
}
.action-bar {
display: flex;
justify-content: center;
gap: 15px;
padding-top: 20px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
/* Status Bar */
.status-bar {
background: rgba(0, 0, 0, 0.3);
border-top: 1px solid rgba(255, 255, 255, 0.1);
padding: 8px 20px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 12px;
color: #b0b0b0;
}
.status-right {
display: flex;
gap: 10px;
align-items: center;
}
.separator {
opacity: 0.5;
}
/* Loading Overlay */
.loading-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
display: none;
align-items: center;
justify-content: center;
z-index: 1000;
backdrop-filter: blur(5px);
}
.loading-overlay.show {
display: flex;
}
.loading-spinner {
text-align: center;
color: #ffffff;
}
.loading-spinner i {
font-size: 48px;
color: #ffd700;
margin-bottom: 20px;
}
.loading-spinner p {
font-size: 16px;
}
/* Toast Notifications */
.toast-container {
position: fixed;
top: 20px;
right: 20px;
z-index: 1001;
}
.toast {
background: rgba(0, 0, 0, 0.9);
color: #ffffff;
padding: 12px 20px;
border-radius: 8px;
margin-bottom: 10px;
border-left: 4px solid #ffd700;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
animation: slideIn 0.3s ease;
max-width: 300px;
}
.toast.error {
border-left-color: #ff4757;
}
.toast.success {
border-left-color: #2ed573;
}
.toast.warning {
border-left-color: #ffa502;
}
@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
/* Scrollbar Styling */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.1);
}
::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.3);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.5);
}
/* Startup Animation */
.startup-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(135deg, #0a0a0a 0%, #1a1a2e 50%, #16213e 100%);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
opacity: 1;
transition: opacity 0.8s ease-out;
}
.startup-overlay.fade-out {
opacity: 0;
pointer-events: none;
}
.startup-content {
text-align: center;
position: relative;
z-index: 2;
}
/* Vault Door Animation */
.vault-door {
margin-bottom: 40px;
position: relative;
}
.vault-door-outer {
width: 200px;
height: 200px;
border: 8px solid #ffd700;
border-radius: 50%;
margin: 0 auto;
position: relative;
animation: vaultGlow 2s ease-in-out infinite alternate;
box-shadow:
0 0 20px rgba(255, 215, 0, 0.5),
inset 0 0 20px rgba(255, 215, 0, 0.2);
}
.vault-door-inner {
width: 160px;
height: 160px;
background: radial-gradient(circle, #2a2a2a 0%, #1a1a1a 100%);
border: 4px solid #666;
border-radius: 50%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: flex;
align-items: center;
justify-content: center;
animation: vaultRotate 3s linear infinite;
}
.vault-number {
font-size: 48px;
font-weight: bold;
color: #ffd700;
text-shadow: 0 0 10px rgba(255, 215, 0, 0.8);
font-family: 'Courier New', monospace;
}
.vault-spokes {
position: absolute;
width: 100%;
height: 100%;
}
.spoke {
position: absolute;
width: 4px;
height: 60px;
background: linear-gradient(to bottom, #ffd700, #ffed4e);
left: 50%;
top: 10px;
transform-origin: 2px 70px;
border-radius: 2px;
box-shadow: 0 0 5px rgba(255, 215, 0, 0.5);
}
.spoke:nth-child(1) { transform: translateX(-50%) rotate(0deg); }
.spoke:nth-child(2) { transform: translateX(-50%) rotate(45deg); }
.spoke:nth-child(3) { transform: translateX(-50%) rotate(90deg); }
.spoke:nth-child(4) { transform: translateX(-50%) rotate(135deg); }
.spoke:nth-child(5) { transform: translateX(-50%) rotate(180deg); }
.spoke:nth-child(6) { transform: translateX(-50%) rotate(225deg); }
.spoke:nth-child(7) { transform: translateX(-50%) rotate(270deg); }
.spoke:nth-child(8) { transform: translateX(-50%) rotate(315deg); }
@keyframes vaultGlow {
0% {
box-shadow:
0 0 20px rgba(255, 215, 0, 0.5),
inset 0 0 20px rgba(255, 215, 0, 0.2);
}
100% {
box-shadow:
0 0 40px rgba(255, 215, 0, 0.8),
inset 0 0 30px rgba(255, 215, 0, 0.4);
}
}
@keyframes vaultRotate {
0% { transform: translate(-50%, -50%) rotate(0deg); }
100% { transform: translate(-50%, -50%) rotate(360deg); }
}
/* Startup Text */
.startup-text {
margin-bottom: 60px;
}
.startup-title {
font-size: 48px;
font-weight: bold;
color: #ffd700;
margin-bottom: 10px;
text-shadow:
0 0 10px rgba(255, 215, 0, 0.8),
0 0 20px rgba(255, 215, 0, 0.6),
0 0 30px rgba(255, 215, 0, 0.4);
animation: titlePulse 1.5s ease-in-out infinite alternate;
font-family: 'Courier New', monospace;
letter-spacing: 4px;
}
.startup-subtitle {
font-size: 24px;
color: #ffffff;
margin-bottom: 30px;
font-weight: 300;
letter-spacing: 2px;
opacity: 0;
animation: fadeInUp 1s ease-out 0.5s forwards;
}
@keyframes titlePulse {
0% {
text-shadow:
0 0 10px rgba(255, 215, 0, 0.8),
0 0 20px rgba(255, 215, 0, 0.6),
0 0 30px rgba(255, 215, 0, 0.4);
}
100% {
text-shadow:
0 0 20px rgba(255, 215, 0, 1),
0 0 30px rgba(255, 215, 0, 0.8),
0 0 40px rgba(255, 215, 0, 0.6);
}
}
@keyframes fadeInUp {
0% {
opacity: 0;
transform: translateY(20px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
/* Loading Dots */
.loading-dots {
display: flex;
justify-content: center;
gap: 8px;
margin-bottom: 20px;
}
.loading-dots span {
width: 8px;
height: 8px;
background: #ffd700;
border-radius: 50%;
animation: dotPulse 1.5s ease-in-out infinite;
box-shadow: 0 0 10px rgba(255, 215, 0, 0.5);
}
.loading-dots span:nth-child(1) { animation-delay: 0s; }
.loading-dots span:nth-child(2) { animation-delay: 0.2s; }
.loading-dots span:nth-child(3) { animation-delay: 0.4s; }
@keyframes dotPulse {
0%, 80%, 100% {
transform: scale(0.8);
opacity: 0.5;
}
40% {
transform: scale(1.2);
opacity: 1;
}
}
.version-info {
display: flex;
justify-content: center;
gap: 20px;
margin-bottom: 20px;
opacity: 0;
animation: fadeInUp 1s ease-out 0.8s forwards;
}
.version-badge, .vault-tec-badge {
padding: 4px 12px;
border-radius: 12px;
font-size: 12px;
font-family: 'Courier New', monospace;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 1px;
}
.version-badge {
background: rgba(255, 215, 0, 0.2);
border: 1px solid #ffd700;
color: #ffd700;
}
.vault-tec-badge {
background: rgba(0, 255, 0, 0.1);
border: 1px solid #00ff00;
color: #00ff00;
animation: badgePulse 2s ease-in-out infinite alternate;
}
@keyframes badgePulse {
0% {
box-shadow: 0 0 5px rgba(0, 255, 0, 0.3);
}
100% {
box-shadow: 0 0 15px rgba(0, 255, 0, 0.6);
}
}
.startup-status {
font-size: 16px;
color: #b0b0b0;
font-family: 'Courier New', monospace;
opacity: 0;
animation: fadeInUp 1s ease-out 1s forwards;
min-height: 20px;
transition: all 0.3s ease;
}
/* Progress Bar */
.startup-progress {
width: 400px;
margin: 0 auto;
opacity: 0;
animation: fadeInUp 1s ease-out 1.5s forwards;
}
.progress-bar {
width: 100%;
height: 8px;
background: rgba(255, 255, 255, 0.1);
border-radius: 4px;
overflow: hidden;
margin-bottom: 10px;
border: 1px solid rgba(255, 215, 0, 0.3);
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #ffd700, #ffed4e, #ffd700);
background-size: 200% 100%;
border-radius: 4px;
width: 0%;
transition: width 0.3s ease;
animation: progressShimmer 1.5s linear infinite;
box-shadow: 0 0 10px rgba(255, 215, 0, 0.5);
}
@keyframes progressShimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}
.progress-text {
display: flex;
justify-content: space-between;
font-size: 12px;
color: #888;
font-family: 'Courier New', monospace;
text-transform: uppercase;
letter-spacing: 1px;
}
/* Radiation Particles */
.radiation-particles {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 1;
}
.particle {
position: absolute;
width: 4px;
height: 4px;
background: #ffd700;
border-radius: 50%;
opacity: 0;
animation: particleFloat 8s linear infinite;
box-shadow: 0 0 6px rgba(255, 215, 0, 0.8);
}
.particle:nth-child(1) {
left: 10%;
animation-delay: 0s;
animation-duration: 6s;
}
.particle:nth-child(2) {
left: 20%;
animation-delay: 1s;
animation-duration: 8s;
}
.particle:nth-child(3) {
left: 30%;
animation-delay: 2s;
animation-duration: 7s;
}
.particle:nth-child(4) {
left: 40%;
animation-delay: 0.5s;
animation-duration: 9s;
}
.particle:nth-child(5) {
left: 60%;
animation-delay: 1.5s;
animation-duration: 6.5s;
}
.particle:nth-child(6) {
left: 70%;
animation-delay: 2.5s;
animation-duration: 8.5s;
}
.particle:nth-child(7) {
left: 80%;
animation-delay: 3s;
animation-duration: 7.5s;
}
.particle:nth-child(8) {
left: 90%;
animation-delay: 3.5s;
animation-duration: 9.5s;
}
@keyframes particleFloat {
0% {
transform: translateY(100vh) translateX(0px);
opacity: 0;
}
10% {
opacity: 1;
}
90% {
opacity: 1;
}
100% {
transform: translateY(-100px) translateX(50px);
opacity: 0;
}
}
/* Responsive Design */
@media (max-width: 768px) {
.sidebar {
width: 200px;
}
.resource-grid {
grid-template-columns: 1fr;
}
.feature-grid {
grid-template-columns: 1fr;
}
.tab-header {
flex-direction: column;
gap: 15px;
align-items: flex-start;
}
.startup-title {
font-size: 36px;
}
.startup-subtitle {
font-size: 18px;
}
.vault-door-outer {
width: 150px;
height: 150px;
}
.vault-door-inner {
width: 120px;
height: 120px;
}
.vault-number {
font-size: 36px;
}
.startup-progress {
width: 300px;
}
}

65
test-electron.js Normal file
View File

@@ -0,0 +1,65 @@
// Simple test to verify the Electron app functionality
const fs = require('fs');
const CryptoJS = require('crypto-js');
// Test the decryption with the actual save file
function testDecryption() {
try {
console.log('Testing Fallout Shelter save decryption...');
// Read the save file
const saveData = fs.readFileSync('Vault1.sav', 'utf8');
console.log('Save file loaded, length:', saveData.length);
// AES constants (same as in renderer.js)
const AES_KEY_WORDS = [2815074099, 1725469378, 4039046167, 874293617, 3063605751, 3133984764, 4097598161, 3620741625];
const AES_KEY = CryptoJS.lib.WordArray.create(AES_KEY_WORDS);
const AES_IV = CryptoJS.enc.Hex.parse('7475383967656A693334307438397532');
console.log('Key:', AES_KEY.toString());
console.log('IV:', AES_IV.toString());
// Create cipher text object from base64 string (same as renderer.js)
const ciphertext = CryptoJS.enc.Base64.parse(saveData);
const cipherParams = CryptoJS.lib.CipherParams.create({
ciphertext: ciphertext
});
// Decrypt
const decrypted = CryptoJS.AES.decrypt(cipherParams, AES_KEY, {
iv: AES_IV,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
const decryptedString = decrypted.toString(CryptoJS.enc.Utf8);
console.log('Decryption successful!');
console.log('Decrypted length:', decryptedString.length);
console.log('First 200 chars:', decryptedString.substring(0, 200));
// Try to parse JSON
const jsonData = JSON.parse(decryptedString);
console.log('JSON parsing successful!');
console.log('Top-level keys:', Object.keys(jsonData));
// Test encryption back
const encrypted = CryptoJS.AES.encrypt(decryptedString, AES_KEY, {
iv: AES_IV,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
console.log('Re-encryption successful!');
console.log('Original length:', saveData.length);
console.log('Re-encrypted length:', encrypted.toString().length);
return true;
} catch (error) {
console.error('Test failed:', error);
return false;
}
}
if (require.main === module) {
testDecryption();
}

98
test_decryption.py Normal file
View File

@@ -0,0 +1,98 @@
#!/usr/bin/env python3
"""
Test script to verify Fallout Shelter save decryption
"""
import base64
import json
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
def test_decryption():
"""Test the decryption with the Vault1.sav file."""
# Fallout Shelter encryption constants
AES_KEY = bytes([
(2815074099 >> 24) & 0xFF, (2815074099 >> 16) & 0xFF, (2815074099 >> 8) & 0xFF, 2815074099 & 0xFF,
(1725469378 >> 24) & 0xFF, (1725469378 >> 16) & 0xFF, (1725469378 >> 8) & 0xFF, 1725469378 & 0xFF,
(4039046167 >> 24) & 0xFF, (4039046167 >> 16) & 0xFF, (4039046167 >> 8) & 0xFF, 4039046167 & 0xFF,
(874293617 >> 24) & 0xFF, (874293617 >> 16) & 0xFF, (874293617 >> 8) & 0xFF, 874293617 & 0xFF,
(3063605751 >> 24) & 0xFF, (3063605751 >> 16) & 0xFF, (3063605751 >> 8) & 0xFF, 3063605751 & 0xFF,
(3133984764 >> 24) & 0xFF, (3133984764 >> 16) & 0xFF, (3133984764 >> 8) & 0xFF, 3133984764 & 0xFF,
(4097598161 >> 24) & 0xFF, (4097598161 >> 16) & 0xFF, (4097598161 >> 8) & 0xFF, 4097598161 & 0xFF,
(3620741625 >> 24) & 0xFF, (3620741625 >> 16) & 0xFF, (3620741625 >> 8) & 0xFF, 3620741625 & 0xFF
])
AES_IV = bytes.fromhex("7475383967656A693334307438397532")
try:
# Read the save file
with open("Vault1.sav", "rb") as f:
raw_data = f.read()
print(f"Raw file size: {len(raw_data)} bytes")
print(f"First 50 bytes (hex): {raw_data[:50].hex()}")
# Decode base64
try:
decoded_data = base64.b64decode(raw_data)
print(f"Decoded size: {len(decoded_data)} bytes")
print(f"First 50 bytes of decoded (hex): {decoded_data[:50].hex()}")
except Exception as e:
print(f"Base64 decode failed: {e}")
return
# Decrypt using AES-CBC
try:
cipher = AES.new(AES_KEY, AES.MODE_CBC, AES_IV)
decrypted_data = cipher.decrypt(decoded_data)
print(f"Decrypted size: {len(decrypted_data)} bytes")
# Try to remove padding
try:
unpadded_data = unpad(decrypted_data, AES.block_size)
print(f"Unpadded size: {len(unpadded_data)} bytes")
except ValueError as e:
print(f"Unpadding failed: {e}")
# Try without unpadding
unpadded_data = decrypted_data.rstrip(b'\x00')
print(f"Manual padding removal size: {len(unpadded_data)} bytes")
# Try to decode as UTF-8
try:
json_str = unpadded_data.decode('utf-8')
print(f"UTF-8 decode successful, length: {len(json_str)}")
print(f"First 200 characters: {json_str[:200]}")
# Try to parse as JSON
try:
save_data = json.loads(json_str)
print("JSON parsing successful!")
print(f"Top-level keys: {list(save_data.keys()) if isinstance(save_data, dict) else 'Not a dict'}")
# Save pretty-printed JSON for inspection
with open("decrypted_save.json", "w") as f:
json.dump(save_data, f, indent=2)
print("Decrypted save written to decrypted_save.json")
except json.JSONDecodeError as e:
print(f"JSON parsing failed: {e}")
print("Saving raw decrypted text for inspection...")
with open("decrypted_raw.txt", "w", encoding='utf-8', errors='ignore') as f:
f.write(json_str)
except UnicodeDecodeError as e:
print(f"UTF-8 decode failed: {e}")
print("Saving raw decrypted bytes for inspection...")
with open("decrypted_raw.bin", "wb") as f:
f.write(unpadded_data)
except Exception as e:
print(f"AES decryption failed: {e}")
except FileNotFoundError:
print("Vault1.sav not found in current directory")
except Exception as e:
print(f"Unexpected error: {e}")
if __name__ == "__main__":
test_decryption()

91
test_save_loading.py Normal file
View File

@@ -0,0 +1,91 @@
#!/usr/bin/env python3
"""
Test script to verify save file loading functionality
"""
import base64
import json
import zlib
import os
def test_save_loading(filepath="Vault1.sav"):
"""Test loading the save file with different methods."""
print(f"Testing save file: {filepath}")
print("=" * 50)
if not os.path.exists(filepath):
print(f"Error: File {filepath} not found")
return False
try:
# Read raw file
with open(filepath, 'rb') as f:
raw_data = f.read()
print(f"Raw file size: {len(raw_data)} bytes")
# Decode base64
try:
decoded_data = base64.b64decode(raw_data)
print(f"Base64 decoded size: {len(decoded_data)} bytes")
print(f"First 20 bytes: {decoded_data[:20].hex()}")
except Exception as e:
print(f"Base64 decode failed: {e}")
return False
# Try decompression methods
methods = [
("Raw JSON", lambda d: json.loads(d.decode('utf-8'))),
("Zlib", lambda d: json.loads(zlib.decompress(d).decode('utf-8'))),
("Zlib with header", lambda d: json.loads(zlib.decompress(d[4:]).decode('utf-8'))),
]
for name, method in methods:
try:
result = method(decoded_data)
print(f"\n✓ SUCCESS with {name}")
print(f"Data type: {type(result)}")
if isinstance(result, dict):
print(f"Keys: {list(result.keys())[:10]}") # First 10 keys
# Look for common Fallout Shelter fields
common_fields = ['vault', 'dwellers', 'resources', 'rooms', 'gameStats']
found_fields = [field for field in common_fields if field in result]
if found_fields:
print(f"Found common fields: {found_fields}")
return True
except Exception as e:
print(f"✗ Failed with {name}: {str(e)[:100]}")
print("\nAll decompression methods failed")
return False
except Exception as e:
print(f"Error reading file: {e}")
return False
def main():
"""Main test function."""
print("Fallout Shelter Save File Test")
print("=" * 40)
# Test with default save file
success = test_save_loading("Vault1.sav")
if success:
print("\n🎉 Save file can be loaded successfully!")
print("The PyQt editor should be able to handle this file.")
else:
print("\n❌ Could not load save file with current methods.")
print("The save file might use a different encryption/compression method.")
# Test if backup exists
if os.path.exists("Vault1.sav.bkp"):
print("\nTesting backup file...")
test_save_loading("Vault1.sav.bkp")
if __name__ == "__main__":
main()