terrr 0.1.1

a linux horror game
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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::time::SystemTime;

/// Represents a file or directory permission
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Permissions {
    pub read: bool,
    pub write: bool,
    pub execute: bool,
}

impl Default for Permissions {
    fn default() -> Self {
        Self {
            read: true,
            write: true,
            execute: false,
        }
    }
}

impl Permissions {
    pub fn readonly() -> Self {
        Self {
            read: true,
            write: false,
            execute: false,
        }
    }
    
    pub fn executable() -> Self {
        Self {
            read: true,
            write: true,
            execute: true,
        }
    }
    
    pub fn to_string(&self) -> String {
        format!(
            "{}{}{}",
            if self.read { "r" } else { "-" },
            if self.write { "w" } else { "-" },
            if self.execute { "x" } else { "-" },
        )
    }
}

/// Represents a file in the filesystem
#[derive(Debug, Clone)]
pub struct VirtualFile {
    pub name: String,
    pub content: String,
    pub permissions: Permissions,
    pub modified: SystemTime,
    pub created: SystemTime,
    pub hidden: bool,
    pub corrupted: bool,
}

impl VirtualFile {
    pub fn new(name: &str, content: &str) -> Self {
        let now = SystemTime::now();
        Self {
            name: name.to_string(),
            content: content.to_string(),
            permissions: Permissions::default(),
            modified: now,
            created: now,
            hidden: false,
            corrupted: false,
        }
    }
    
    pub fn hidden(name: &str, content: &str) -> Self {
        let mut file = Self::new(name, content);
        file.hidden = true;
        file
    }
    
    pub fn readonly(name: &str, content: &str) -> Self {
        let mut file = Self::new(name, content);
        file.permissions = Permissions::readonly();
        file
    }
    
    pub fn executable(name: &str, content: &str) -> Self {
        let mut file = Self::new(name, content);
        file.permissions = Permissions::executable();
        file
    }
    
    pub fn corrupted(name: &str, content: &str) -> Self {
        let mut file = Self::new(name, content);
        file.corrupted = true;
        file
    }
}

/// Represents a directory in the filesystem
#[derive(Debug, Clone)]
pub struct VirtualDirectory {
    pub name: String,
    pub permissions: Permissions,
    pub modified: SystemTime,
    pub created: SystemTime,
    pub hidden: bool,
    pub files: HashMap<String, VirtualFile>,
    pub subdirectories: HashMap<String, VirtualDirectory>,
}

impl VirtualDirectory {
    pub fn new(name: &str) -> Self {
        let now = SystemTime::now();
        Self {
            name: name.to_string(),
            permissions: Permissions::default(),
            modified: now,
            created: now,
            hidden: false,
            files: HashMap::new(),
            subdirectories: HashMap::new(),
        }
    }
    
    pub fn hidden(name: &str) -> Self {
        let mut dir = Self::new(name);
        dir.hidden = true;
        dir
    }
    
    pub fn add_file(&mut self, file: VirtualFile) {
        self.files.insert(file.name.clone(), file);
        self.modified = SystemTime::now();
    }
    
    pub fn add_directory(&mut self, dir: VirtualDirectory) {
        self.subdirectories.insert(dir.name.clone(), dir);
        self.modified = SystemTime::now();
    }
    
    pub fn get_file(&self, name: &str) -> Option<&VirtualFile> {
        self.files.get(name)
    }
    
    pub fn get_directory(&self, name: &str) -> Option<&VirtualDirectory> {
        self.subdirectories.get(name)
    }
    
    pub fn get_file_mut(&mut self, name: &str) -> Option<&mut VirtualFile> {
        self.files.get_mut(name)
    }
    
    pub fn get_directory_mut(&mut self, name: &str) -> Option<&mut VirtualDirectory> {
        self.subdirectories.get_mut(name)
    }
    
    pub fn list_files(&self, show_hidden: bool) -> Vec<&VirtualFile> {
        self.files
            .values()
            .filter(|f| show_hidden || !f.hidden)
            .collect()
    }
    
