#![no_std]
extern crate alloc;
use alloc::vec::Vec;
use core::fmt::{self, Write};
mod sys {
#[link(wasm_import_module = "env")]
unsafe extern "C" {
pub fn GetWinSizeX() -> i32;
pub fn GetWinSizeY() -> i32;
pub fn SetWinSize(w: i32, h: i32);
pub fn GetScreenWidth() -> i32;
pub fn GetScreenHeight() -> i32;
pub fn DrawRect(x: i32, y: i32, w: i32, h: i32, color: u32);
pub fn DrawText(x: i32, y: i32, ptr: *const u8, len: i32, color: u32);
pub fn Print(ptr: *const u8, len: i32);
pub fn GetFileSize(path_ptr: *const u8, path_len: i32) -> i32;
pub fn ReadFile(path_ptr: *const u8, path_len: i32, out_ptr: *mut u8, max_len: i32) -> i32;
pub fn WriteFile(path_ptr: *const u8, path_len: i32, data_ptr: *const u8, data_len: i32) -> i32;
pub fn RmFile(path_ptr: *const u8, len: i32) -> i32;
pub fn PollEvent(out_ptr: *mut u8) -> i32;
pub fn Die(code: i32) -> i32;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Event {
KeyDown(u16),
KeyUp(u16),
MouseLeftDown { x: i32, y: i32 },
MouseLeftUp { x: i32, y: i32 },
MouseRightDown { x: i32, y: i32 },
MouseRightUp { x: i32, y: i32 },
MouseMove { x: i32, y: i32 },
ScrollUp,
ScrollDown,
}
pub struct Stdout;
impl Write for Stdout {
fn write_str(&mut self, s: &str) -> fmt::Result {
print(s);
Ok(())
}
}
#[macro_export]
macro_rules! print {
($($arg:tt)*) => {{
use core::fmt::Write;
let _ = write!($crate::Stdout, $($arg)*);
}};
}
#[macro_export]
macro_rules! println {
() => ($crate::print!("\n"));
($($arg:tt)*) => {{
$crate::print!($($arg)*);
$crate::print!("\n");
}};
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Color(pub u32);
impl Color {
pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
Self(((r as u32) << 16) | ((g as u32) << 8) | (b as u32))
}
pub const fn rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
Self(((a as u32) << 24) | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32))
}
}
pub struct Window;
impl Window {
pub fn size() -> (i32, i32) {
unsafe { (sys::GetWinSizeX(), sys::GetWinSizeY()) }
}
pub fn set_size(width: i32, height: i32) {
unsafe { sys::SetWinSize(width, height) }
}
pub fn screen_size() -> (i32, i32) {
unsafe { (sys::GetScreenWidth(), sys::GetScreenHeight()) }
}
}
pub struct Graphics;
impl Graphics {
pub fn draw_rect(x: i32, y: i32, w: i32, h: i32, color: u32) {
unsafe { sys::DrawRect(x, y, w, h, color) }
}
pub fn draw_text(x: i32, y: i32, text: &str, color: u32) {
unsafe {
sys::DrawText(x, y, text.as_ptr(), text.len() as i32, color);
}
}
}
pub fn print(text: &str) {
unsafe {
sys::Print(text.as_ptr(), text.len() as i32);
}
}
pub fn die(code: i32) {
unsafe {
sys::Die(code);
}
}
pub struct Fs;
pub const MAX_FILE_SIZE: i32 = 512 * 1024 * 1024;
impl Fs {
pub fn get_file_size(path: &str) -> Result<i32, FsError> {
let res = unsafe { sys::GetFileSize(path.as_ptr(), path.len() as i32) };
if res >= 0 {
Ok(res)
} else {
Err(FsError::from_code(res))
}
}
pub fn read_file(path: &str) -> Result<Vec<u8>, i32> {
let size = unsafe { sys::GetFileSize(path.as_ptr(), path.len() as i32) };
if size < 0 {
return Err(size);
}
if size > MAX_FILE_SIZE {
return Err(-6);
}
let mut buffer = alloc::vec![0u8; size as usize];
let res = unsafe {
sys::ReadFile(
path.as_ptr(),
path.len() as i32,
buffer.as_mut_ptr(),
buffer.len() as i32,
)
};
if res >= 0 {
buffer.truncate(res as usize);
Ok(buffer)
} else {
Err(res)
}
}
pub fn write_file(path: &str, data: &[u8]) -> Result<(), FsError> {
let res = unsafe {
sys::WriteFile(
path.as_ptr(),
path.len() as i32,
data.as_ptr(),
data.len() as i32,
)
};
if res == 0 {
Ok(())
} else {
Err(FsError::from_code(res))
}
}
pub fn remove_file(path: &str) -> Result<(), FsError> {
let res = unsafe { sys::RmFile(path.as_ptr(), path.len() as i32) };
if res == 0 {
Ok(())
} else {
Err(FsError::from_code(res))
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FsError {
NotFound,
PermissionDenied,
AlreadyExists,
DiskFull,
InvalidPath,
FileTooLarge,
Unknown(i32),
}
#[allow(non_upper_case_globals)]
impl FsError {
pub const Dolbayob: Self = crate::FsError::NotFound;
pub const IdiNahui: Self = crate::FsError::PermissionDenied;
pub const AlreadyFucked: Self = crate::FsError::AlreadyExists;
pub const AssFull: Self = crate::FsError::DiskFull;
pub const YouInvalid: Self = crate::FsError::InvalidPath;
pub const DickTooLarge: Self = crate::FsError::FileTooLarge;
pub fn from_code(code: i32) -> Self {
match code {
-1 => Self::NotFound,
-2 => Self::PermissionDenied,
-3 => Self::AlreadyExists,
-4 => Self::DiskFull,
-5 => Self::InvalidPath,
-6 => Self::FileTooLarge,
c => Self::Unknown(c),
}
}
pub fn from_code_but_for_me(code: i32) -> Self {
match code {
-1 => Self::Dolbayob,
-2 => Self::IdiNahui,
-3 => Self::AlreadyFucked,
-4 => Self::AssFull,
-5 => Self::YouInvalid,
-6 => Self::DickTooLarge,
c => Self::Unknown(c),
}
}
}
pub fn poll_event() -> Option<Event> {
let mut buf = [0u8; 16];
let res = unsafe { sys::PollEvent(buf.as_mut_ptr()) };
if res == 0 {
return None;
}
match buf[0] {
1 => {
let code = u16::from_le_bytes([buf[1], buf[2]]);
Some(Event::KeyDown(code))
}
2 => {
let code = u16::from_le_bytes([buf[1], buf[2]]);
Some(Event::KeyUp(code))
}
3 => {
let x = i32::from_le_bytes([buf[1], buf[2], buf[3], buf[4]]);
let y = i32::from_le_bytes([buf[5], buf[6], buf[7], buf[8]]);
Some(Event::MouseLeftDown { x, y })
}
4 => {
let x = i32::from_le_bytes([buf[1], buf[2], buf[3], buf[4]]);
let y = i32::from_le_bytes([buf[5], buf[6], buf[7], buf[8]]);
Some(Event::MouseLeftUp { x, y })
}
5 => {
let x = i32::from_le_bytes([buf[1], buf[2], buf[3], buf[4]]);
let y = i32::from_le_bytes([buf[5], buf[6], buf[7], buf[8]]);
Some(Event::MouseRightDown { x, y })
}
6 => {
let x = i32::from_le_bytes([buf[1], buf[2], buf[3], buf[4]]);
let y = i32::from_le_bytes([buf[5], buf[6], buf[7], buf[8]]);
Some(Event::MouseRightUp { x, y })
}
7 => {
let x = i32::from_le_bytes([buf[1], buf[2], buf[3], buf[4]]);
let y = i32::from_le_bytes([buf[5], buf[6], buf[7], buf[8]]);
Some(Event::MouseMove { x, y })
}
8 => Some(Event::ScrollUp),
9 => Some(Event::ScrollDown),
_ => None,
}
}