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
use crate::rrt0;
use crate::thread;
use crate::result::*;
use crate::mem;
use crate::ipc::sf;
use alloc::string::String;

pub type LogSeverity = logpacket::detail::LogSeverity;

pub struct LogMetadata {
    pub severity: LogSeverity,
    pub verbosity: bool,
    pub msg: String,
    pub file_name: &'static str,
    pub fn_name: &'static str,
    pub line_number: u32
}

impl LogMetadata {
    pub fn new(severity: LogSeverity, verbosity: bool, msg: String, file_name: &'static str, fn_name: &'static str, line_number: u32) -> Self {
        Self {
            severity,
            verbosity,
            msg,
            file_name,
            fn_name,
            line_number,
        }
    }
}

pub trait Logger {
    fn new() -> Self;
    fn log(&mut self, metadata: &LogMetadata);
}

pub fn log_with<L: Logger>(metadata: &LogMetadata) {
    let mut logger = L::new();
    logger.log(metadata);
}

fn format_plain_string_log_impl(metadata: &LogMetadata, log_type: &str) -> String {
    let severity_str = match metadata.severity {
        LogSeverity::Trace => "Trace",
        LogSeverity::Info => "Info",
        LogSeverity::Warn => "Warn",
        LogSeverity::Error => "Error",
        LogSeverity::Fatal => "Fatal",
    };
    let thread_name = match thread::get_current_thread().name.get_str() {
        Ok(name) => name,
        _ => "<unknown>",
    };
    format!("[ {} (severity: {}, verbosity: {}) from {} in thread {}, at {}:{} ] {}", log_type, severity_str, metadata.verbosity, metadata.fn_name, thread_name, metadata.file_name, metadata.line_number, metadata.msg)
}

use crate::svc;

pub struct SvcOutputLogger;

impl Logger for SvcOutputLogger {
    fn new() -> Self {
        Self {}
    }

    fn log(&mut self, metadata: &LogMetadata) {
        let msg = format_plain_string_log_impl(metadata, "SvcOutputLog");
        let _ = svc::output_debug_string(msg.as_ptr(), msg.len());
    }
}

use crate::service;
use crate::service::fsp::srv;
use crate::service::fsp::srv::IFileSystemProxy;

pub struct FsAccessLogLogger {
    service: Result<mem::Shared<srv::FileSystemProxy>>
}

impl Logger for FsAccessLogLogger {
    fn new() -> Self {
        Self { service: service::new_service_object() }
    }

    fn log(&mut self, metadata: &LogMetadata) {
        let msg = format_plain_string_log_impl(metadata, "FsAccessLog");
        match self.service {
            Ok(ref mut fspsrv) => {
                let _ = fspsrv.get().output_access_log_to_sd_card(sf::Buffer::from_array(msg.as_bytes()));
            },
            _ => {}
        }
    }
}

use crate::service::lm;
use crate::service::lm::ILogService;
use crate::service::lm::ILogger;

pub struct LmLogger {
    logger: Option<mem::Shared<dyn ILogger>>
}

impl Logger for LmLogger {
    fn new() -> Self {
        let logger = match service::new_service_object::<lm::LogService>() {
            Ok(log_srv) => {
                match log_srv.get().open_logger(sf::ProcessId::new()) {
                    Ok(logger_obj) => Some(logger_obj),
                    Err(_) => None
                }
            },
            Err(_) => None
        };

        Self { logger }
    }

    fn log(&mut self, metadata: &LogMetadata) {
        if let Some(logger_obj) = &self.logger {
            let mut log_packet = logpacket::LogPacket::new();

            if let Ok(process_id) = svc::get_process_id(svc::CURRENT_PROCESS_PSEUDO_HANDLE) {
                log_packet.set_process_id(process_id);
            }

            let cur_thread = thread::get_current_thread();
            if let Ok(thread_id) = cur_thread.get_id() {
                log_packet.set_thread_id(thread_id);
            }

            log_packet.set_file_name(String::from(metadata.file_name));
            log_packet.set_function_name(String::from(metadata.fn_name));
            log_packet.set_line_number(metadata.line_number);
    
            let mod_name = match rrt0::get_module_name().path.get_string() {
                Ok(name) => name,
                Err(_) => String::from("aarch64-switch-rs (invalid module name)")
            };
            log_packet.set_module_name(mod_name);

            log_packet.set_text_log(metadata.msg.clone());

            let thread_name = match cur_thread.name.get_str() {
                Ok(name) => name,
                _ => "aarch64-switch-rs (invalid thread name)",
            };
            log_packet.set_thread_name(String::from(thread_name));

            for packet in log_packet.encode_packet() {
                let _ = logger_obj.get().log(sf::Buffer::from_array(&packet));
            }
        }
    }
}