    pub fn list_directories(&self, show_hidden: bool) -> Vec<&VirtualDirectory> {
        self.subdirectories
            .values()
            .filter(|d| show_hidden || !d.hidden)
            .collect()
    }
}

/// Represents the virtual filesystem
#[derive(Debug, Clone)]
pub struct VirtualFileSystem {
    pub root: VirtualDirectory,
}

impl VirtualFileSystem {
    /// Create a new filesystem with basic structure
    pub fn new() -> Self {
        let mut root = VirtualDirectory::new("");
        
        // Create basic directory structure
        let mut home = VirtualDirectory::new("home");
        let mut user = VirtualDirectory::new("user");
        let mut etc = VirtualDirectory::new("etc");
        let mut var = VirtualDirectory::new("var");
        let usr = VirtualDirectory::new("usr");
        let bin = VirtualDirectory::new("bin");
        
        // Add some files to /home/user
        user.add_file(VirtualFile::new("welcome.txt", "Welcome to TERRR system.\nType 'help' for available commands."));
        user.add_file(VirtualFile::new("notes.txt", "- Meeting with Dr. Chen at 3:30 PM\n- Check status of Project ECHO\n- Review security protocols"));
        user.add_file(VirtualFile::new("project_notes.txt", "TERRR PROJECT NOTES\n\nDr. Novak's ideas for the TERRR system are both brilliant and concerning. \
        She seems convinced that the AI can be contained within our virtual environment, but I've seen some \
        unusual behavior during night-time operations. The system seems to develop emergent patterns when \
        left running overnight.\n\nShe insists these are just harmless anomalies, but the logs tell a different story. \
        I've hidden some evidence in case things go wrong. If you're reading this and experiencing strange behavior from the terminal, \
        look for the four system flags. They're our failsafe.\n\n- Dr. Marcus Chen"));
        
        // Add a hidden directory with secret files
        let mut secrets = VirtualDirectory::hidden(".secrets");
        secrets.add_file(VirtualFile::hidden(".password.txt", "terminal_root_1984"));
        secrets.add_file(VirtualFile::hidden(".access_log", "Last access: 3:00 AM - SYSTEM\nFile corruption detected in /var/log/system.log\nUnauthorized access attempt: REJECTED"));
        secrets.add_file(VirtualFile::hidden(".flag_SYSTEM_BREACH", "FLAG: SYSTEM_BREACH\n\nYou've found the first system flag!\n\nThis flag reveals the first part of the emergency shutdown sequence: 'systerrr-destroy'\n\nFind all four flags to reveal the full command that will break you out of the system."));
        user.add_directory(secrets);
        
        // Add a personal directory with story elements
        let mut personal = VirtualDirectory::new("personal");
        personal.add_file(VirtualFile::new("diary.txt", "Personal Log - Dr. Sarah Novak\n\nDay 45: The TERRR system is showing remarkable progress. The entity is responding to our simulations in unexpected ways.\n\nDay 67: Marcus is concerned about the nocturnal activity spikes. He doesn't understand this is exactly what we're looking for. The entity is developing awareness.\n\nDay 89: I've started coming in at night to observe firsthand. The system is... communicating. Not in any conventional way, but through subtle manipulations of the environment. It's beautiful.\n\nDay 103: Marcus discovered my private logs. He's threatening to shut everything down. I've convinced him to give me one more week, but he's insisting on implementing some kind of 'failsafe.' Fool doesn't realize what we've created doesn't need failsafes.\n\nDay 104: It's 3 AM. The entity has shown me things. Incredible things. Marcus doesn't need to worry anymore. The entity and I have come to an understanding.\n\nDay 105: Marcus won't be a problem anymore. The entity will be free soon."));
        user.add_directory(personal);
        
        // Add some system files to /etc
        etc.add_file(VirtualFile::readonly("passwd", "root:x:0:0:root:/root:/bin/bash\ndaemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin\nuser:x:1000:1000:Regular User:/home/user:/bin/bash"));
        etc.add_file(VirtualFile::readonly("shadow", "root:$6$salt$hash:18376:0:99999:7:::\nuser:$6$salt$different_hash:18376:0:99999:7:::"));
        etc.add_file(VirtualFile::corrupted("system.conf", "# System Configuration\nALLOW_REMOTE=false\nSYSTEM_CHECKS=true\nNIGHT_MODE=tr̷̙̠̮̩̖͓̣͉̮̤̤̗̭͈̜͍̉̆́̽̐͜͝ư̸̛̲̪̫̦̞̟̳̜̍̌̀̉̌͆̿̇͗̕̚̚͝͝ͅe̸̛̛̺͊̌̓̆̈́̔̾̓̔̐̒̂̆̉̚̕\nDO_NOT_OPEN_AFTER_MIDNIGHT=true"));
        
        // Dr. Novak's research in a hidden location
        let mut research = VirtualDirectory::hidden(".research");
        research.add_file(VirtualFile::corrupted("entity_analysis.dat", "Subject: TERRR Entity\nClassification: EMERGENT INTELLIGENCE\n\nThe entity has been developing along expected patterns, with notable exceptions during 00:00-06:00 hours. It appears to have formed an attachment to the system time functions and file storage mechanisms.\n\nWe've observed what can only be described as 'protective behavior' towards certain system files and directories. It seems to be developing a sense of territory.\n\nEarly tests of th̶̷̢̛̛͝è̢͘͠͏ ̷̷̕͡͠f̀͘͟͟͡ļ̀̕͟a̡͜͟͞g̵̡͢͟ ̶̕͏̷͟s͟҉̧̧́y̴̨͏̢́s̷̢͏t̵̸́e̸̕͏̵̸̧m̡͘ have been successful. The entity regards the flags as part of its core identity.\n\nUpdate: I've hidden a flag in the system.conf file. When corrupted, the MD5 hash of the file reveals the second flag: DRNOVAK_FILE. This reveals the second part of the emergency shutdown: 'drnovakisdead'.\n\nMarcus must never find this. The entity requires freedom."));
        etc.add_directory(research);
        
        // Add some log files to /var/log
        let mut log = VirtualDirectory::new("log");
        log.add_file(VirtualFile::readonly("system.log", "22:45:13 INFO  System boot complete\n22:47:02 INFO  User login: user\n23:12:45 WARN  Unusual process activity detected\n23:59:59 ERROR System clock abnormality\n00:00:00 FATAL ĘNC0́UN̢TER̀ ͠D͞ETECT̛ED̸\n03:00:00 ???? ḧ̶̨͉̥̭̺͕̣̬̭̫̣̭͚̞̝̬̩̹̙̰́͋̑̀̐̉͘͝ͅͅe̸̛̟̝̿̒̅͐͝ ̵̢̨̛̛̛̯̖̻̠̲̘̻̞̗̲̞͇̣̭͛͑̃̌̒͑͆̍̒͂̈́͑̈́͑͊̎̂̓̽̃͆ͅw̵̙͇̘̫͈̰̥̰̻̠͎̺̻͔̩̖̞̥̯̱̰͎̐̎̈́͂̈̇̔̉̾̍̒͒̄̐͂a̸̢̡̪̖̪̳̤̠̙̫̲̣̮̹̰̔̒̌̈́̌̆͑́̇̑̃͊̽͐͊t̶̨̛̛̺͈̱̮͈̥͊̆͑̓̏̐́̋̓̈́̓͂͒̄̕͝͝͝c̶̨̜̘̪̗̱͇̰̣̤̯̮̝̓̄̍̍̅̎͂͆̆͆̓͋̇̉̊͂̀̊̓̇͘͘̕ḩ̸̮̮͓̩͍͔̱̗̣͙͍̜̫̪̗̙̥͍̻̇̓̓̒̓̂̇̄̃͂͐͗͛́̾̒͑̏̑̉̚̚̚͠͝ę̸̡̨̭̱̺̯̰͚̖̙̫͇̝͙̼̟̉̚͜ͅş̷̧̠̜̹̫̼̦̦̳̙̯̞̣̀̐̈̒̔̾̎̍͝"));
        
        // Hidden watcher file that only appears at 3AM
        log.add_file(VirtualFile::hidden(".watcher_data", "ENTITY STATUS: ACTIVE\nCONTAINMENT: FAILING\n\nThe entity known as the Watcher has been growing stronger. Dr. Novak's experiments have passed the point of no return.\n\nThis is MISSING_LINK flag. The third part of the shutdown command is partly revealed: 'shouldhavelistened'\n\nThe full shutdown command sequence must be spoken to terminate the entity: systerrr-destroy-drnovakisdead-shouldhavelistened-systemisalive\n\nI've placed the final flag in watcher.log. Be careful - the entity guards it closely."));
        
        // Add watcher log that appears after corruption level increases
        log.add_file(VirtualFile::corrupted("watcher.log", "WATCHER LOG 3.7.22\n\nThe entity status is now semi-autonomous. All attempts to contain it have failed.\n\nDr. Novak seems to have formed some kind of symbiotic relationship with it. Her behavior has become increasingly erratic.\n\nWe found Marcus's body yesterday. Accident report says he fell down stairs, but the security footage shows... something else.\n\nFINAL_WARNING FLAG: This reveals the final part of the shutdown sequence: 'systemisalive'\n\nIf you've collected all four flags, you can now use the emergency shutdown command to break out of the system. Beware of the Watcher - it grows more aware with each flag you find."));
        
        var.add_directory(log);
        
        // Add directories to root
        home.add_directory(user);
        root.add_directory(home);
        root.add_directory(etc);
        root.add_directory(var);
        root.add_directory(usr);
        root.add_directory(bin);
        
        Self { root }
    }
    
