Skip to main content

Overview

The services module emulates Nintendo Switch system services using the nn namespace. It provides 160+ services that games use for graphics, audio, input, networking, and system functions.

ServiceTrait

Core trait that all system services must implement.

Definition

pub trait ServiceTrait {
    fn run(_state: &mut sys::State) -> () {
        todo!();
    }
}
state
&mut sys::State
required
Mutable reference to the global system state

Implementation

Each service implements this trait to initialize its state:
impl ServiceTrait for acc::State {
    fn run(state: &mut sys::State) {
        state.services.acc = Some(State::new(state));
    }
}

System State

The global system state that holds all service instances and GPU state.

Structure

pub struct State {
    pub services: Services,
    pub gpu_state: gpu::State,
}
services
Services
Container for all 160+ service instances
gpu_state
gpu::State
GPU emulation state (see GPU Module)

Methods

new()

Create a new system state with default values.
pub fn new() -> Self
Example:
use oboromi_core::sys::State;
use oboromi_core::nn;

let mut system = State::new();
nn::start_host_services(&mut system);

Services Container

The Services struct holds optional instances of all 160 system services.

Structure

#[derive(Default)]
pub struct Services {
    pub acc: Option<nn::acc::State>,
    pub hid: Option<nn::hid::State>,
    pub vi: Option<nn::vi::State>,
    pub nvdrv: Option<nn::nvdrv::State>,
    pub fsp_srv: Option<nn::fsp_srv::State>,
    pub audout: Option<nn::audout::State>,
    // ... 154 more services
}
Each service is wrapped in Option<T> because services are initialized on-demand.

Service Macro

Services are defined using the define_service! macro for boilerplate reduction.

Macro Definition

macro_rules! define_service {
    ($($name:ident),* $(,)?) => {
        $(
            pub mod $name {
                use crate::nn::ServiceTrait;
                use crate::sys;
                
                pub struct State {}
                
                impl State {
                    pub fn new(_state: &mut sys::State) -> Self {
                        Self {}
                    }
                }
                
                impl ServiceTrait for State {
                    fn run(state: &mut sys::State) {
                        state.services.$name = Some(State::new(state));
                    }
                }
            }
        )*
    };
}

Usage

Define all services in one macro invocation:
define_service!(
    acc,      // Account services
    hid,      // Human Interface Device (input)
    vi,       // Visual/Display services
    nvdrv,    // NVIDIA driver
    audout,   // Audio output
    fsp_srv,  // Filesystem services
    // ... 154 more
);

Service Initialization

start_host_services()

Initialize all system services.
pub fn start_host_services(state: &mut sys::State)
state
&mut sys::State
required
System state to initialize services into
This function iterates through all 160 services and calls their run() method:
pub fn start_host_services(state: &mut sys::State) {
    let entries: [(&str, fn(&mut sys::State)); 160] = [
        ("acc", nn::acc::State::run),
        ("hid", nn::hid::State::run),
        ("vi", nn::vi::State::run),
        // ... 157 more
    ];
    
    for (_name, run_fn) in entries.iter() {
        run_fn(state);
    }
}
Example:
use oboromi_core::sys::State;
use oboromi_core::nn;

fn main() {
    let mut system = State::new();
    
    // Initialize all 160 services
    nn::start_host_services(&mut system);
    
    // Now services are available
    assert!(system.services.hid.is_some());
    assert!(system.services.vi.is_some());
}

Available Services

Oboromi emulates 160 Nintendo Switch system services:
  • vi - Visual/Display interface
  • vi2 - Visual interface v2
  • nvdrv - NVIDIA driver
  • nvdrvdbg - NVIDIA driver debug
  • nvdbg - NVIDIA debug
  • nvgem - NVIDIA GEM memory manager
  • nvmemp - NVIDIA memory profiler
  • audout - Audio output
  • audin - Audio input
  • audren - Audio renderer
  • audrec - Audio recorder
  • audctl - Audio control
  • auddev - Audio device
  • auddebug - Audio debug
  • auddmg - Audio damage (DRC)
  • audsmx - Audio submix
  • aud - Audio base
  • hwopus - Hardware Opus codec
  • hid - Human Interface Device
  • hidbus - HID bus
  • irs - Infrared sensor
  • ahid - Abstract HID
  • fsp_srv - Filesystem service
  • fsp_ldr - Filesystem loader
  • fsp_pr - Filesystem PR
  • fs - Filesystem base
  • nifm - Network interface manager
  • bsd - BSD sockets
  • bsdcfg - BSD configuration
  • sfdnsres - DNS resolver
  • ldn - Local wireless
  • lp2p - Local P2P
  • eth - Ethernet
  • ethc - Ethernet control
  • wlan - Wireless LAN
  • nsd - Network service discovery
  • dns - DNS client
  • htc / htcs - Host target communication
  • acc - Account management
  • pctl - Parental controls
  • set - Settings
  • pm - Process manager
  • ldr - Loader
  • ro - Runtime object linker
  • fatal - Fatal error handler
  • time - Time services
  • psm - Power state manager
  • apm - Application power management
  • pcv - Power control voltage
  • clkrst - Clock/reset control
  • And many more…
