// Program for playing melodys on PC-Speaker.
// For GNU/Linux 64 bit version.
// Version: 3.
// Written on Rust. For compile run rustc gorg64_spkplay.rs
// Copyright (C) 2021,2022,2024,2025  Artyomov Alexander
// http://self-made-free.ru/
// aralni@mail.ru

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.

// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

use std::{thread, time};
use std::arch::asm;
use std::fs::File;
use std::io::{self, Read};
use std::env;
use std::process::Command;
use std::process::exit;

const SIGTERM: i32 = 15;
const SIGINT: i32 = 2;
const SIGHUP: i32 = 1;
const SIGQUIT: i32 = 3;
const SIGTSTP: i32 = 20;

static mut O: u16 = 0;

// #[repr(C)]
// struct SigAction {
//    sa_handler: usize, // Вектор функции обработчика
//    sa_mask: usize,
//    sa_flags: usize,
//    sa_restorer: usize,
// }

// В этом примере будет небезопасный код для вызова сигнала в Linux через syscalls
extern "C" {
    fn signal(sig: i32, handler: usize) -> usize;
}

// Обработчик сигнала, который будет вызываться при получении сигнала
unsafe extern "C" fn signal_handler(sig: i32) {
    match sig {
        SIGTERM => {
            println!("Получен сигнал SIGTERM: завершение программы.");
            gspkoff();
            exit(0);
        }
        SIGINT => {
            println!("Получен сигнал SIGINT: прерывание программы (Ctrl+C).");
            gspkoff();
            exit(0);
        }
        SIGHUP => {
            println!("Получен сигнал SIGHUP: перезапуск программы.");
            gspkoff();
            exit(0);
        }
        SIGQUIT => {
            println!("Получен сигнал SIGQUIT: выход с дампом памяти.");
            gspkoff();
            exit(0);
        }
        SIGTSTP => {
            println!("Получен сигнал SIGTSTP: остановка программы.");
            gspkoff();
            exit(0);
        }
        _ => (),
    }
}

struct TTW {
tone: u16,
duration: u16,
}

fn read_ttw_records(file_path: &str) -> io::Result<Vec<TTW>> {
    let mut file = File::open(file_path)?;
    let mut buffer = Vec::new();

    // Читаем весь файл в буфер
    file.read_to_end(&mut buffer)?;

    let mut records = Vec::new();
    let record_size = std::mem::size_of::<TTW>();

    // Разбиваем буфер на записи
    for chunk in buffer.chunks(record_size) {
        if chunk.len() == record_size {
            let ttw: TTW = unsafe {
                // Пытаемся интерпретировать блок байтов как структуру TTW
                std::ptr::read_unaligned(chunk.as_ptr() as *const TTW)
            };
            records.push(ttw);
        }
    }
        
    Ok(records)
}

fn delay(d: u16) {
let t_millis = time::Duration::from_millis(d.into());
thread::sleep(t_millis);
}

fn ioperm1() -> bool {
unsafe {
let r: i64;
asm!(
"mov  rax, 173",
"mov  rdi, 42h",
"mov  rsi, 2",
"mov  rdx, 1",
"syscall",
out("rax") r
);
return r != 0
} }

fn ioperm2() -> bool {
unsafe {
let r: i64;
asm!(
"mov  rax, 173",
"mov  rdi, 61h",
"mov  rsi, 1",
"mov  rdx, 1",
"syscall",
out("rax") r
);
return r != 0
} }

fn spkon() { unsafe { asm!(
"in al, 61h",
"or al, 03h",
"out 61h, al"
); } }

fn spkoff() { unsafe { asm!(
"in al, 61h",
"or al, 03h",
"xor al, 03h",
"out 61h, al"
); } }

fn spk(t: u16) {
unsafe { asm!(
"push rax",
"mov	al, 0B6h",
"out	43h, al",
"pop rax",
"out	42h, al",
"shr	ax, 8",
"out	42h, al",
in("ax") t
); } }

fn kspkon() {
unsafe { asm!(
"mov rax, 1000",
"syscall"
); } }

fn kspkoff() {
unsafe { asm!(
"mov rax, 1001",
"syscall"
); } }