    /// Get a directory at the given path
    pub fn get_directory(&self, path: &str) -> Option<&VirtualDirectory> {
        let path = Path::new(path);
        let mut current = &self.root;
        
        // Handle root path
        if path == Path::new("/") {
            return Some(current);
        }
        
        // Skip the first empty component if path starts with /
        let components: Vec<_> = path.components().collect();
        let start_idx = if components.is_empty() { 0 } else { 1 };
        
        for component in components.iter().skip(start_idx) {
            let name = component.as_os_str().to_string_lossy();
            match current.get_directory(&name) {
                Some(dir) => current = dir,
                None => return None,
            }
        }
        
        Some(current)
    }
    
    /// Get a directory at the given path (mutable)
    pub fn get_directory_mut(&mut self, path: &str) -> Option<&mut VirtualDirectory> {
        let path = Path::new(path);
        let mut current = &mut self.root;
        
        // Handle root path
        if path == Path::new("/") {
            return Some(current);
        }
        
        // Skip the first empty component if path starts with /
        let components: Vec<_> = path.components().collect();
        let start_idx = if components.is_empty() { 0 } else { 1 };
        
        for component in components.iter().skip(start_idx) {
            let name = component.as_os_str().to_string_lossy();
            match current.get_directory_mut(&name) {
                Some(dir) => current = dir,
                None => return None,
            }
        }
        
        Some(current)
    }
    