acc, adraw, ahid, aoc, apm, applet_ae, applet_oe, arp, aud, audctl, auddebug, auddev, auddmg, audin, audout, audrec, audren, audsmx, avm, banana, batlog, bcat, bgtc, bpc, bpmpmr, bsd, bsdcfg, bt, btdrv, btm, btp, capmtp, caps, caps2, cec_mgr, chat, clkrst, codecctl, csrng, dauth, disp, dispdrv, dmnt, dns, dt, ectx, erpt, es, eth, ethc, eupld, fan, fatal, fgm, file_io, friend, fs, fsp_ldr, fsp_pr, fsp_srv, gds, gpio, gpuk, grc, gsv, hdcp, hid, hidbus, host1x, hshl, htc, htcs, hwopus, i2c, idle, ifcfg, imf, ins, irs, jit, lbl, ldn, ldr, led, lm, lp2p, lr, manu, mig, mii, miiimg, mm, mnpp, ncm, nd, ndd, ndrm, news, nfc, nfp, ngc, ngct, nifm, nim, notif, npns, ns, nsd, ntc, nvdbg, nvdrv, nvdrvdbg, nvgem, nvmemp, olsc, omm, ommdisp, ovln, pcie, pcm, pctl, pcv, pdm, pgl, pinmux, pl, pm, prepo, psc, psm, pwm, rgltr, ro, rtc, sasbus, set, sf_uds, sfdnsres, spbg, spi, spl, sprof, spsm, srepo, ssl, syncpt, tc, tcap, time, tma_log, tmagent, ts, tspm, uart, usb, vi, vi2, vic, wlan, xcd

Implementing a Custom Service

To add functionality to an existing service or create a new one:
1

Define the service state

pub mod my_service {
pub struct State {
    pub counter: u32,
    pub enabled: bool,
}

impl State {
    pub fn new(_state: &mut sys::State) -> Self {
        Self {
            counter: 0,
            enabled: true,
        }
    }
    
    pub fn increment(&mut self) {
        self.counter += 1;
    }
}
}
2

Implement ServiceTrait

impl ServiceTrait for my_service::State {
fn run(state: &mut sys::State) {
    state.services.my_service = Some(State::new(state));
}
}
3

Add to Services struct

pub struct Services {
// ... existing services
pub my_service: Option<nn::my_service::State>,
}
4

Register in start_host_services

let entries: [(&str, fn(&mut sys::State)); 161] = [
// ... existing entries
("my-service", nn::my_service::State::run),
];

Complete Example

use oboromi_core::sys::State;
use oboromi_core::nn;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create system state
    let mut system = State::new();
    
    println!("Initializing system services...");
    
    // Initialize GPU
    system.gpu_state.init()?;
    println!("GPU initialized");
    
    // Start all 160 services
    nn::start_host_services(&mut system);
    println!("All services started");
    
    // Check service availability
    if system.services.hid.is_some() {
        println!("HID service available");
    }
    
    if system.services.vi.is_some() {
        println!("VI service available");
    }
    
    if system.services.nvdrv.is_some() {
        println!("NVIDIA driver service available");
    }
    
    if system.services.fsp_srv.is_some() {
        println!("Filesystem service available");
    }
    
    // System is now ready for emulation
    println!("System ready!");
    
    Ok(())
}
use oboromi_core::sys;
use oboromi_core::nn::ServiceTrait;

// Custom HID service implementation with state
pub mod hid {
    use super::*;
    
    pub struct State {
        pub button_state: u32,
        pub touch_x: i16,
        pub touch_y: i16,
        pub last_update: u64,
    }
    
    impl State {
        pub fn new(_state: &mut sys::State) -> Self {
            Self {
                button_state: 0,
                touch_x: 0,
                touch_y: 0,
                last_update: 0,
            }
        }
        
        pub fn update_button(&mut self, button: u32, pressed: bool) {
            if pressed {
                self.button_state |= button;
            } else {
                self.button_state &= !button;
            }
        }
        
        pub fn update_touch(&mut self, x: i16, y: i16) {
            self.touch_x = x;
            self.touch_y = y;
        }
    }
    
    impl ServiceTrait for State {
        fn run(state: &mut sys::State) {
            state.services.hid = Some(State::new(state));
        }
    }
}

fn main() {
    let mut system = sys::State::new();
    hid::State::run(&mut system);
    
    if let Some(ref mut hid) = system.services.hid {
        // Simulate button press
        hid.update_button(0x01, true); // A button
        println!("Button state: {:#010x}", hid.button_state);
        
        // Simulate touch
        hid.update_touch(640, 360);
        println!("Touch: ({}, {})", hid.touch_x, hid.touch_y);
    }
}

Architecture

The service system follows Nintendo’s IPC (Inter-Process Communication) model:
Currently, all services run in the same process. Future versions may implement true IPC for better isolation.

See Also

Build docs developers (and LLMs) love