1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
//! Core CLI definitions
use clap::{Parser, Subcommand};
use std::path::PathBuf;
use super::idb::ItemsDbCommand;
use super::memory::MemoryAction;
use super::ncs::NcsCommand;
#[cfg(feature = "research")]
use super::research::{ExtractCommand, UsmapCommand};
use super::save::SaveCommand;
use super::serial::SerialCommand;
#[derive(Parser)]
#[command(name = "bl4")]
#[command(about = "Borderlands 4 Save Editor", long_about = None)]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
}
#[derive(Subcommand)]
pub enum Commands {
/// Save file operations (decrypt, encrypt, edit, get, set)
#[command(visible_alias = "s")]
Save {
#[command(subcommand)]
command: SaveCommand,
},
/// Inspect a save file (decrypt and display info)
#[command(visible_alias = "i")]
Inspect {
/// Path to .sav file
input: PathBuf,
/// Steam ID for decryption (uses configured default if not provided)
#[arg(short, long)]
steam_id: Option<String>,
/// Show full YAML output
#[arg(short, long)]
full: bool,
},
/// Configure default settings
#[command(visible_alias = "c")]
Configure {
/// Set default Steam ID
#[arg(long)]
steam_id: Option<String>,
/// Show current configuration
#[arg(long)]
show: bool,
},
/// Item serial operations (decode, encode, compare, modify)
#[command(visible_alias = "r")]
Serial {
#[command(subcommand)]
command: SerialCommand,
},
/// Query parts database - find parts for a weapon type
#[command(visible_alias = "p")]
Parts {
/// Weapon name (e.g. "Jakobs Pistol", "Vladof SMG")
#[arg(short, long)]
weapon: Option<String>,
/// Category ID (e.g. 3 for Jakobs Pistol)
#[arg(short, long)]
category: Option<i64>,
/// List all categories
#[arg(short, long)]
list: bool,
/// Path to parts database
#[arg(long, default_value = "share/manifest/parts_database.json")]
parts_db: PathBuf,
},
/// Read/analyze game memory (live process or dump file)
#[command(visible_alias = "m")]
Memory {
/// Use preload-based injection (requires game launched with LD_PRELOAD)
#[arg(long)]
preload: bool,
/// Read from memory dump file instead of live process (for offline analysis)
#[arg(long, short = 'd')]
dump: Option<PathBuf>,
/// Path to maps file for dump (optional, defaults to <dump>.maps)
#[arg(long)]
maps: Option<PathBuf>,
#[command(subcommand)]
action: MemoryAction,
},
/// Launch Borderlands 4 with instrumentation
#[command(visible_alias = "l")]
Launch {
/// Skip confirmation prompt
#[arg(short = 'y', long)]
yes: bool,
},
/// Usmap file utilities (requires 'research' feature)
#[cfg(feature = "research")]
Usmap {
#[command(subcommand)]
command: UsmapCommand,
},
/// Data extraction utilities (requires 'research' feature)
#[cfg(feature = "research")]
#[command(visible_alias = "e")]
Extract {
#[command(subcommand)]
command: ExtractCommand,
},
/// Manage the verified items database
Idb {
/// Path to database file (can also set BL4_ITEMS_DB env var)
#[arg(short, long, env = "BL4_ITEMS_DB", default_value = bl4_idb::DEFAULT_DB_PATH)]
db: PathBuf,
#[command(subcommand)]
command: ItemsDbCommand,
},
/// NCS (Nexus Config Store) file operations
#[command(visible_alias = "n")]
Ncs {
#[command(subcommand)]
command: NcsCommand,
},
/// Generate manifest files from game data (requires 'research' feature)
#[cfg(feature = "research")]
Manifest {
/// Path to game's Paks directory containing .utoc/.ucas files
paks: PathBuf,
/// Path to memory dump file for usmap and parts extraction
#[arg(short, long)]
dump: Option<PathBuf>,
/// Path to .usmap file (generated from dump if not provided)
#[arg(short = 'm', long)]
usmap: Option<PathBuf>,
/// Output directory for manifest files
#[arg(short, long, default_value = "share/manifest")]
output: PathBuf,
/// AES encryption key if paks are encrypted (base64 or hex)
#[arg(long)]
aes_key: Option<String>,
/// Skip uextract step (use existing extracted files)
#[arg(long)]
skip_extract: bool,
/// Path to existing extracted files (with --skip-extract)
#[arg(long, default_value = "/tmp/bl4_extract")]
extracted: PathBuf,
/// Skip memory dump extraction (usmap, parts)
#[arg(long)]
skip_memory: bool,
},
}