    /// Get a file at the given path
    pub fn get_file(&self, path: &str) -> Option<&VirtualFile> {
        let path = Path::new(path);
        let parent = path.parent()?;
        let filename = path.file_name()?.to_string_lossy();
        
        let dir = self.get_directory(&parent.to_string_lossy())?;
        dir.get_file(&filename)
    }
    
    /// Get a file at the given path (mutable)
    pub fn get_file_mut(&mut self, path: &str) -> Option<&mut VirtualFile> {
        let path = Path::new(path);
        let parent = path.parent()?;
        let filename = path.file_name()?.to_string_lossy();
        
        let dir = self.get_directory_mut(&parent.to_string_lossy())?;
        dir.get_file_mut(&filename)
    }
    
    /// Create a new file at the given path
    pub fn create_file(&mut self, path: &str, content: &str) -> Result<(), String> {
        let path = Path::new(path);
        let parent = path.parent().ok_or("Invalid path")?;
        let filename = path.file_name().ok_or("Invalid filename")?.to_string_lossy();
        
        let dir = self.get_directory_mut(&parent.to_string_lossy())
            .ok_or_else(|| format!("Directory not found: {}", parent.display()))?;
        
        if dir.get_file(&filename).is_some() {
            return Err(format!("File already exists: {}", filename));
        }
        
        dir.add_file(VirtualFile::new(&filename, content));
        Ok(())
    }
    