fn kspk(t: u16) {
unsafe { asm!(
"mov rax, 1002",
"syscall",
in("rdi") t
); } }

fn kspkexists() -> bool {
unsafe {
let r: i64;
asm!(
"mov rax, 1003",
"syscall",
out("rax") r
);
return r == 123
} }

fn setuid() -> bool {
unsafe {
let r: i64;
asm!(
"mov rax, 105",
"mov rdi, 0",
"syscall",
out("rax") r
);
return r != 0
} }

fn getuid() -> u64 {
unsafe {
let r: u64;
asm!(
"mov rax, 102",
"syscall",
out("rax") r
);
return r
} }

fn geteuid() -> u64 {
unsafe {
let r: u64;
asm!(
"mov rax, 107",
"syscall",
out("rax") r
);
return r
} }

fn gspkoff() {
match unsafe{O}{
0=>{spkoff();},
1=>{kspkoff();},
_=>println!("Rest of the number") }
}

fn main() -> io::Result<()> {
let args: Vec<String> = env::args().collect();
let param_count = args.len()-1;
println!("GALAXY ORGANIZER SPEAKER PLAYER Version 3");
println!("Artyomov Alexander 2024-2025  License: GNU AGPLv3 and above");
println!("Use: gorg64_spkplay or gorg64_spkplay somemusic.speaker somemusic2.speaker ...");

// Получаем реальный UID
let uid = getuid();
// Получаем эффективный UID
let euid = geteuid();
println!("UID: {}, EUID: {}", uid, euid);
    // Если эффективный UID равен 0 (root), пытаемся установить UID в 0 (root)
    if euid == 0 {
            if setuid() {
                eprintln!("Ошибка при установке UID");
                exit(1);
            }
    }
// Получаем реальный UID
let uid = getuid();
// Получаем эффективный UID
let euid = geteuid();
println!("After: UID: {}, EUID: {}", uid, euid);

unsafe {
let r: u64;
asm!(
"mov rax, 141",
"mov rdi, 0", // PRIO_PROCESS
//"mov rsi, 0",
"mov rdx, -20",
"syscall",
in("rsi") euid,
out("rax") r
);
println!("RetValNice= {}", r);
if r == 0 {println!("Priority=-20")} else {println!("Priority not settled. Need Root privileges.")};
}

//let mut o: u16 = 0;
if ioperm1() | ioperm2() {
 println!("Error access to I/O ports");
 if kspkexists() {
  println!("Kernel path exists. Use it.");
unsafe{ O = 1; }
 } else {
  println!("Error. Not valid output.");
  std::process::exit(0);
 }
} else {
 println!("Use I/O ports");
}

    unsafe {
        // Регистрируем обработчики для сигналов
        signal(SIGTERM, signal_handler as usize);
        signal(SIGINT, signal_handler as usize);
        signal(SIGHUP, signal_handler as usize);
        signal(SIGQUIT, signal_handler as usize);
        signal(SIGTSTP, signal_handler as usize);
    }

if param_count < 1 {
match unsafe{O}{
0=>{ spkon(); spk(1000); delay(1000); spkoff(); },
1=>{ kspkon(); kspk(1000); delay(1000); kspkoff(); },
_=>println!("Rest of the number") }
println!("No files given");
std::process::exit(0);
}

if args[1] == "--stop" {
Command::new("killall")
.arg("gorg64_spkplay")
.output()
.expect("failed to execute process");
std::process::exit(0);
}

for f in 1..param_count+1 {
println!("Playing: {:?}",args[f]);
let records = read_ttw_records(&args[f])?;
match unsafe{O}{
0=>{ spkon();
    for record in records {
 if record.tone < 1 {
   spkoff();
   delay(record.duration);
   spkon();
 } else {
   spk(record.tone);
   delay(record.duration);
 };
    }
spkoff(); },
1=>{ kspkon();
    for record in records {
 if record.tone < 1 {
   kspkoff();
   delay(record.duration);
   kspkon();
 } else {
   kspk(record.tone);
   delay(record.duration);
 };
    }
kspkoff(); }
_=>println!("Rest of the number"), }
}

	Ok(())
}