Skip to content

Commit abc0a68

Browse files
Merge pull request #4 from ADawesomeguy/linux-scriptslog
2 parents 012bc83 + 84403bf commit abc0a68

3 files changed

Lines changed: 119 additions & 29 deletions

File tree

src/cli/local_subcommands.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@ pub(crate) enum LocalSubcommands {
2121

2222
/// Filter out lines that do not containt highlighted text
2323
#[clap(short, long)]
24-
filter_non_highlighted: bool
24+
filter_non_highlighted: bool,
25+
26+
/// Specify a custom, full path to the scriptslog file
27+
#[clap(short='p', long)]
28+
custom_path: Option<String>
2529
}
2630
}
2731

@@ -57,9 +61,9 @@ pub(crate) fn handle_local_subcommand( cmd: LocalSubcommands, options: CliOption
5761
if !options.no_wait { thread::sleep( Duration::from_millis(3000) ) }
5862

5963
match cmd {
60-
LocalSubcommands::Scriptslog { colors, refresh_time, filter_non_highlighted } => {
64+
LocalSubcommands::Scriptslog { colors, refresh_time, filter_non_highlighted, custom_path } => {
6165
let highlights = scriptslog_colors_to_highlight_records(colors);
62-
if let Some(err) = rw3d_core::scriptslog::tail_scriptslog(|text| scriptslog_printer(text, &highlights, filter_non_highlighted), refresh_time, logger_rcv) {
66+
if let Some(err) = rw3d_core::scriptslog::tail_scriptslog(|text| scriptslog_printer(text, &highlights, filter_non_highlighted), refresh_time, logger_rcv, custom_path) {
6367
println!("{}", err);
6468
}
6569
}

src/core/constants.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,5 @@ pub const TYPE_STRING_UTF8: [u8; 2] = [0xAC, 0x08];
3838
pub const TYPE_STRING_UTF16: [u8; 2] = [0x9C, 0x16];
3939

4040

41-
pub const SCRIPTSLOG_FILE_NAME: &str = "scriptslog.txt";
41+
pub const SCRIPTSLOG_FILE_NAME: &str = "scriptslog.txt";
42+
pub const LINUX_STEAM_PFX_PATH: &str = ".local/share/Steam/steamapps/compatdata/292030/pfx/drive_c/users/steamuser/Documents"; // it's ok to use forward slashes because this is Linux-specific

src/core/scriptslog.rs

Lines changed: 110 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{fs::{File, OpenOptions}, sync::mpsc::{Receiver, TryRecvError}, io::{BufReader, Seek, SeekFrom, Read}, time::Duration, path::Path};
1+
use std::{fs::{File, OpenOptions}, sync::mpsc::{Receiver, TryRecvError}, io::{BufReader, Seek, SeekFrom, Read}, time::Duration, path::{Path, PathBuf}};
22
use directories::UserDirs;
33

44
use crate::constants;
@@ -8,9 +8,66 @@ use crate::constants;
88
/// In case of error will return Some with that error message, otherwise will return None.
99
/// For `printer` parameter you can pass any function or closure that you want to print log text with,
1010
/// the string passed to said printer will consist of one or more lines of text.
11-
pub fn tail_scriptslog<P>( printer: P, refresh_time_millis: u64, cancel_token: Receiver<()> ) -> Option<String>
11+
pub fn tail_scriptslog<P>( printer: P, refresh_time_millis: u64, cancel_token: Receiver<()>, custom_path: Option<String> ) -> Option<String>
1212
where P: Fn(&String) -> () {
13-
match scriptslog_file() {
13+
if let Some(p) = custom_path {
14+
tail_scriptslog_loop(Path::new(&p).to_path_buf(), printer, refresh_time_millis, cancel_token)
15+
} else {
16+
match scriptslog_file_path() {
17+
Ok(p) => {
18+
tail_scriptslog_loop(p, printer, refresh_time_millis, cancel_token)
19+
}
20+
Err(e) => {
21+
Some(e)
22+
}
23+
}
24+
}
25+
}
26+
27+
fn scriptslog_file_path() -> Result<PathBuf, String> {
28+
let mut docs = None;
29+
if let Some(ud) = UserDirs::new() {
30+
if cfg!(windows) {
31+
if let Some(path) = ud.document_dir() {
32+
docs = Some(path.to_owned());
33+
}
34+
}
35+
else if cfg!(unix) {
36+
if let Some(path) = Some(ud.home_dir()) {
37+
docs = Some(path.join(constants::LINUX_STEAM_PFX_PATH).to_owned());
38+
}
39+
}
40+
else {
41+
unimplemented!();
42+
}
43+
}
44+
45+
if let Some(docs) = docs {
46+
return Ok( docs.join(Path::new("The Witcher 3").join(constants::SCRIPTSLOG_FILE_NAME)) );
47+
} else {
48+
return Err( "Documents directory could not be found.".to_owned() );
49+
}
50+
}
51+
52+
fn open_scriptslog(path: &PathBuf) -> Result<File, String> {
53+
let file = OpenOptions::new()
54+
.read(true)
55+
.write(true) // so that it can be created if doesn't exist
56+
.create(true)
57+
.open(path);
58+
59+
if let Err(e) = file {
60+
println!("{:?}", e.kind());
61+
return Err("File open error: ".to_owned() + &e.to_string());
62+
} else {
63+
return Ok(file.unwrap());
64+
}
65+
}
66+
67+
#[cfg(target_os = "windows")]
68+
fn tail_scriptslog_loop<P>(scriptslog_path: PathBuf, printer: P, refresh_time_millis: u64, cancel_token: Receiver<()>) -> Option<String>
69+
where P: Fn(&String) -> () {
70+
match open_scriptslog(&scriptslog_path) {
1471
Ok(file) => {
1572
let mut reader = BufReader::new(&file);
1673
// start from the end of the file
@@ -53,33 +110,61 @@ where P: Fn(&String) -> () {
53110
Err(e) => {
54111
Some(e)
55112
}
56-
}
113+
}
57114
}
58115

59-
fn scriptslog_file() -> Result<File, String> {
60-
let mut docs = None;
61-
if let Some(ud) = UserDirs::new() {
62-
if let Some(path) = ud.document_dir() {
63-
docs = Some(path.to_owned());
116+
#[cfg(target_os = "linux")]
117+
fn tail_scriptslog_loop<P>(scriptslog_path: PathBuf, printer: P, refresh_time_millis: u64, cancel_token: Receiver<()>) -> Option<String>
118+
where P: Fn(&String) -> () {
119+
let mut last_pos: u64;
120+
match open_scriptslog(&scriptslog_path) {
121+
Ok(file) => last_pos = file.metadata().unwrap().len(),
122+
Err(e) => {
123+
return Some(e)
64124
}
65125
}
126+
127+
let mut text = String::new();
128+
loop {
129+
match cancel_token.try_recv() {
130+
Ok(_) | Err(TryRecvError::Disconnected) => {
131+
break;
132+
}
133+
Err(_) => {}
134+
}
135+
136+
// on linux file system is different in a sense that we have to reopen the file to see the changes made to it
137+
match open_scriptslog(&scriptslog_path) {
138+
Ok(file) => {
139+
let mut reader = BufReader::new(&file);
140+
let filesize = file.metadata().unwrap().len();
66141

67-
if let Some(docs) = docs {
68-
let scriptslog_path = docs.join(Path::new("The Witcher 3").join(constants::SCRIPTSLOG_FILE_NAME));
69-
70-
let file = OpenOptions::new()
71-
.read(true)
72-
.write(true) // so that it can be created if doesn't exist
73-
.create(true)
74-
.open( scriptslog_path );
75-
76-
if let Err(e) = file {
77-
println!("{:?}", e.kind());
78-
return Err("File open error: ".to_owned() + &e.to_string());
79-
} else {
80-
return Ok(file.unwrap());
142+
if last_pos > filesize {
143+
last_pos = reader.seek(SeekFrom::Start(0)).unwrap();
144+
} else {
145+
reader.seek(SeekFrom::Start(last_pos)).unwrap();
146+
}
147+
148+
text.clear();
149+
match reader.read_to_string(&mut text) {
150+
Ok(size) => {
151+
if size > 0 {
152+
last_pos += size as u64;
153+
printer(&text);
154+
}
155+
}
156+
Err(e) => {
157+
return Some("File read error: ".to_owned() + &e.to_string())
158+
}
159+
}
160+
}
161+
Err(e) => {
162+
return Some(e)
163+
}
81164
}
82-
} else {
83-
Err( "Documents directory could not be found.".to_owned() )
165+
166+
std::thread::sleep( Duration::from_millis( refresh_time_millis ) );
84167
}
168+
169+
None
85170
}

0 commit comments

Comments
 (0)