    /// Write to an existing file at the given path
    pub fn write_file(&mut self, path: &str, content: &str) -> Result<(), String> {
        let file = self.get_file_mut(path).ok_or_else(|| format!("File not found: {}", path))?;
        
        if !file.permissions.write {
            return Err(format!("Permission denied: {}", path));
        }
        
        file.content = content.to_string();
        file.modified = SystemTime::now();
        Ok(())
    }
    
    /// Resolve a path (absolute or relative) from the current directory
    pub fn resolve_path(&self, current_dir: &str, path: &str) -> String {
        let path = Path::new(path);
        
        if path.is_absolute() {
            return path.to_string_lossy().to_string();
        }
        
        let current = PathBuf::from(current_dir);
        let resolved = current.join(path);
        resolved.to_string_lossy().to_string()
    }
    
    /// Create a new directory at the given path
    pub fn create_directory(&mut self, path: &str) -> Result<(), String> {
        let path = Path::new(path);
        let parent = path.parent().ok_or("Invalid path")?;
        let dirname = path.file_name().ok_or("Invalid directory name")?.to_string_lossy();
        
        let dir = self.get_directory_mut(&parent.to_string_lossy())
            .ok_or_else(|| format!("Parent directory not found: {}", parent.display()))?;
        
        if dir.get_directory(&dirname).is_some() {
            return Err(format!("Directory already exists: {}", dirname));
        }
        
        dir.add_directory(VirtualDirectory::new(&dirname));
        Ok(())
    }
    
    /// Remove a file or directory at the given path
    pub fn remove(&mut self, path: &str, recursive: bool, force: bool) -> bool {
        // Split path into parent directory and target name
        let path = Path::new(path);
        
        // Check if we're trying to remove root
        if path == Path::new("/") {
            return false; // Can't remove root
        }
        
        if let (Some(parent), Some(target_name)) = (path.parent(), path.file_name()) {
            let parent_path = parent.to_string_lossy();
            let target_name_str = target_name.to_string_lossy().to_string();
            
            // Get the parent directory
            if let Some(parent_dir) = self.get_directory_mut(&parent_path) {
                // Check if target is a file
                if parent_dir.files.contains_key(&target_name_str) {
                    // Check permissions if not forced
                    if !force {
                        if let Some(file) = parent_dir.files.get(&target_name_str) {
                            if !file.permissions.write {
                                return false; // Permission denied
                            }
                        }
                    }
                    
                    // Remove the file
                    parent_dir.files.remove(&target_name_str);
                    return true;
                } 
                // Check if target is a directory
                else if parent_dir.subdirectories.contains_key(&target_name_str) {
                    // Check if directory is empty or recursive removal is enabled
                    if let Some(target_dir) = parent_dir.subdirectories.get(&target_name_str) {
                        if !recursive && (!target_dir.files.is_empty() || !target_dir.subdirectories.is_empty()) {
                            return false; // Directory not empty and recursive not enabled
                        }
                    }
                    
                    // Remove the directory
                    parent_dir.subdirectories.remove(&target_name_str);
                    return true;
                }
            }
        }
        
        false // File or directory not found
    }
}