mirror of
https://github.com/m4rcel-lol/m5rcode-ubuntu.git
synced 2025-12-06 11:03:58 +05:30
Uploading m5rcode Ubuntu Port to the repo
This commit is contained in:
185
README.md
Normal file
185
README.md
Normal file
@@ -0,0 +1,185 @@
|
||||
# m5rcode – The Unofficial Polyglot Programming Language
|
||||
|
||||

|
||||

|
||||
|
||||
**m5rcode** is an experimental **polyglot programming language** written with a blend of **Python, JavaScript, PHP, C#, and C++**.
|
||||
It uses obfuscation and cross-language embedding to allow developers to write multi-language scripts in a single file (`.m5r`).
|
||||
The project includes a custom **REPL shell** (`m5rshell`) and an **interpreter** for `.m5r` files.
|
||||
|
||||
---
|
||||
|
||||
## ✨ Features
|
||||
|
||||
- **Polyglot Language Core**
|
||||
- Mix **Python, JavaScript, PHP, CSS, C#, and C++** in a single `.m5r` file.
|
||||
- Interpreter extracts, executes, and blends code blocks.
|
||||
- Supports obfuscation for added challenge and uniqueness.
|
||||
|
||||
- **m5rshell – The REPL Shell**
|
||||
- Interactive REPL for testing code snippets.
|
||||
- Commands:
|
||||
- `new`, `nano`, `run`, `fastfetch`, `credits`, `exit`, `cd`
|
||||
- Developer-friendly CLI for creating & running `.m5r` scripts.
|
||||
|
||||
- **.m5r File Runner (Interpreter)**
|
||||
- Executes `.m5r` polyglot files directly.
|
||||
- Efficient, multi-language-aware execution engine.
|
||||
- Provides fast output even for obfuscated code.
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Requirements
|
||||
|
||||
- Python **3.8+**
|
||||
|
||||
---
|
||||
|
||||
## 📦 Installation
|
||||
|
||||
Clone this repository:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/m4rcel-lol/m5rcode.git
|
||||
cd m5rcode
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚡ Quick Start
|
||||
|
||||
### Run the REPL shell
|
||||
```bash
|
||||
python3 m5rshell.py
|
||||
```
|
||||
---
|
||||
|
||||
## 📝 Example
|
||||
|
||||
Here’s a `hello.m5r` script that prints **Hello world** in all supported languages:
|
||||
|
||||
```m5r
|
||||
<?py
|
||||
# M5RCode Python Block: OBFUSCATED - 3D Hello World + Floating Rotating 3D Square
|
||||
|
||||
import tkinter as _tk
|
||||
import math as _m
|
||||
import time as _t
|
||||
|
||||
class _T:
|
||||
def __init__(self):
|
||||
self._rt=_tk.Tk()
|
||||
self._rt.title(''.join([chr(c) for c in [51,68,32,84,101,115,116]])) # 3D Test
|
||||
self._cv=_tk.Canvas(self._rt,width=420,height=260,bg='#181c22',highlightthickness=0)
|
||||
self._cv.pack()
|
||||
self._ang=0
|
||||
self._t=0
|
||||
self._run()
|
||||
self._rt.mainloop()
|
||||
|
||||
def _run(self):
|
||||
self._cv.delete('all')
|
||||
# Draw "3D" shadowed Hello World text
|
||||
_s="".join([chr(c) for c in [72,101,108,108,111,32,119,111,114,108,100]])
|
||||
for _i in range(15,0,-3):
|
||||
self._cv.create_text(212+_i,90+_i,fill=f"#2a2a5{9-_i//3}",font=('Consolas',42,'bold'),text=_s)
|
||||
self._cv.create_text(212,90,fill="#ffe257",font=('Consolas',42,'bold'),text=_s)
|
||||
# Rotating & bouncing 3D square (pseudo-perspective)
|
||||
_a=self._ang
|
||||
_Y=130+_m.sin(self._t)*24
|
||||
_sz=70
|
||||
_pts=[]
|
||||
for _dx,_dy in [(-1,-1),(1,-1),(1,1),(-1,1)]:
|
||||
# 3D cube points, rotate a bit in Y, project to 2D
|
||||
_x=_dx*_sz*_m.cos(_a)
|
||||
_y=_dy*_sz*0.67
|
||||
_z=_dx*_sz*_m.sin(_a)
|
||||
_X=_x+_z*0.45
|
||||
_pts.append((212+_X,_Y+_y-_z*0.29))
|
||||
# Draw top face for 3D effect
|
||||
self._cv.create_polygon([_pts[i] for i in [0,1,2,3]],fill="#00ffae",outline="#222",width=3)
|
||||
# Draw floating shadow
|
||||
self._cv.create_oval(212-45,(_Y+_sz*1.03)+10,212+45,(_Y+_sz*1.24)+20,fill="#000",outline="")
|
||||
# Draw outline with depth
|
||||
for _offs in range(1,8):
|
||||
_offp=[(_x,_y+_offs*2) for _x,_y in _pts]
|
||||
self._cv.create_polygon(_offp,outline="#161413",fill="",width=1)
|
||||
# Animate: spin & up-down
|
||||
self._ang+=0.12
|
||||
self._t+=0.1
|
||||
self._rt.after(28,self._run)
|
||||
|
||||
_T()
|
||||
?>
|
||||
<?js
|
||||
(function(){
|
||||
var x=[72,101,108,108,111,32,119,111,114,108,100];
|
||||
var s='';
|
||||
for(var i of x){ s+=String.fromCharCode(i); }
|
||||
console.log(s);
|
||||
})();
|
||||
?>
|
||||
<?php
|
||||
${a}=array(72,101,108,108,111,32,119,111,114,108,100);
|
||||
echo implode(array_map('chr',${a})) . "\n";
|
||||
?>
|
||||
<?cs
|
||||
// M5RCode C# Block: OBFUSCATED, illustrative
|
||||
using System;
|
||||
class S{
|
||||
static void Main(){
|
||||
Console.WriteLine(string.Join("", new int[] {72,101,108,108,111,32,119,111,114,108,100}.Select(c => (char)c)));
|
||||
}
|
||||
}
|
||||
?>
|
||||
<?cpp
|
||||
// M5RCode C++ Block: OBFUSCATED, illustrative
|
||||
#include <iostream>
|
||||
int main() {
|
||||
int arr[] = {72,101,108,108,111,32,119,111,114,108,100};
|
||||
for(int i = 0; i < 11; i++) std::cout << (char)arr[i];
|
||||
std::cout << std::endl;
|
||||
return 0;
|
||||
}
|
||||
?>
|
||||
<?css
|
||||
body { color: #ffe257; background: #1a1c1f; }
|
||||
?>
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📂 Project Structure
|
||||
|
||||
```
|
||||
m5rcode/
|
||||
├─ m5rshell.py # The REPL shell
|
||||
├─ m5r_interpreter.py # The .m5r polyglot interpreter
|
||||
├─ files/ # Sample m5rcode scripts
|
||||
├─ utils/ # Handling everything
|
||||
├─ commands # Commands handling
|
||||
├─ version.txt # Version of m5rcode showing on fastfetch command
|
||||
├─ requirements.txt # Modules u need to install for m5rcode to work.
|
||||
└─ README.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
Contributions are welcome!
|
||||
If you want to add support for more languages, open an issue or PR.
|
||||
|
||||
---
|
||||
|
||||
## 👥 Credits
|
||||
|
||||
- **Creator:** [m5rcel](https://github.com/m4rcel-lol)
|
||||
- **Contributors:** The m5rcode community
|
||||
|
||||
---
|
||||
|
||||
## 📜 Where can I install m5rcode from?
|
||||
|
||||
You can install m5rcode from it's official website **pythonjs.cfd** hover over and copy paste to your browser.
|
||||
BIN
commands/__pycache__/cmd_cd.cpython-313.pyc
Normal file
BIN
commands/__pycache__/cmd_cd.cpython-313.pyc
Normal file
Binary file not shown.
BIN
commands/__pycache__/cmd_credits.cpython-313.pyc
Normal file
BIN
commands/__pycache__/cmd_credits.cpython-313.pyc
Normal file
Binary file not shown.
BIN
commands/__pycache__/cmd_exit.cpython-313.pyc
Normal file
BIN
commands/__pycache__/cmd_exit.cpython-313.pyc
Normal file
Binary file not shown.
BIN
commands/__pycache__/cmd_fastfetch.cpython-313.pyc
Normal file
BIN
commands/__pycache__/cmd_fastfetch.cpython-313.pyc
Normal file
Binary file not shown.
BIN
commands/__pycache__/cmd_nano.cpython-313.pyc
Normal file
BIN
commands/__pycache__/cmd_nano.cpython-313.pyc
Normal file
Binary file not shown.
BIN
commands/__pycache__/cmd_new.cpython-313.pyc
Normal file
BIN
commands/__pycache__/cmd_new.cpython-313.pyc
Normal file
Binary file not shown.
BIN
commands/__pycache__/cmd_run.cpython-313.pyc
Normal file
BIN
commands/__pycache__/cmd_run.cpython-313.pyc
Normal file
Binary file not shown.
BIN
commands/__pycache__/cmd_wdir.cpython-313.pyc
Normal file
BIN
commands/__pycache__/cmd_wdir.cpython-313.pyc
Normal file
Binary file not shown.
46
commands/cmd_cd.py
Normal file
46
commands/cmd_cd.py
Normal file
@@ -0,0 +1,46 @@
|
||||
# commands/cmd_cd.py
|
||||
import os
|
||||
from colorama import Fore
|
||||
|
||||
class CdCommand:
|
||||
def __init__(self, files_dir, shell_ref, target_dir):
|
||||
# files_dir = ~/m5rcode/files
|
||||
self.files_dir = files_dir
|
||||
self.project_root = os.path.dirname(files_dir) # ~/m5rcode
|
||||
self.shell = shell_ref[0] # the M5RShell instance
|
||||
self.target = target_dir.strip()
|
||||
|
||||
def run(self):
|
||||
if not self.target or self.target == '.':
|
||||
# Stay in current directory
|
||||
return
|
||||
|
||||
# Compute new absolute path
|
||||
candidate = os.path.abspath(
|
||||
os.path.normpath(
|
||||
os.path.join(self.shell.cwd, self.target)
|
||||
)
|
||||
)
|
||||
|
||||
# If they typed '..' from files_dir, allow up to project_root
|
||||
if self.target == '..':
|
||||
# from files_dir → project_root
|
||||
if self.shell.cwd == self.files_dir:
|
||||
new_path = self.project_root
|
||||
# from any subfolder of files_dir → one level up, but not above project_root
|
||||
else:
|
||||
new_path = os.path.dirname(self.shell.cwd)
|
||||
if not new_path.startswith(self.project_root):
|
||||
new_path = self.project_root
|
||||
else:
|
||||
new_path = candidate
|
||||
|
||||
# Check it stays within project_root
|
||||
if not new_path.startswith(self.project_root):
|
||||
print(Fore.RED + "Access denied: You cannot leave the m5rcode project.")
|
||||
return
|
||||
|
||||
if os.path.isdir(new_path):
|
||||
self.shell.cwd = new_path
|
||||
else:
|
||||
print(Fore.RED + f"No such directory: {self.target}")
|
||||
47
commands/cmd_credits.py
Normal file
47
commands/cmd_credits.py
Normal file
@@ -0,0 +1,47 @@
|
||||
from colorama import Fore, Style
|
||||
from pyfiglet import Figlet
|
||||
|
||||
def strip_ansi(text):
|
||||
import re
|
||||
return re.sub(r'\x1b\[[0-9;]*m', '', text)
|
||||
|
||||
class CreditsCommand:
|
||||
def run(self):
|
||||
box_width = 70
|
||||
inner_width = box_width - 2
|
||||
fig = Figlet(font='slant')
|
||||
credits = [
|
||||
(f"{Style.BRIGHT}{Fore.CYAN}m5rcel{Style.RESET_ALL}", "Lead Developer"),
|
||||
(f"{Style.BRIGHT}{Fore.YELLOW}pythonjs.cfd{Style.RESET_ALL}", "Project Hosting & Deployment"),
|
||||
(f"{Style.BRIGHT}{Fore.MAGENTA}colorama{Style.RESET_ALL}", "Used for terminal styling"),
|
||||
(f"{Style.BRIGHT}{Fore.GREEN}fastfetch inspired{Style.RESET_ALL}", "Design influence"),
|
||||
(f"{Style.BRIGHT}{Fore.RED}openai.com{Style.RESET_ALL}", "Some smart AI help ;)"),
|
||||
]
|
||||
|
||||
# Top border
|
||||
print(Fore.MAGENTA + "╔" + "═" * inner_width + "╗")
|
||||
|
||||
# Figlet "CREDITS" title, trimmed/padded and centered vertically
|
||||
credits_title_lines = fig.renderText("CREDITS").splitlines()
|
||||
for line in credits_title_lines:
|
||||
if line.strip() == '': continue
|
||||
raw = line[:inner_width]
|
||||
pad = (inner_width - len(strip_ansi(raw))) // 2
|
||||
out = " " * pad + raw
|
||||
out = out[:inner_width]
|
||||
out = out + " " * (inner_width - len(strip_ansi(out)))
|
||||
print(Fore.MAGENTA + "║" + Fore.LIGHTMAGENTA_EX + out + Fore.MAGENTA + "║")
|
||||
print(Fore.MAGENTA + "╟" + "─" * inner_width + "╢")
|
||||
|
||||
# Content, each line padded to match box width
|
||||
for name, role in credits:
|
||||
left = f"{name:<25}"
|
||||
dash = f"{Fore.CYAN}━{Style.RESET_ALL}"
|
||||
right = f"{Fore.LIGHTWHITE_EX}{role}{Style.RESET_ALL}"
|
||||
raw_text = f"{left} {dash} {right}"
|
||||
raw_len = len(strip_ansi(raw_text))
|
||||
line = raw_text + " " * (inner_width - raw_len)
|
||||
print(Fore.MAGENTA + "║" + line + Fore.MAGENTA + "║")
|
||||
|
||||
# Bottom border
|
||||
print(Fore.MAGENTA + "╚" + "═" * inner_width + "╝" + Style.RESET_ALL)
|
||||
163
commands/cmd_dir.py
Normal file
163
commands/cmd_dir.py
Normal file
@@ -0,0 +1,163 @@
|
||||
import os
|
||||
from colorama import Fore, Style
|
||||
import re
|
||||
|
||||
def strip_ansi(text):
|
||||
return re.sub(r'\x1b\[[0-9;]*m', '', text)
|
||||
|
||||
class DirCommand:
|
||||
def __init__(self, cwd, target):
|
||||
self.cwd = cwd
|
||||
self.target = target if target else cwd
|
||||
|
||||
def run(self):
|
||||
path = os.path.abspath(os.path.join(self.cwd, self.target))
|
||||
box_min = 60
|
||||
|
||||
if not os.path.exists(path):
|
||||
# Error box
|
||||
width = max(box_min, len(f"No such file or directory: {self.target}") + 10)
|
||||
print(Fore.RED + "╔" + "═" * (width - 2) + "╗")
|
||||
error_msg = f"No such file or directory: {self.target}"
|
||||
print(Fore.RED + "║" + error_msg.center(width - 2) + "║")
|
||||
print(Fore.RED + "╚" + "═" * (width - 2) + "╝" + Style.RESET_ALL)
|
||||
return
|
||||
|
||||
if os.path.isfile(path):
|
||||
# Single file info box
|
||||
file_size = os.path.getsize(path)
|
||||
if file_size > 1024*1024:
|
||||
size_str = f"{file_size/(1024*1024):.1f} MB"
|
||||
elif file_size > 1024:
|
||||
size_str = f"{file_size/1024:.1f} KB"
|
||||
else:
|
||||
size_str = f"{file_size} B"
|
||||
|
||||
width = max(box_min, len(self.target) + 20)
|
||||
print(Fore.MAGENTA + "╔" + "═" * (width - 2) + "╗")
|
||||
title = "File Information"
|
||||
print(Fore.MAGENTA + "║" + Fore.CYAN + Style.BRIGHT + title.center(width - 2) + Fore.MAGENTA + "║")
|
||||
print(Fore.MAGENTA + "╟" + "─" * (width - 2) + "╢")
|
||||
|
||||
name_line = f"Name: {Fore.LIGHTWHITE_EX}{self.target}{Style.RESET_ALL}"
|
||||
size_line = f"Size: {Fore.LIGHTWHITE_EX}{size_str}{Style.RESET_ALL}"
|
||||
type_line = f"Type: {Fore.LIGHTWHITE_EX}File{Style.RESET_ALL}"
|
||||
|
||||
for line in [name_line, size_line, type_line]:
|
||||
pad = " " * (width - 2 - len(strip_ansi(line)))
|
||||
print(Fore.MAGENTA + "║" + line + pad + Fore.MAGENTA + "║")
|
||||
|
||||
print(Fore.MAGENTA + "╚" + "═" * (width - 2) + "╝" + Style.RESET_ALL)
|
||||
return
|
||||
|
||||
# Directory listing
|
||||
try:
|
||||
items = os.listdir(path)
|
||||
if not items:
|
||||
# Empty directory
|
||||
width = box_min
|
||||
print(Fore.MAGENTA + "╔" + "═" * (width - 2) + "╗")
|
||||
title = f"Directory: {os.path.basename(path) or 'Root'}"
|
||||
print(Fore.MAGENTA + "║" + Fore.CYAN + Style.BRIGHT + title.center(width - 2) + Fore.MAGENTA + "║")
|
||||
print(Fore.MAGENTA + "╟" + "─" * (width - 2) + "╢")
|
||||
empty_msg = "Directory is empty"
|
||||
print(Fore.MAGENTA + "║" + Fore.YELLOW + empty_msg.center(width - 2) + Fore.MAGENTA + "║")
|
||||
print(Fore.MAGENTA + "╚" + "═" * (width - 2) + "╝" + Style.RESET_ALL)
|
||||
return
|
||||
|
||||
# Calculate columns for nice layout
|
||||
name_col = 35
|
||||
type_col = 12
|
||||
size_col = 15
|
||||
total_width = name_col + type_col + size_col + 6 # spaces + borders
|
||||
width = max(box_min, total_width)
|
||||
|
||||
# Separate files and directories
|
||||
dirs = []
|
||||
files = []
|
||||
for item in sorted(items):
|
||||
full_path = os.path.join(path, item)
|
||||
if os.path.isdir(full_path):
|
||||
dirs.append(item)
|
||||
else:
|
||||
try:
|
||||
size = os.path.getsize(full_path)
|
||||
if size > 1024*1024:
|
||||
size_str = f"{size/(1024*1024):.1f} MB"
|
||||
elif size > 1024:
|
||||
size_str = f"{size/1024:.1f} KB"
|
||||
else:
|
||||
size_str = f"{size} B"
|
||||
except:
|
||||
size_str = "Unknown"
|
||||
files.append((item, size_str))
|
||||
|
||||
# Header
|
||||
print(Fore.MAGENTA + "╔" + "═" * (width - 2) + "╗")
|
||||
title = f"Directory: {os.path.basename(path) or 'Root'}"
|
||||
print(Fore.MAGENTA + "║" + Fore.CYAN + Style.BRIGHT + title.center(width - 2) + Fore.MAGENTA + "║")
|
||||
print(Fore.MAGENTA + "╟" + "─" * (width - 2) + "╢")
|
||||
|
||||
# Column headers
|
||||
header = (
|
||||
Fore.LIGHTMAGENTA_EX + Style.BRIGHT +
|
||||
f"{'Name':<{name_col}} {'Type':<{type_col}} {'Size':<{size_col}}" +
|
||||
Style.RESET_ALL
|
||||
)
|
||||
pad = " " * (width - 2 - len(strip_ansi(header)))
|
||||
print(Fore.MAGENTA + "║" + header + pad + Fore.MAGENTA + "║")
|
||||
print(Fore.MAGENTA + "╟" + "─" * (width - 2) + "╢")
|
||||
|
||||
# List directories first
|
||||
for dirname in dirs:
|
||||
name = (dirname[:name_col-2] + "..") if len(dirname) > name_col-1 else dirname
|
||||
name_colored = f"{Fore.BLUE + Style.BRIGHT}{name:<{name_col}}{Style.RESET_ALL}"
|
||||
type_colored = f"{Fore.LIGHTCYAN_EX}Directory{Style.RESET_ALL}"
|
||||
size_colored = f"{Style.DIM}-{Style.RESET_ALL}"
|
||||
|
||||
row = f"{name_colored} {type_colored:<{type_col}} {size_colored:<{size_col}}"
|
||||
pad = " " * (width - 2 - len(strip_ansi(row)))
|
||||
print(Fore.MAGENTA + "║" + row + pad + Fore.MAGENTA + "║")
|
||||
|
||||
# List files
|
||||
for filename, file_size in files:
|
||||
name = (filename[:name_col-2] + "..") if len(filename) > name_col-1 else filename
|
||||
name_colored = f"{Fore.LIGHTWHITE_EX}{name:<{name_col}}{Style.RESET_ALL}"
|
||||
|
||||
# File type based on extension
|
||||
if '.' in filename:
|
||||
ext = filename.split('.')[-1].lower()
|
||||
if ext in ['txt', 'md', 'log']:
|
||||
type_color = Fore.GREEN
|
||||
elif ext in ['py', 'js', 'html', 'css', 'php']:
|
||||
type_color = Fore.YELLOW
|
||||
elif ext in ['jpg', 'png', 'gif', 'bmp']:
|
||||
type_color = Fore.MAGENTA
|
||||
elif ext in ['mp3', 'wav', 'mp4', 'avi']:
|
||||
type_color = Fore.CYAN
|
||||
else:
|
||||
type_color = Fore.WHITE
|
||||
file_type = f".{ext} file"
|
||||
else:
|
||||
type_color = Fore.WHITE
|
||||
file_type = "File"
|
||||
|
||||
type_colored = f"{type_color}{file_type:<{type_col}}{Style.RESET_ALL}"
|
||||
size_colored = f"{Style.DIM}{Fore.LIGHTWHITE_EX}{file_size:<{size_col}}{Style.RESET_ALL}"
|
||||
|
||||
row = f"{name_colored} {type_colored} {size_colored}"
|
||||
pad = " " * (width - 2 - len(strip_ansi(row)))
|
||||
print(Fore.MAGENTA + "║" + row + pad + Fore.MAGENTA + "║")
|
||||
|
||||
# Footer with count
|
||||
print(Fore.MAGENTA + "╟" + "─" * (width - 2) + "╢")
|
||||
count_msg = f"{len(dirs)} directories, {len(files)} files"
|
||||
print(Fore.MAGENTA + "║" + Fore.LIGHTBLACK_EX + Style.DIM + count_msg.center(width - 2) + Fore.MAGENTA + "║")
|
||||
print(Fore.MAGENTA + "╚" + "═" * (width - 2) + "╝" + Style.RESET_ALL)
|
||||
|
||||
except PermissionError:
|
||||
width = box_min
|
||||
print(Fore.RED + "╔" + "═" * (width - 2) + "╗")
|
||||
error_msg = "Access denied"
|
||||
print(Fore.RED + "║" + error_msg.center(width - 2) + "║")
|
||||
print(Fore.RED + "╚" + "═" * (width - 2) + "╝" + Style.RESET_ALL)
|
||||
72
commands/cmd_exit.py
Normal file
72
commands/cmd_exit.py
Normal file
@@ -0,0 +1,72 @@
|
||||
from colorama import Fore, Style
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
|
||||
class ExitCommand:
|
||||
def shutdown_animation(self):
|
||||
# Clear screen for clean shutdown
|
||||
os.system('cls' if os.name == 'nt' else 'clear')
|
||||
|
||||
# Shutdown sequence messages
|
||||
shutdown_msgs = [
|
||||
"Stopping Discord RPC Integration...",
|
||||
"Saving shell session...",
|
||||
"Clearing command history...",
|
||||
"Stopping background processes...",
|
||||
"Unmounting m5rcode directories...",
|
||||
"Finalizing cleanup...",
|
||||
"Thank you for using m5rcode shell!"
|
||||
]
|
||||
|
||||
print(Fore.LIGHTBLACK_EX + "m5rOS Shutdown Sequence" + Style.RESET_ALL)
|
||||
print(Fore.LIGHTBLACK_EX + "=" * 25 + Style.RESET_ALL)
|
||||
|
||||
for i, msg in enumerate(shutdown_msgs):
|
||||
time.sleep(0.3)
|
||||
if i == len(shutdown_msgs) - 1:
|
||||
# Last message in cyan
|
||||
print(Fore.CYAN + Style.BRIGHT + f"[ OK ] {msg}" + Style.RESET_ALL)
|
||||
else:
|
||||
# Regular messages in white/grey
|
||||
color = Fore.WHITE if i % 2 == 0 else Fore.LIGHTBLACK_EX
|
||||
print(color + f"[ OK ] {msg}" + Style.RESET_ALL)
|
||||
|
||||
time.sleep(0.5)
|
||||
|
||||
# Animated "powering down" effect
|
||||
print()
|
||||
sys.stdout.write(Fore.LIGHTMAGENTA_EX + "Powering down")
|
||||
for _ in range(6):
|
||||
time.sleep(0.2)
|
||||
sys.stdout.write(".")
|
||||
sys.stdout.flush()
|
||||
|
||||
print(Style.RESET_ALL)
|
||||
time.sleep(0.3)
|
||||
|
||||
# Final goodbye box
|
||||
box_width = 50
|
||||
print(Fore.MAGENTA + "╔" + "═" * (box_width - 2) + "╗")
|
||||
|
||||
goodbye_lines = [
|
||||
"m5rcode shell session ended",
|
||||
"",
|
||||
"Thanks for coding with us!",
|
||||
"See you next time! 👋"
|
||||
]
|
||||
|
||||
for line in goodbye_lines:
|
||||
if line == "":
|
||||
print(Fore.MAGENTA + "║" + " " * (box_width - 2) + "║")
|
||||
else:
|
||||
color = Fore.CYAN if "m5rcode" in line else Fore.LIGHTWHITE_EX
|
||||
centered = color + line.center(box_width - 2) + Style.RESET_ALL
|
||||
print(Fore.MAGENTA + "║" + centered + Fore.MAGENTA + "║")
|
||||
|
||||
print(Fore.MAGENTA + "╚" + "═" * (box_width - 2) + "╝" + Style.RESET_ALL)
|
||||
time.sleep(1)
|
||||
|
||||
def run(self):
|
||||
self.shutdown_animation()
|
||||
return True # Signals to shell to exit
|
||||
121
commands/cmd_fastfetch.py
Normal file
121
commands/cmd_fastfetch.py
Normal file
@@ -0,0 +1,121 @@
|
||||
import platform
|
||||
import re
|
||||
import datetime
|
||||
from pathlib import Path
|
||||
from colorama import Fore, Style
|
||||
from pyfiglet import Figlet
|
||||
|
||||
try:
|
||||
import psutil
|
||||
_PSUTIL_AVAILABLE = True
|
||||
except ImportError:
|
||||
_PSUTIL_AVAILABLE = False
|
||||
|
||||
def strip_ansi(text):
|
||||
return re.sub(r'\x1b\[[0-9;]*m', '', text)
|
||||
|
||||
class FastfetchCommand:
|
||||
def _get_uptime(self):
|
||||
if not _PSUTIL_AVAILABLE:
|
||||
return "N/A (psutil not installed)"
|
||||
try:
|
||||
boot_time_timestamp = psutil.boot_time()
|
||||
boot_datetime = datetime.datetime.fromtimestamp(boot_time_timestamp)
|
||||
current_datetime = datetime.datetime.now()
|
||||
uptime_seconds = (current_datetime - boot_datetime).total_seconds()
|
||||
|
||||
days = int(uptime_seconds // (24 * 3600))
|
||||
uptime_seconds %= (24 * 3600)
|
||||
hours = int(uptime_seconds // 3600)
|
||||
uptime_seconds %= 3600
|
||||
minutes = int(uptime_seconds // 60)
|
||||
seconds = int(uptime_seconds % 60)
|
||||
|
||||
uptime_str = []
|
||||
if days > 0:
|
||||
uptime_str.append(f"{days}d")
|
||||
if hours > 0:
|
||||
uptime_str.append(f"{hours}h")
|
||||
if minutes > 0:
|
||||
uptime_str.append(f"{minutes}m")
|
||||
if seconds > 0 or not uptime_str:
|
||||
uptime_str.append(f"{seconds}s")
|
||||
return " ".join(uptime_str)
|
||||
except Exception:
|
||||
return "Error calculating uptime"
|
||||
|
||||
def run(self):
|
||||
m5rcode_version = "1.0.0"
|
||||
try:
|
||||
version_file = Path(__file__).parents[1] / "version.txt"
|
||||
if version_file.exists():
|
||||
m5rcode_version = version_file.read_text().strip()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# ASCII "M" logo—27 chars wide
|
||||
ascii_m = [
|
||||
" _____ ",
|
||||
" /\\ \\ ",
|
||||
" /::\\____\\ ",
|
||||
" /::::| | ",
|
||||
" /:::::| | ",
|
||||
" /::::::| | ",
|
||||
" /:::/|::| | ",
|
||||
" /:::/ |::| | ",
|
||||
" /:::/ |::|___|______ ",
|
||||
" /:::/ |::::::::\\ \\ ",
|
||||
" /:::/ |:::::::::\\____\\",
|
||||
" \\::/ / ~~~~~/:::/ / ",
|
||||
" \\/____/ /:::/ / ",
|
||||
" /:::/ / ",
|
||||
" /:::/ / ",
|
||||
" /:::/ / ",
|
||||
" /:::/ / ",
|
||||
" /:::/ / ",
|
||||
" /:::/ / ",
|
||||
" \\::/ / ",
|
||||
" \\/____/ ",
|
||||
" ",
|
||||
]
|
||||
|
||||
uptime_info = self._get_uptime()
|
||||
LABEL_PAD = 17
|
||||
|
||||
info_lines = [
|
||||
f"{Fore.CYAN}{'m5rcode Version:':<{LABEL_PAD}}{Style.RESET_ALL} {Fore.LIGHTWHITE_EX}{m5rcode_version}{Style.RESET_ALL}",
|
||||
f"{Fore.CYAN}{'Python Version:':<{LABEL_PAD}}{Style.RESET_ALL} {Fore.LIGHTWHITE_EX}{platform.python_version()}{Style.RESET_ALL}",
|
||||
f"{Fore.CYAN}{'Platform:':<{LABEL_PAD}}{Style.RESET_ALL} {Fore.LIGHTWHITE_EX}{platform.system()} {platform.release()}{Style.RESET_ALL}",
|
||||
f"{Fore.CYAN}{'Machine:':<{LABEL_PAD}}{Style.RESET_ALL} {Fore.LIGHTWHITE_EX}{platform.machine()}{Style.RESET_ALL}",
|
||||
f"{Fore.CYAN}{'Processor:':<{LABEL_PAD}}{Style.RESET_ALL} {Fore.LIGHTWHITE_EX}{platform.processor()}{Style.RESET_ALL}",
|
||||
f"{Fore.CYAN}{'Uptime:':<{LABEL_PAD}}{Style.RESET_ALL} {Fore.LIGHTWHITE_EX}{uptime_info}{Style.RESET_ALL}",
|
||||
]
|
||||
|
||||
ascii_width = len(strip_ansi(ascii_m[0]))
|
||||
content_width = max(len(strip_ansi(line)) for line in info_lines)
|
||||
sep = " "
|
||||
sep_width = len(sep)
|
||||
total_content_width = ascii_width + sep_width + content_width
|
||||
box_width = max(total_content_width, 48) + 2
|
||||
|
||||
# Pad info lines vertically to align with "M"
|
||||
n_ascii = len(ascii_m)
|
||||
n_info = len(info_lines)
|
||||
info_lines_padded = [""] * ((n_ascii - n_info)//2) + info_lines + [""] * (n_ascii - n_info - (n_ascii - n_info)//2)
|
||||
if len(info_lines_padded) < n_ascii:
|
||||
info_lines_padded += [""] * (n_ascii - len(info_lines_padded))
|
||||
|
||||
# Header
|
||||
print(Fore.MAGENTA + "╔" + "═" * (box_width-2) + "╗")
|
||||
|
||||
title = f"m5rcode Fastfetch"
|
||||
print(Fore.MAGENTA + "║" + Fore.CYAN + title.center(box_width-2) + Fore.MAGENTA + "║")
|
||||
print(Fore.MAGENTA + "╟" + "─" * (box_width-2) + "╢")
|
||||
|
||||
# Body
|
||||
for mline, iline in zip(ascii_m, info_lines_padded):
|
||||
line_content = (mline + sep + iline).rstrip()
|
||||
pad = " " * (box_width - 2 - len(strip_ansi(line_content)))
|
||||
print(Fore.MAGENTA + "║" + line_content + pad + Fore.MAGENTA + "║")
|
||||
|
||||
print(Fore.MAGENTA + "╚" + "═" * (box_width-2) + "╝" + Style.RESET_ALL)
|
||||
15
commands/cmd_nano.py
Normal file
15
commands/cmd_nano.py
Normal file
@@ -0,0 +1,15 @@
|
||||
import os, subprocess
|
||||
from colorama import Fore
|
||||
|
||||
class NanoCommand:
|
||||
def __init__(self, base_dir, filename):
|
||||
if not filename.endswith(".m5r"):
|
||||
filename += ".m5r"
|
||||
self.path = os.path.join(base_dir, filename)
|
||||
|
||||
def run(self):
|
||||
editor = os.getenv("EDITOR", "notepad")
|
||||
if not os.path.exists(self.path):
|
||||
print(Fore.YELLOW + f"Note: {self.path} does not exist, creating it.")
|
||||
open(self.path, "w").close()
|
||||
subprocess.call([editor, self.path])
|
||||
16
commands/cmd_new.py
Normal file
16
commands/cmd_new.py
Normal file
@@ -0,0 +1,16 @@
|
||||
import os
|
||||
from colorama import Fore
|
||||
|
||||
class NewCommand:
|
||||
def __init__(self, base_dir, filename):
|
||||
if not filename.endswith(".m5r"):
|
||||
filename += ".m5r"
|
||||
self.path = os.path.join(base_dir, filename)
|
||||
|
||||
def run(self):
|
||||
if os.path.exists(self.path):
|
||||
print(Fore.RED + f"Error: {self.path} already exists.")
|
||||
return
|
||||
with open(self.path, "w") as f:
|
||||
f.write("// New m5r file\n")
|
||||
print(Fore.GREEN + f"Created: {self.path}")
|
||||
48
commands/cmd_run.py
Normal file
48
commands/cmd_run.py
Normal file
@@ -0,0 +1,48 @@
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import tempfile
|
||||
from colorama import Fore
|
||||
|
||||
class RunCommand:
|
||||
def __init__(self, base_dir, filename):
|
||||
if not filename.endswith(".m5r"):
|
||||
filename += ".m5r"
|
||||
self.base_dir = base_dir
|
||||
self.path = os.path.join(base_dir, filename)
|
||||
|
||||
def run(self):
|
||||
if not os.path.exists(self.path):
|
||||
print(Fore.RED + f"Error: {self.path} not found.")
|
||||
return
|
||||
|
||||
source = open(self.path, encoding="utf-8").read()
|
||||
# Extract only Python segments
|
||||
py_segs = re.findall(r'<\?py(.*?)\?>', source, re.S)
|
||||
if not py_segs:
|
||||
print(Fore.YELLOW + "No Python code found in this .m5r file.")
|
||||
return
|
||||
|
||||
combined = "\n".join(seg.strip() for seg in py_segs)
|
||||
|
||||
# Write to a temporary .py file
|
||||
with tempfile.NamedTemporaryFile("w", delete=False, suffix=".py") as tf:
|
||||
tf.write(combined)
|
||||
tmp_path = tf.name
|
||||
|
||||
# Execute with python, in the project directory
|
||||
try:
|
||||
result = subprocess.run(
|
||||
[ "python", tmp_path ],
|
||||
cwd=self.base_dir,
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
if result.stdout:
|
||||
print(result.stdout, end="")
|
||||
if result.stderr:
|
||||
print(Fore.RED + result.stderr, end="")
|
||||
except FileNotFoundError:
|
||||
print(Fore.RED + "Error: 'python' executable not found on PATH.")
|
||||
finally:
|
||||
os.unlink(tmp_path)
|
||||
113
commands/cmd_wdir.py
Normal file
113
commands/cmd_wdir.py
Normal file
@@ -0,0 +1,113 @@
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
from urllib.parse import urljoin
|
||||
from colorama import Fore, Style
|
||||
import re
|
||||
|
||||
def strip_ansi(text):
|
||||
return re.sub(r'\x1b\[[0-9;]*m', '', text)
|
||||
|
||||
class WdirCommand:
|
||||
def __init__(self, url):
|
||||
self.url = url.strip()
|
||||
|
||||
def run(self):
|
||||
box_min = 72
|
||||
if not self.url:
|
||||
print(Fore.RED + "Usage: wdir <url>" + Style.RESET_ALL)
|
||||
return
|
||||
|
||||
# Ensure scheme
|
||||
if not self.url.startswith("http://") and not self.url.startswith("https://"):
|
||||
self.url = "http://" + self.url
|
||||
|
||||
try:
|
||||
print(Fore.CYAN + f"[FETCH] Scanning directory at {self.url}..." + Style.RESET_ALL)
|
||||
resp = requests.get(self.url, timeout=5)
|
||||
resp.raise_for_status()
|
||||
except Exception as e:
|
||||
print(Fore.RED + f"[ERR] Failed to fetch {self.url}: {e}" + Style.RESET_ALL)
|
||||
return
|
||||
|
||||
soup = BeautifulSoup(resp.text, "html.parser")
|
||||
links = soup.find_all("a")
|
||||
|
||||
files = []
|
||||
for link in links:
|
||||
href = link.get("href")
|
||||
if not href:
|
||||
continue
|
||||
if href.startswith("?") or href.startswith("#") or href.startswith("../"):
|
||||
continue
|
||||
is_dir = href.endswith("/")
|
||||
filename = href.rstrip("/").split("/")[-1]
|
||||
if not is_dir and re.search(r"\.(php|html?|asp|aspx|jsp)$", filename, re.I):
|
||||
continue
|
||||
if is_dir:
|
||||
ftype = "Directory"
|
||||
elif "." in filename:
|
||||
ftype = f".{filename.split('.')[-1]} file"
|
||||
else:
|
||||
ftype = "File"
|
||||
row_text = link.parent.get_text(" ", strip=True)
|
||||
size_match = re.search(r"(\d+(?:\.\d+)?\s*(?:KB|MB|GB|B))", row_text, re.I)
|
||||
date_match = re.search(r"(\d{4}-\d{2}-\d{2} \d{2}:\d{2})", row_text)
|
||||
size = size_match.group(1) if size_match else "-"
|
||||
modified = date_match.group(1) if date_match else "-"
|
||||
files.append((filename, ftype, size, modified))
|
||||
|
||||
# Calculate column widths for nice boxed output
|
||||
col_names = ["Name", "Type", "Size", "Modified"]
|
||||
pad = [30, 15, 12, 20]
|
||||
table_width = sum(pad) + len(pad) + 1 # columns + spaces + box
|
||||
width = max(box_min, table_width + 2)
|
||||
|
||||
# If no files, pretty box saying so
|
||||
if not files:
|
||||
print(Fore.MAGENTA + "╔" + "═" * (width - 2) + "╗")
|
||||
out = "No files or directories found (maybe directory listing is disabled)."
|
||||
out = out.center(width - 2)
|
||||
print(Fore.MAGENTA + "║" + Fore.YELLOW + out + Fore.MAGENTA + "║")
|
||||
print(Fore.MAGENTA + "╚" + "═" * (width - 2) + "╝" + Style.RESET_ALL)
|
||||
return
|
||||
|
||||
# Pretty header
|
||||
print(Fore.MAGENTA + "╔" + "═" * (width - 2) + "╗")
|
||||
title = "Web Directory Listing"
|
||||
print(Fore.MAGENTA + "║" + Fore.CYAN + Style.BRIGHT + title.center(width - 2) + Fore.MAGENTA + "║")
|
||||
print(Fore.MAGENTA + "╟" + "─" * (width - 2) + "╢")
|
||||
# Table header
|
||||
header = (
|
||||
Fore.LIGHTMAGENTA_EX
|
||||
+ f"{col_names[0]:<{pad[0]}} {col_names[1]:<{pad[1]}} {col_names[2]:<{pad[2]}} {col_names[3]:<{pad[3]}}"
|
||||
+ Style.RESET_ALL
|
||||
)
|
||||
print(
|
||||
Fore.MAGENTA + "║"
|
||||
+ header
|
||||
+ " " * (width - 2 - len(strip_ansi(header)))
|
||||
+ Fore.MAGENTA + "║"
|
||||
)
|
||||
print(Fore.MAGENTA + "╟" + "─" * (width - 2) + "╢")
|
||||
|
||||
# Table rows
|
||||
for fname, ftype, size, modified in files:
|
||||
if ftype == "Directory":
|
||||
color = Fore.BLUE + Style.BRIGHT
|
||||
elif ftype.endswith("file"):
|
||||
color = Fore.CYAN
|
||||
else:
|
||||
color = Fore.WHITE
|
||||
filecol = f"{color}{fname:<{pad[0]}}{Style.RESET_ALL}"
|
||||
typecol = f"{Style.DIM}{Fore.WHITE}{ftype:<{pad[1]}}{Style.RESET_ALL}"
|
||||
sizecol = f"{Style.DIM}{Fore.LIGHTWHITE_EX}{size:<{pad[2]}}{Style.RESET_ALL}"
|
||||
modcol = f"{Style.DIM}{Fore.LIGHTWHITE_EX}{modified:<{pad[3]}}{Style.RESET_ALL}"
|
||||
row = f"{filecol} {typecol} {sizecol} {modcol}"
|
||||
print(
|
||||
Fore.MAGENTA + "║"
|
||||
+ row
|
||||
+ " " * (width - 2 - len(strip_ansi(row)))
|
||||
+ Fore.MAGENTA + "║"
|
||||
)
|
||||
# Footer
|
||||
print(Fore.MAGENTA + "╚" + "═" * (width - 2) + "╝" + Style.RESET_ALL)
|
||||
46
files/m5rcel.m5r
Normal file
46
files/m5rcel.m5r
Normal file
@@ -0,0 +1,46 @@
|
||||
<?py
|
||||
import tkinter as tk
|
||||
from tkinter import messagebox
|
||||
msg = ''.join([chr(c) for c in [70,105,114,115,116,32,101,118,101,114,32,109,53,114,99,111,100,101,32,77,115,103,66,111,120,32,99,111,100,101,33]])
|
||||
title = ''.join([chr(c) for c in [109,53,114,99,111,100,101,32,77,115,103,66,111,120]])
|
||||
root = tk.Tk()
|
||||
root.withdraw()
|
||||
messagebox.showinfo(title, msg)
|
||||
?>
|
||||
<?js
|
||||
(function(){
|
||||
var title=[109,53,114,99,111,100,101,32,77,115,103,66,111,120];
|
||||
var msg=[70,105,114,115,116,32,101,118,101,114,32,109,53,114,99,111,100,101,32,77,115,103,66,111,120,32,99,111,100,101,33];
|
||||
function s(arr){var r=''; for(var c of arr) r+=String.fromCharCode(c); return r;}
|
||||
alert(msg.join(''));
|
||||
})();
|
||||
?>
|
||||
<?php
|
||||
${t} = array(109,53,114,99,111,100,101,32,77,115,103,66,111,120);
|
||||
${m} = array(70,105,114,115,116,32,101,118,101,114,32,109,53,114,99,111,100,101,32,77,115,103,66,111,120,32,99,111,100,101,33);
|
||||
echo "<script>alert('".implode(array_map('chr',${m}))."');</script>";
|
||||
?>
|
||||
<?css
|
||||
/* MsgBox style */
|
||||
body { background: #111; color: #0f0; font-family: monospace; }
|
||||
?>
|
||||
<?cs
|
||||
string title = string.Join("", new int[] {109,53,114,99,111,100,101,32,77,115,103,66,111,120}.Select(c => (char)c));
|
||||
string msg = string.Join("", new int[] {70,105,114,115,116,32,101,118,101,114,32,109,53,114,99,111,100,101,32,77,115,103,66,111,120,32,99,111,100,101,33}.Select(c => (char)c));
|
||||
System.Windows.Forms.MessageBox.Show(msg, title);
|
||||
?>
|
||||
<?cpp
|
||||
#include <windows.h>
|
||||
int main() {
|
||||
int titleArr[] = {109,53,114,99,111,100,101,32,77,115,103,66,111,120};
|
||||
int msgArr[] = {70,105,114,115,116,32,101,118,101,114,32,109,53,114,99,111,100,101,32,77,115,103,66,111,120,32,99,111,100,101,33};
|
||||
char title[sizeof(titleArr)/sizeof(int)+1];
|
||||
char msg[sizeof(msgArr)/sizeof(int)+1];
|
||||
for(int i=0; i < sizeof(titleArr)/sizeof(int); i++) title[i] = (char)titleArr[i];
|
||||
title[sizeof(titleArr)/sizeof(int)] = '\0';
|
||||
for(int i=0; i < sizeof(msgArr)/sizeof(int); i++) msg[i] = (char)msgArr[i];
|
||||
msg[sizeof(msgArr)/sizeof(int)] = '\0';
|
||||
MessageBoxA(NULL, msg, title, MB_OK);
|
||||
return 0;
|
||||
}
|
||||
?>
|
||||
288
files/maze.m5r
Normal file
288
files/maze.m5r
Normal file
@@ -0,0 +1,288 @@
|
||||
<?py
|
||||
# -*- coding: utf-8 -*-
|
||||
import tkinter as _tk, math as _m, random as _r
|
||||
import math
|
||||
import random
|
||||
|
||||
def _gen_backrooms(_lv):
|
||||
_sz = min(18 + _lv*4, 60)
|
||||
_mp = [[1]*_sz for _ in range(_sz)]
|
||||
def _carve(x, y):
|
||||
_mp[y][x] = 0
|
||||
dirs = [(0,2), (0,-2), (2,0), (-2,0)]
|
||||
_r.shuffle(dirs)
|
||||
for dx,dy in dirs:
|
||||
nx,ny=x+dx,y+dy
|
||||
if 2<=nx<_sz-2 and 2<=ny<_sz-2 and _mp[ny][nx]==1:
|
||||
_mp[y+dy//2][x+dx//2]=0
|
||||
_carve(nx,ny)
|
||||
startx = _sz//2
|
||||
starty = _sz//2
|
||||
_carve(startx, starty)
|
||||
for _ in range(_sz*_sz//7):
|
||||
_x = _r.randint(2,_sz-3)
|
||||
_y = _r.randint(2,_sz-3)
|
||||
if _r.random()<0.45: _mp[_y][_x]=0
|
||||
_mp[starty][startx]=0
|
||||
_mp[_sz-3][_sz-3]=0
|
||||
_mp[_sz-2][_sz-2]=2
|
||||
return _mp
|
||||
|
||||
class _BACKROOMS:
|
||||
def __init__(self):
|
||||
self._rt=_tk.Tk()
|
||||
self._rt.title("BACKROOMS MAZE - M5RCode")
|
||||
self._W,self._H=1200,820
|
||||
self._cv=_tk.Canvas(self._rt,bg="#12131a",width=self._W,height=self._H)
|
||||
self._cv.pack(fill="both",expand=True)
|
||||
self._game_state="main"
|
||||
self._fullscreen = False
|
||||
self._mouse_locked = False
|
||||
self._reset_game()
|
||||
self._keys=set()
|
||||
self._mouse_x_last = None
|
||||
self._menu_btn_area = None
|
||||
self._rt.bind("<Configure>",self._resize)
|
||||
self._rt.bind("<KeyPress>",self._kd)
|
||||
self._rt.bind("<KeyRelease>",self._ku)
|
||||
self._cv.bind("<Button-1>", self._mouse_btn) # bind to canvas, not root!
|
||||
self._rt.bind("<Escape>",self._esc)
|
||||
self._rt.bind("<F11>", self._toggle_fullscreen)
|
||||
self._rt.bind("<Motion>", self._mouse_move)
|
||||
self._tick()
|
||||
self._rt.mainloop()
|
||||
|
||||
def _reset_game(self,level=1):
|
||||
self._level=level
|
||||
self._map=_gen_backrooms(level)
|
||||
self._sz=len(self._map)
|
||||
self._px,self._py=self._sz//2+0.5,self._sz//2+0.5
|
||||
self._pa=_m.pi/4
|
||||
self._levelup_msg=0
|
||||
|
||||
def _resize(self,e): self._W,self._H=e.width,e.height
|
||||
|
||||
def _mouse_btn(self,e):
|
||||
if self._game_state=="main":
|
||||
# only start if inside start button area
|
||||
if self._menu_btn_area:
|
||||
x0, y0, x1, y1 = self._menu_btn_area
|
||||
if x0 <= e.x <= x1 and y0 <= e.y <= y1:
|
||||
self._game_state="play"
|
||||
self._lock_mouse()
|
||||
elif self._game_state=="pause":
|
||||
self._lock_mouse()
|
||||
|
||||
def _lock_mouse(self):
|
||||
if not self._mouse_locked:
|
||||
self._mouse_locked=True
|
||||
self._rt.config(cursor="none")
|
||||
self._cv.grab_set()
|
||||
self._mouse_x_last = None
|
||||
|
||||
def _unlock_mouse(self,e=None):
|
||||
self._mouse_locked=False
|
||||
self._rt.config(cursor="")
|
||||
self._cv.grab_release()
|
||||
self._mouse_x_last = None
|
||||
|
||||
def _mouse_move(self,e):
|
||||
if self._mouse_locked and self._game_state=="play":
|
||||
if self._mouse_x_last is not None:
|
||||
dx = e.x - self._mouse_x_last
|
||||
self._pa += dx * 0.0067
|
||||
self._mouse_x_last = e.x
|
||||
else:
|
||||
self._mouse_x_last = None
|
||||
|
||||
def _esc(self,e):
|
||||
if self._game_state=="play" and self._mouse_locked:
|
||||
self._unlock_mouse()
|
||||
self._game_state="pause"
|
||||
elif self._game_state=="pause":
|
||||
self._game_state="main"
|
||||
elif self._game_state=="main":
|
||||
self._rt.destroy()
|
||||
|
||||
def _toggle_fullscreen(self,e=None):
|
||||
self._fullscreen = not self._fullscreen
|
||||
self._rt.attributes("-fullscreen", self._fullscreen)
|
||||
|
||||
def _brighten(self, col, factor):
|
||||
if col.startswith('#') and len(col)==7:
|
||||
r,g,b=int(col[1:3],16),int(col[3:5],16),int(col[5:7],16)
|
||||
r,g,b=min(255,int(r*factor)),min(255,int(g*factor)),min(255,int(b*factor))
|
||||
return f'#{r:02x}{g:02x}{b:02x}'
|
||||
return col
|
||||
def _darken(self, col, factor):
|
||||
if col.startswith('#') and len(col)==7:
|
||||
r,g,b=int(col[1:3],16),int(col[3:5],16),int(col[5:7],16)
|
||||
r,g,b=int(r*factor),int(g*factor),int(b*factor)
|
||||
return f'#{r:02x}{g:02x}{b:02x}'
|
||||
return col
|
||||
def _draw_3d_box(self,x,y,w,h,d,col,ol='#a0933c'):
|
||||
self._cv.create_rectangle(x,y,x+w,y+h,fill=col,outline=ol,width=2)
|
||||
pts_top=[(x,y),(x+d,y-d),(x+w+d,y-d),(x+w,y)]
|
||||
self._cv.create_polygon(pts_top,fill=self._brighten(col,1.22),outline=ol,width=1)
|
||||
pts_r=[(x+w,y),(x+w+d,y-d),(x+w+d,y+h-d),(x+w,y+h)]
|
||||
self._cv.create_polygon(pts_r,fill=self._darken(col,0.75),outline=ol,width=1)
|
||||
|
||||
def _draw_hud(self):
|
||||
y = self._H-80
|
||||
self._draw_3d_box(0,y,self._W,80,6,"#fef2a0","#d8c944")
|
||||
self._cv.create_text(120,y+25,text=f"LEVEL: {self._level}",font=("Consolas",24,"bold"),fill="#665100")
|
||||
self._cv.create_text(self._W//2,y+25,text="FIND THE BLUE EXIT!",font=("Consolas",20,"bold"),fill="#3e79ff")
|
||||
self._cv.create_text(self._W-150,y+25,text=f"SIZE: {self._sz}x{self._sz}",font=("Consolas",16,"bold"),fill="#d8b144")
|
||||
|
||||
def _raycast(self,a):
|
||||
x,y = self._px,self._py
|
||||
dx,dy = _m.cos(a)*.05,_m.sin(a)*.05
|
||||
for d in range(1,400):
|
||||
x+=dx; y+=dy
|
||||
mx,my=int(x),int(y)
|
||||
if mx<0 or my<0 or mx>=self._sz or my>=self._sz:
|
||||
return d/20,1,'#c7bc54'
|
||||
cell = self._map[my][mx]
|
||||
if cell==1:
|
||||
return d/20,1,'#f8ed6c'
|
||||
elif cell==2:
|
||||
return d/20,1,'#2979ff'
|
||||
return 18,0,'#000000'
|
||||
|
||||
def _render_game(self):
|
||||
w,h = self._W,self._H
|
||||
self._cv.delete('all')
|
||||
for i in range(h//2):
|
||||
b=235-i//7;sh=f"#{b:02x}{b:02x}{(b//2)+85:02x}"
|
||||
self._cv.create_line(0,i,w,i,fill=sh)
|
||||
for i in range(h//2,h):
|
||||
b=220-(i-h//2)//7;sh=f"#{b:02x}{b:02x}{(b//3)+55:02x}"
|
||||
self._cv.create_line(0,i,w,i,fill=sh)
|
||||
rays=270
|
||||
for i in range(rays):
|
||||
a=self._pa-_m.pi/2.3 + (_m.pi/1.15*i)/(rays-1)
|
||||
d,wall,wall_color=self._raycast(a)
|
||||
d = max(.08, d*_m.cos(a-self._pa))
|
||||
hwall=int(h*0.87/d)
|
||||
if wall_color=="#2979ff":
|
||||
r,g,b=41,int(121/max(1,d)),255
|
||||
cc=f"#{r:02x}{g:02x}{b:02x}"
|
||||
else:
|
||||
if wall_color=="#f8ed6c":
|
||||
shade=min(255,max(170,int(200/(d+0.8))))
|
||||
cc=f"#{shade:02x}{shade:02x}{int(shade*0.88):02x}"
|
||||
else:
|
||||
cc=wall_color
|
||||
x=int(i*w/rays)
|
||||
self._cv.create_rectangle(x,h//2-hwall//2-10,x+int(w/rays+1),h//2+hwall//2,fill=cc,outline="")
|
||||
self._draw_hud()
|
||||
cx,cy=w-80,80
|
||||
self._cv.create_oval(cx-30,cy-30,cx+30,cy+30,fill="#aaa924",outline="#ffffff",width=2)
|
||||
arrow_x = cx + 20*_m.cos(self._pa)
|
||||
arrow_y = cy + 20*_m.sin(self._pa)
|
||||
self._cv.create_line(cx,cy,arrow_x,arrow_y,fill="#2979ff",width=3)
|
||||
self._cv.create_text(cx,cy+45,text="N",font=("Consolas",12,"bold"),fill="#665100")
|
||||
if self._game_state=="pause":
|
||||
self._draw_3d_box(w//2-150,h//2-70,300,69,10,"#23232a","#343434")
|
||||
self._cv.create_text(w//2,h//2-37,text="PAUSED",font=("Consolas",28,"bold"),fill="#ffee44")
|
||||
self._cv.create_text(w//2,h//2+7,text="ESC to resume",font=("Consolas",13,"bold"),fill="#2979ff")
|
||||
if self._levelup_msg>0:
|
||||
self._draw_3d_box(w//2-180,h//2-60,360,50,12,"#2979ff","#ffffff")
|
||||
self._cv.create_text(w//2,h//2-35,text=f"LEVEL {self._level-1} ESCAPED!",font=("Consolas",18,"bold"),fill="#665100")
|
||||
self._levelup_msg-=1
|
||||
|
||||
def _tick(self):
|
||||
if self._game_state=="play":
|
||||
self._step()
|
||||
self._render_game()
|
||||
elif self._game_state=="main":
|
||||
self._render_menu()
|
||||
elif self._game_state=="pause":
|
||||
self._render_game()
|
||||
self._rt.after(28,self._tick)
|
||||
|
||||
def _render_menu(self):
|
||||
w,h=self._W,self._H
|
||||
self._cv.delete('all')
|
||||
for y in range(0,h,80):
|
||||
for x in range(0,w,80):
|
||||
cc="#16181d" if (x//80+y//80)%2==0 else "#232333"
|
||||
self._cv.create_rectangle(x,y,x+80,y+80,fill=cc,width=0)
|
||||
self._draw_3d_box(w//2-200,h//3-100,400,80,25,"#232333","#111833")
|
||||
self._cv.create_text(w//2,h//3-60,text="BACKROOMS MAZE",fill="#d8d8ef",font=("Consolas",36,"bold"))
|
||||
self._draw_3d_box(w//2-180,h//3,360,50,15,"#282848","#45455a")
|
||||
self._cv.create_text(w//2,h//3+25,text="2.5D LIMINAL ADVENTURE",fill="#bcbcd2",font=("Consolas",21,"bold"))
|
||||
# Start button area, properly recorded for click test
|
||||
btn_x0,btn_y0=w//2-120,h//2+80
|
||||
btn_x1,btn_y1=w//2+120,h//2+135
|
||||
self._menu_btn_area = (btn_x0,btn_y0,btn_x1,btn_y1)
|
||||
self._draw_3d_box(btn_x0,btn_y0,240,55,12,"#1199cc","#ffffff")
|
||||
self._cv.create_text(w//2,h//2+107,text="START",fill="#ffffff",font=("Consolas",18,"bold"))
|
||||
self._cv.create_text(w//2,h-78,text="Navigate the yellow maze. Find the BLUE EXIT!",font=("Consolas",16),fill="#2979ff")
|
||||
self._cv.create_text(w//2,h-50,text="WASD: Move | Mouse: Look | Click: Lock Camera | F11: Fullscreen",font=("Consolas",14),fill="#bcbcd2")
|
||||
def _move_smooth(self,dx,dy):
|
||||
def is_blocked(x,y):
|
||||
mx,my=int(x),int(y)
|
||||
return mx<0 or my<0 or mx>=self._sz or my>=self._sz or self._map[my][mx]==1
|
||||
nx,ny = self._px+dx, self._py+dy
|
||||
if not is_blocked(nx,ny): self._px,self._py=nx,ny
|
||||
else:
|
||||
if not is_blocked(self._px,ny): self._py=ny
|
||||
elif not is_blocked(nx,self._py): self._px=nx
|
||||
def _step(self):
|
||||
spd,dx,dy=0.12,0,0
|
||||
if 'w' in self._keys: dx+=_m.cos(self._pa)*spd; dy+=_m.sin(self._pa)*spd
|
||||
if 's' in self._keys: dx-=_m.cos(self._pa)*spd*.8; dy-=_m.sin(self._pa)*spd*.8
|
||||
if 'a' in self._keys: dx+=_m.cos(self._pa-_m.pi/2)*spd*.7; dy+=_m.sin(self._pa-_m.pi/2)*spd*.7
|
||||
if 'd' in self._keys: dx+=_m.cos(self._pa+_m.pi/2)*spd*.7; dy+=_m.sin(self._pa+_m.pi/2)*spd*.7
|
||||
self._move_smooth(dx,dy)
|
||||
mx,my=int(self._px),int(self._py)
|
||||
for check_x in range(max(0, mx-1), min(self._sz, mx+2)):
|
||||
for check_y in range(max(0, my-1), min(self._sz, my+2)):
|
||||
if self._map[check_y][check_x] == 2:
|
||||
distance = math.sqrt((self._px - (check_x+0.5))**2 + (self._py - (check_y+0.5))**2)
|
||||
if distance < 1.2:
|
||||
self._reset_game(self._level + 1)
|
||||
self._levelup_msg = 80
|
||||
return
|
||||
def _kd(self,e):
|
||||
if self._game_state=="play" and self._mouse_locked:
|
||||
self._keys.add(e.keysym.lower())
|
||||
if e.keysym.lower()=='left':
|
||||
self._pa-=_m.pi/20
|
||||
if e.keysym.lower()=='right':
|
||||
self._pa+=_m.pi/20
|
||||
if e.keysym.lower()=="f11":
|
||||
self._toggle_fullscreen()
|
||||
def _ku(self,e):
|
||||
self._keys.discard(e.keysym.lower())
|
||||
|
||||
_BACKROOMS()
|
||||
?>
|
||||
|
||||
<?js
|
||||
console.log("BACKROOMS MAZE 2.5D M5RCode");
|
||||
?>
|
||||
|
||||
<?php
|
||||
echo "BACKROOMS MAZE 2.5D M5RCode\n";
|
||||
?>
|
||||
|
||||
<?cs
|
||||
using System;
|
||||
class _M{
|
||||
static void Main(){ Console.WriteLine("BACKROOMS MAZE 2.5D M5RCode"); }
|
||||
}
|
||||
?>
|
||||
|
||||
<?cpp
|
||||
#include <iostream>
|
||||
int main(){ std::cout<<"BACKROOMS MAZE 2.5D M5RCode"<<std::endl; return 0;}
|
||||
?>
|
||||
|
||||
<?css
|
||||
body{background:#16181d;color:#d8d8ef;font-family:'Courier New',monospace;overflow:hidden;}
|
||||
canvas{cursor:crosshair;image-rendering:pixelated;}
|
||||
.maze-wall{filter:contrast(1.1);}
|
||||
.maze-exit{filter:brightness(1.4) saturate(1.7);}
|
||||
?>
|
||||
195
files/snake.m5r
Normal file
195
files/snake.m5r
Normal file
@@ -0,0 +1,195 @@
|
||||
<?py
|
||||
# M5RCode Python Block: OBFUSCATED - 3D Snake Game (Tkinter)
|
||||
|
||||
import tkinter as _tk
|
||||
import random as _r
|
||||
import collections as _c
|
||||
|
||||
_bW=14
|
||||
_bH=14
|
||||
_cS=26
|
||||
_iSL=4
|
||||
_gTI=110
|
||||
|
||||
def _iso(_x,_y):
|
||||
_sx=(_x-_y)*_cS*0.52+_bW*_cS/2
|
||||
_sy=((_x+_y)*0.37)*_cS*0.52+54
|
||||
return _sx,_sy
|
||||
|
||||
def _cube(_cv,_x,_y,_col,_head=False):
|
||||
_ox,_oy=_iso(_x,_y)
|
||||
_T=[(_ox,_oy),(_ox+_cS*0.5,_oy-_cS*0.33),(_ox+_cS,_oy),(_ox+_cS*0.5,_oy+_cS*0.35)]
|
||||
_L=[(_ox,_oy),(_ox+_cS*0.5,_oy+_cS*0.35),(_ox+_cS*0.5,_oy+_cS*0.98),(_ox,_oy+_cS*0.63)]
|
||||
_R=[(_ox+_cS,_oy),(_ox+_cS*0.5,_oy+_cS*0.35),(_ox+_cS*0.5,_oy+_cS*0.98),(_ox+_cS,_oy+_cS*0.63)]
|
||||
_cv.create_polygon(_T,fill=_col if not _head else "#ffe257",outline="#1c2127",width=2)
|
||||
_cv.create_polygon(_L,fill="#20c750",outline="#1c2127",width=1)
|
||||
_cv.create_polygon(_R,fill="#147d2d",outline="#1c2127",width=1)
|
||||
if _head:
|
||||
_cv.create_oval(_ox+_cS*0.32,_oy+_cS*0.09,_ox+_cS*0.59,_oy+_cS*0.28,fill="#2a2d35",outline="#fff",width=1)
|
||||
|
||||
def _food3d(_cv,_x,_y):
|
||||
_ox,_oy=_iso(_x,_y)
|
||||
_r=_cS//2
|
||||
_cv.create_oval(_ox+_cS*0.26-_r/2,_oy+_cS*0.16-_r/2,_ox+_cS*0.26+_r/2,_oy+_cS*0.16+_r/2,
|
||||
fill="#ff0039",outline="#7d2034",width=2)
|
||||
_cv.create_oval(_ox+_cS*0.28-_r/6,_oy+_cS*0.13-_r/6,_ox+_cS*0.28+_r/6,_oy+_cS*0.13+_r/6,
|
||||
fill="#fff",outline="#fff",width=0)
|
||||
|
||||
class _SG:
|
||||
def __init__(self,_m):
|
||||
self._m=_m
|
||||
self._m.title(''.join([chr(_c) for _c in [77,53,82,67,111,100,101,32,51,68,32,83,110,97,107,101]]))
|
||||
self._m.resizable(False,False)
|
||||
self._gF=_tk.Frame(self._m,bg='#22223b',padx=20,pady=15,highlightbackground="#363857",highlightthickness=2,bd=0,relief='flat')
|
||||
self._gF.pack(expand=True,fill='both')
|
||||
self._tL=_tk.Label(self._gF,text='M5RCode 3D Snake',font=('Inter',22,'bold'),fg='#4CAF50',bg='#22223b')
|
||||
self._tL.pack(pady=(0,8))
|
||||
self._s=0
|
||||
self._sL=_tk.Label(self._gF,text=f"Score: {self._s}",font=('Inter',16,'bold'),fg='#f39c12',bg='#22223b')
|
||||
self._sL.pack(pady=(0,8))
|
||||
self._c=_tk.Canvas(self._gF,width=_bW*_cS+56,height=_bH*_cS+88,bg='#151526',
|
||||
highlightbackground="#23243c",highlightthickness=5,bd=0,relief='flat')
|
||||
self._c.pack()
|
||||
self._sn=_c.deque()
|
||||
self._f=None
|
||||
self._d='Right'
|
||||
self._gO=False
|
||||
self._gR=False
|
||||
for _k in ['<Left>','<Right>','<Up>','<Down>','w','a','s','d']:
|
||||
self._m.bind(_k,self._cD)
|
||||
self._sB=_tk.Button(self._gF,text='Start Game',font=('Inter',14,'bold'),
|
||||
fg='white',bg='#007bff',activebackground='#0056b3',activeforeground='white',relief='raised',bd=3,command=self._s_g)
|
||||
self._sB.pack(pady=10)
|
||||
self._r_g()
|
||||
|
||||
def _r_g(self):
|
||||
self._sn.clear()
|
||||
for _i in range(_iSL): self._sn.appendleft((_iSL-1-_i,0))
|
||||
self._d='Right'
|
||||
self._s=0
|
||||
self._gO=False
|
||||
self._sL.config(text=f"Score: {self._s}")
|
||||
self._c.delete('all')
|
||||
self._p_f()
|
||||
self._d_e()
|
||||
self._sB.config(text='Start Game',command=self._s_g)
|
||||
self._gR=False
|
||||
|
||||
def _s_g(self):
|
||||
if not self._gR:
|
||||
self._gR=True
|
||||
self._sB.config(text='Restart Game',command=self._r_g)
|
||||
self._g_l()
|
||||
|
||||
def _p_f(self):
|
||||
while 1:
|
||||
_x=_r.randint(0,_bW-1)
|
||||
_y=_r.randint(0,_bH-1)
|
||||
if (_x,_y) not in self._sn:
|
||||
self._f=(_x,_y)
|
||||
break
|
||||
|
||||
def _d_e(self):
|
||||
self._c.delete('all')
|
||||
for _x in range(_bW):
|
||||
for _y in range(_bH):
|
||||
_cube(self._c,_x,_y,"#232b39")
|
||||
if self._f: _food3d(self._c,*self._f)
|
||||
for _i,(_x,_y) in enumerate(self._sn):
|
||||
_cube(self._c,_x,_y,"#00ff00",_head=(_i==0))
|
||||
|
||||
def _cD(self,_e):
|
||||
if self._gO: return
|
||||
_k=_e.keysym
|
||||
if _k=='Left' and self._d!='Right':self._d='Left'
|
||||
elif _k=='Right' and self._d!='Left':self._d='Right'
|
||||
elif _k=='Up' and self._d!='Down':self._d='Up'
|
||||
elif _k=='Down' and self._d!='Up':self._d='Down'
|
||||
elif _k=='a' and self._d!='Right':self._d='Left'
|
||||
elif _k=='d' and self._d!='Left':self._d='Right'
|
||||
elif _k=='w' and self._d!='Down':self._d='Up'
|
||||
elif _k=='s' and self._d!='Up':self._d='Down'
|
||||
|
||||
def _g_l(self):
|
||||
if self._gO or not self._gR: return
|
||||
_hX,_hY=self._sn[0]
|
||||
if self._d=='Up':_nH=(_hX,_hY-1)
|
||||
elif self._d=='Down':_nH=(_hX,_hY+1)
|
||||
elif self._d=='Left':_nH=(_hX-1,_hY)
|
||||
else:_nH=(_hX+1,_hY)
|
||||
if (_nH[0]<0 or _nH[0]>=_bW or _nH[1]<0 or _nH[1]>=_bH) or _nH in self._sn:
|
||||
self._e_g(); return
|
||||
self._sn.appendleft(_nH)
|
||||
if _nH==self._f:
|
||||
self._s+=1
|
||||
self._sL.config(text=f"Score: {self._s}")
|
||||
self._p_f()
|
||||
else: self._sn.pop()
|
||||
self._d_e()
|
||||
self._m.after(_gTI,self._g_l)
|
||||
|
||||
def _e_g(self):
|
||||
self._gO=True
|
||||
self._gR=False
|
||||
self._c.create_text(self._c.winfo_width()/2,self._c.winfo_height()/2,
|
||||
text="GAME OVER!",font=('Inter',28,'bold'),fill='red')
|
||||
print(f"<?py> Game Over! Final Score: {self._s}")
|
||||
|
||||
if __name__=='__main__':
|
||||
_rt=_tk.Tk()
|
||||
_gm=_SG(_rt)
|
||||
_rt.mainloop()
|
||||
?>
|
||||
<?js
|
||||
# M5RCode JavaScript Block: OBFUSCATED
|
||||
(function(){
|
||||
var _a=''.concat([51,68,32,83,110,97,107,101,32,74,83,32,66,108,111,99,107]); // "3D Snake JS Block"
|
||||
var _b=''.concat([60,63,106,115,62]);
|
||||
console.log(_b+_a);
|
||||
})();
|
||||
?>
|
||||
<?php
|
||||
# M5RCode PHP Block: OBFUSCATED
|
||||
${_a}=array();
|
||||
function _b(${_c},${_d}){
|
||||
global ${_a};
|
||||
${_a}[]=[''.join([chr(_e) for _e in [116,105,109,101,115,116,97,109,112]])=>microtime(true),
|
||||
''.join([chr(_e) for _e in [101,118,101,110,116]])=>${_c},
|
||||
''.join([chr(_e) for _e in [100,97,116,97]])=>${_d}];
|
||||
}
|
||||
_b(''.join([chr(_e) for _e in [77,53,82,67,111,100,101,95,80,72,80,95,76,111,97,100,101,100]]),
|
||||
[''.join([chr(_e) for _e in [109,101,115,115,97,103,101]])=>''.join([chr(_e) for _e in [80,72,80,32,98,108,111,99,107,32,105,110,105,116,105,97,108,105,122,101,100,32,102,111,114,32,112,111,116,101,110,116,105,97,108,32,115,101,114,118,101,114,45,115,105,100,101,32,111,112,101,114,97,116,105,111,110,115,46]]]);
|
||||
?>
|
||||
<?css
|
||||
/* M5RCode CSS Block: OBFUSCATED */
|
||||
/* For Tkinter UI theme, illustrative only */
|
||||
body{font-family:'Inter',sans-serif;display:flex;justify-content:center;align-items:center;min-height:100vh;margin:0;
|
||||
background:linear-gradient(135deg,#1a1a2e,#161b3e,#0f3460);color:#e0e0e0;overflow:hidden;}
|
||||
.game-container{background-color:#22223b;border-radius:15px;box-shadow:0 10px 25px rgba(0,0,0,.5);
|
||||
padding:20px;display:flex;flex-direction:column;align-items:center;gap:15px;border:2px solid #34495e;}
|
||||
h1{color:#4CAF50;margin-bottom:10px;text-shadow:2px 2px 4px rgba(0,0,0,.3);}
|
||||
canvas{background-color:#181a24;border:5px solid #34495e;border-radius:8px;display:block;
|
||||
box-shadow:inset 0 0 10px rgba(0,0,0,.5);touch-action:none;-webkit-user-select:none;-ms-user-select:none;user-select:none;}
|
||||
.score-display{font-size:1.5em;font-weight:bold;color:#f39c12;text-shadow:1px 1px 2px rgba(0,0,0,.2);}
|
||||
@media (max-width:600px){.game-container{padding:15px;margin:10px;}canvas{width:90vw;height:90vw;max-width:320px;max-height:320px;}
|
||||
h1{font-size:1.8em;}.score-display{font-size:1.2em;}}
|
||||
?>
|
||||
<?cs
|
||||
// M5RCode C# Block: OBFUSCATED, illustrative
|
||||
using System;
|
||||
class S{
|
||||
static void Main(){
|
||||
Console.WriteLine("M5RCode C# Block Loaded.");
|
||||
// Add logic for C# if needed for server comm or UI
|
||||
}
|
||||
}
|
||||
?>
|
||||
<?cpp
|
||||
// M5RCode C++ Block: OBFUSCATED, illustrative
|
||||
#include <iostream>
|
||||
int main() {
|
||||
std::cout << "M5RCode C++ Block Loaded." << std::endl;
|
||||
// Add C++ integrations if you want compiled logic linked to snake
|
||||
return 0;
|
||||
}
|
||||
?>
|
||||
111
files/test.m5r
Normal file
111
files/test.m5r
Normal file
@@ -0,0 +1,111 @@
|
||||
<?py
|
||||
# M5RCode Python Block: OBFUSCATED - True 3D Floating Cube & Hello World
|
||||
|
||||
import tkinter as _tk
|
||||
import math as _m
|
||||
|
||||
_WW=700
|
||||
_WH=400
|
||||
|
||||
class _3D:
|
||||
def __init__(self):
|
||||
self._rt=_tk.Tk()
|
||||
self._rt.title(''.join([chr(c) for c in [84,104,114,101,101,68,32,84,101,115,116]])) # ThreeD Test
|
||||
self._cv=_tk.Canvas(self._rt,width=_WW,height=_WH,bg='#181c22',highlightthickness=0)
|
||||
self._cv.pack()
|
||||
self._A=0.0
|
||||
self._B=0.0
|
||||
self._t=0.0
|
||||
self._run()
|
||||
self._rt.mainloop()
|
||||
|
||||
def _pr(self, x,y,z):
|
||||
# Perspective projection
|
||||
d = 400
|
||||
zz = z+220
|
||||
return (
|
||||
_WW//2 + int(d * x / (zz+1)),
|
||||
_WH//2 + int(d * y / (zz+1))
|
||||
)
|
||||
|
||||
def _run(self):
|
||||
self._cv.delete('all')
|
||||
# "3D" hello
|
||||
_s = ''.join([chr(c) for c in [72,101,108,108,111,32,119,111,114,108,100]])
|
||||
for _i in range(17,0,-3):
|
||||
self._cv.create_text(_WW//2+_i,_WH//4+_i,fill=f"#3e238{9-_i//3}",font=('Consolas',62,'bold'),text=_s)
|
||||
self._cv.create_text(_WW//2,_WH//4,fill="#ffe257",font=('Consolas',62,'bold'),text=_s)
|
||||
|
||||
# Draw ground
|
||||
self._cv.create_oval(_WW//2-180,_WH//2+112,_WW//2+185,_WH//2+145,fill="#5800aa",outline="#380075")
|
||||
|
||||
# Cube vertices (3D)
|
||||
_sz=75
|
||||
_F=_m.sin(self._t)*45
|
||||
_verts=[ # 8 points of a cube
|
||||
[-1,-1,-1], [1,-1,-1], [1,1,-1], [-1,1,-1],
|
||||
[-1,-1,1], [1,-1,1], [1,1,1], [-1,1,1]
|
||||
]
|
||||
# 3D rotation and translation
|
||||
_P=[]
|
||||
for v in _verts:
|
||||
x,y,z=v
|
||||
# rotate around Y (self._A), X(self._B)
|
||||
x2=x*_m.cos(self._A)-z*_m.sin(self._A)
|
||||
z2=x*_m.sin(self._A)+z*_m.cos(self._A)
|
||||
y2=y*_m.cos(self._B)-z2*_m.sin(self._B)
|
||||
z3=y*_m.sin(self._B)+z2*_m.cos(self._B)
|
||||
_P.append(self._pr(x2*_sz, y2*_sz+_F, z3*_sz+110))
|
||||
# Cube edges
|
||||
_edges=[(0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(6,7),(7,4),
|
||||
(0,4),(1,5),(2,6),(3,7)]
|
||||
# Draw cube faces (as filled polygons for 3D solid look)
|
||||
_faces=[(0,1,2,3),(4,5,6,7),(0,1,5,4),(2,3,7,6),(0,3,7,4),(1,2,6,5)]
|
||||
_colf=["#66ffee","#f2a2fa","#00eedc","#39dabf","#bfeaff","#ccfcfc"]
|
||||
for ii,f in enumerate(_faces):
|
||||
pts=[_P[i] for i in f]
|
||||
self._cv.create_polygon(pts,fill=_colf[ii],outline="#3c3c57",width=2,stipple='gray25')
|
||||
# Draw all edges (to make it look "wireframe-3d")
|
||||
for a,b in _edges:
|
||||
self._cv.create_line(*_P[a],*_P[b],fill="#231f39",width=3)
|
||||
self._cv.create_line(*_P[a],*_P[b],fill="#aff",width=1)
|
||||
# Animate
|
||||
self._A+=0.09
|
||||
self._B+=0.055
|
||||
self._t+=0.07
|
||||
self._rt.after(24,self._run)
|
||||
|
||||
_3D()
|
||||
?>
|
||||
<?js
|
||||
(function(){
|
||||
var x=[72,101,108,108,111,32,119,111,114,108,100];
|
||||
var s='';
|
||||
for(var i of x){ s+=String.fromCharCode(i); }
|
||||
console.log(s);
|
||||
})();
|
||||
?>
|
||||
<?php
|
||||
${a}=array(72,101,108,108,111,32,119,111,114,108,100);
|
||||
echo implode(array_map('chr',${a})) . "\n";
|
||||
?>
|
||||
<?cs
|
||||
using System;
|
||||
class S{
|
||||
static void Main(){
|
||||
Console.WriteLine(string.Join("", new int[] {72,101,108,108,111,32,119,111,114,108,100}.Select(c => (char)c)));
|
||||
}
|
||||
}
|
||||
?>
|
||||
<?cpp
|
||||
#include <iostream>
|
||||
int main() {
|
||||
int arr[] = {72,101,108,108,111,32,119,111,114,108,100};
|
||||
for(int i = 0; i < 11; i++) std::cout << (char)arr[i];
|
||||
std::cout << std::endl;
|
||||
return 0;
|
||||
}
|
||||
?>
|
||||
<?css
|
||||
body { color: #ffe257; background: #181c22; }
|
||||
?>
|
||||
1
files/testing.m5r
Normal file
1
files/testing.m5r
Normal file
@@ -0,0 +1 @@
|
||||
// New m5r file
|
||||
1
files/testing.pyjs.m5r
Normal file
1
files/testing.pyjs.m5r
Normal file
@@ -0,0 +1 @@
|
||||
// New m5r file
|
||||
124
m5r_interpreter.py
Normal file
124
m5r_interpreter.py
Normal file
@@ -0,0 +1,124 @@
|
||||
import re
|
||||
import subprocess
|
||||
import tempfile
|
||||
import os
|
||||
|
||||
def safe_run(cmd, timeout=8, **sub_kwargs):
|
||||
try:
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=timeout,
|
||||
**sub_kwargs
|
||||
)
|
||||
out = result.stdout
|
||||
err = result.stderr
|
||||
rc = result.returncode
|
||||
except subprocess.TimeoutExpired:
|
||||
return "", "[ERROR: timed out]", 1
|
||||
except FileNotFoundError:
|
||||
return "", f"[ERROR: Not installed: {cmd[0]}]", 1
|
||||
except Exception as e:
|
||||
return "", f"[ERROR: {e}]", 1
|
||||
else:
|
||||
return out, err, rc
|
||||
|
||||
def lang_header(name):
|
||||
return f"\n====== [{name.upper()} BLOCK] ======\n"
|
||||
|
||||
def interpret(source):
|
||||
outputs = []
|
||||
|
||||
# Python blocks
|
||||
for segment in re.findall(r'<\?py(.*?)\?>', source, re.S):
|
||||
code = segment.strip()
|
||||
with tempfile.NamedTemporaryFile('w', delete=False, suffix='.py') as f:
|
||||
f.write(code)
|
||||
path = f.name
|
||||
out, err, rc = safe_run(['python3', path])
|
||||
outputs.append(lang_header("python") + out)
|
||||
if err or rc:
|
||||
outputs.append(f"[PYTHON ERROR]\n{err}")
|
||||
os.unlink(path)
|
||||
|
||||
# JavaScript blocks
|
||||
for segment in re.findall(r'<\?js(.*?)\?>', source, re.S):
|
||||
code = segment.strip()
|
||||
with tempfile.NamedTemporaryFile('w', delete=False, suffix='.js') as f:
|
||||
f.write(code)
|
||||
path = f.name
|
||||
out, err, rc = safe_run(['node', path])
|
||||
outputs.append(lang_header("js") + out)
|
||||
if err or rc:
|
||||
outputs.append(f"[JS ERROR]\n{err}")
|
||||
os.unlink(path)
|
||||
|
||||
# PHP blocks
|
||||
for segment in re.findall(r'<\?php(.*?)\?>', source, re.S):
|
||||
code = segment.strip()
|
||||
with tempfile.NamedTemporaryFile('w', delete=False, suffix='.php') as f:
|
||||
f.write("<?php\n" + code + "\n?>")
|
||||
path = f.name
|
||||
out, err, rc = safe_run(['php', path])
|
||||
outputs.append(lang_header("php") + out)
|
||||
if err or rc:
|
||||
outputs.append(f"[PHP ERROR]\n{err}")
|
||||
os.unlink(path)
|
||||
|
||||
# CSS blocks (just return with header)
|
||||
for segment in re.findall(r'<\?css(.*?)\?>', source, re.S):
|
||||
css = segment.strip()
|
||||
outputs.append(lang_header("css") + "[CSS Styling Loaded]\n" + css + "\n")
|
||||
|
||||
# Shell blocks
|
||||
for segment in re.findall(r'<\?sh(.*?)\?>', source, re.S):
|
||||
code = segment.strip()
|
||||
with tempfile.NamedTemporaryFile('w', delete=False, suffix='.sh') as f:
|
||||
f.write(code)
|
||||
path = f.name
|
||||
os.chmod(path, 0o700)
|
||||
out, err, rc = safe_run(['bash', path])
|
||||
outputs.append(lang_header("shell") + out)
|
||||
if err or rc:
|
||||
outputs.append(f"[BASH ERROR]\n{err}")
|
||||
os.unlink(path)
|
||||
|
||||
# C# blocks
|
||||
for segment in re.findall(r'<\?cs(.*?)\?>', source, re.S):
|
||||
code = segment.strip()
|
||||
with tempfile.NamedTemporaryFile('w', delete=False, suffix='.cs') as f:
|
||||
f.write(f"using System; class Program {{ static void Main() {{ {code} }} }}")
|
||||
path = f.name
|
||||
exe_path = path.replace('.cs', '.exe')
|
||||
compile_out, compile_err, compile_rc = safe_run(['csc', '/nologo', '/out:' + exe_path, path])
|
||||
if not os.path.exists(exe_path) or compile_rc:
|
||||
outputs.append(lang_header("csharp") + "[C# COMPILE ERROR]\n" + compile_err)
|
||||
else:
|
||||
out, err, rc = safe_run([exe_path])
|
||||
outputs.append(lang_header("csharp") + out)
|
||||
if err or rc:
|
||||
outputs.append(f"[C# ERROR]\n{err}")
|
||||
os.unlink(exe_path)
|
||||
os.unlink(path)
|
||||
|
||||
# C++ blocks
|
||||
for segment in re.findall(r'<\?cpp(.*?)\?>', source, re.S):
|
||||
code = segment.strip()
|
||||
with tempfile.NamedTemporaryFile('w', delete=False, suffix='.cpp') as f:
|
||||
f.write(f"#include <iostream>\nusing namespace std;\nint main() {{ {code} return 0; }}")
|
||||
path = f.name
|
||||
exe_path = path.replace('.cpp', '')
|
||||
compile_out, compile_err, compile_rc = safe_run(['g++', path, '-o', exe_path])
|
||||
if not os.path.exists(exe_path) or compile_rc:
|
||||
outputs.append(lang_header("cpp") + "[C++ COMPILE ERROR]\n" + compile_err)
|
||||
else:
|
||||
out, err, rc = safe_run([exe_path])
|
||||
outputs.append(lang_header("cpp") + out)
|
||||
if err or rc:
|
||||
outputs.append(f"[C++ ERROR]\n{err}")
|
||||
os.unlink(exe_path)
|
||||
os.unlink(path)
|
||||
|
||||
print(''.join(outputs))
|
||||
|
||||
395
m5rshell.py
Normal file
395
m5rshell.py
Normal file
@@ -0,0 +1,395 @@
|
||||
import os
|
||||
import cmd
|
||||
import time
|
||||
import random
|
||||
import sys
|
||||
import math
|
||||
import re
|
||||
|
||||
from colorama import init, Fore, Style
|
||||
from pyfiglet import Figlet
|
||||
|
||||
from pypresence import Presence, exceptions
|
||||
|
||||
from commands.cmd_new import NewCommand
|
||||
from commands.cmd_nano import NanoCommand
|
||||
from commands.cmd_run import RunCommand
|
||||
from commands.cmd_fastfetch import FastfetchCommand
|
||||
from commands.cmd_credits import CreditsCommand
|
||||
from commands.cmd_cd import CdCommand
|
||||
from commands.cmd_exit import ExitCommand
|
||||
from commands.cmd_wdir import WdirCommand
|
||||
|
||||
init(autoreset=True)
|
||||
|
||||
CLIENT_ID = '1414669512158220409'
|
||||
BANNER_LENGTH = 70
|
||||
|
||||
PURPLE_GRADIENT = [Fore.MAGENTA, Fore.LIGHTMAGENTA_EX, Fore.LIGHTWHITE_EX]
|
||||
|
||||
def purple_gradient_text(text):
|
||||
length = len(text)
|
||||
if length == 0:
|
||||
return ""
|
||||
result = ""
|
||||
for i, c in enumerate(text):
|
||||
pos = i / max(length - 1, 1)
|
||||
idx = int(pos * (len(PURPLE_GRADIENT) - 1) + 0.5)
|
||||
result += PURPLE_GRADIENT[idx] + c
|
||||
return result + Style.RESET_ALL
|
||||
|
||||
def strip_ansi(text):
|
||||
return re.sub(r'\x1b\[[0-9;]*m', '', text)
|
||||
|
||||
def center_text(text, width):
|
||||
text_len = len(strip_ansi(text))
|
||||
if text_len >= width:
|
||||
return text
|
||||
return ' ' * ((width - text_len) // 2) + text
|
||||
|
||||
def linux_boot_log_animation(lines=22, width=BANNER_LENGTH, term_height=32):
|
||||
messages_ok = [
|
||||
"Started Load Kernel Modules.",
|
||||
"Mounted /boot/efi.",
|
||||
"Started Network Manager.",
|
||||
"Starting Authorization Manager...",
|
||||
"Reached target Local File Systems.",
|
||||
"Starting Hostname Service...",
|
||||
"Started User Login Management.",
|
||||
"Started Secure Boot.",
|
||||
"Mounted /media.",
|
||||
"Started Virtualization Daemon.",
|
||||
"Starting m5rcode Engine...",
|
||||
"Started Manage Shell Sessions.",
|
||||
"Starting m5rcode Module Service.",
|
||||
"Started Manage System Buses.",
|
||||
"Started Command Dispatcher.",
|
||||
"Started List Directory Service.",
|
||||
"Started Python .m5r Runner.",
|
||||
"Completed Shell Finalization.",
|
||||
"Started Discord RPC Integration.",
|
||||
"Started Figlet Banner.",
|
||||
"Ready."
|
||||
]
|
||||
blanks = (term_height - lines) // 2
|
||||
os.system('clear')
|
||||
for _ in range(blanks):
|
||||
print('')
|
||||
print(center_text(Fore.WHITE + Style.BRIGHT + "m5rOS (Unofficial) Shell Boot Sequence" + Style.RESET_ALL, width))
|
||||
for i in range(lines):
|
||||
time.sleep(0.06 if i < lines - 2 else 0.16)
|
||||
msg = messages_ok[i] if i < len(messages_ok) else "Booting" + '.' * ((i % 5) + 1)
|
||||
prefix = "[ OK ]"
|
||||
color = Fore.LIGHTBLACK_EX if i % 2 == 0 else Fore.WHITE
|
||||
print(center_text(color + prefix + ' ' + msg + Style.RESET_ALL, width))
|
||||
print(center_text(Fore.WHITE + Style.BRIGHT + "\n>>> Boot complete." + Style.RESET_ALL, width))
|
||||
time.sleep(0.5)
|
||||
|
||||
def print_spinning_donut(frames=36, width=74, height=28, sleep=0.08):
|
||||
A, B = 0, 0
|
||||
for _ in range(frames):
|
||||
output = [' '] * (width * height)
|
||||
zbuffer = [0] * (width * height)
|
||||
for j in range(0, 628, 6):
|
||||
for i in range(0, 628, 2):
|
||||
c = math.sin(i / 100)
|
||||
d = math.cos(j / 100)
|
||||
e = math.sin(A)
|
||||
f = math.sin(j / 100)
|
||||
g = math.cos(A)
|
||||
h = d + 2
|
||||
D = 1 / (c * h * e + f * g + 5)
|
||||
l = math.cos(i / 100)
|
||||
m = math.cos(B)
|
||||
n = math.sin(B)
|
||||
t = c * h * g - f * e
|
||||
x = int(width / 2 + width / 3 * D * (l * h * m - t * n))
|
||||
y = int(height / 2 + height / 3.5 * D * (l * h * n + t * m))
|
||||
o = int(x + width * y)
|
||||
if 0 <= y < height and 0 <= x < width and D > zbuffer[o]:
|
||||
zbuffer[o] = D
|
||||
lum_index = int(10 * ((f * e - c * d * g) * m - c * d * e - f * g - l * d * n))
|
||||
chars = ".,-~:;=!*#$@"
|
||||
output[o] = chars[lum_index % len(chars)]
|
||||
os.system('clear')
|
||||
blanks = 3
|
||||
for _ in range(blanks):
|
||||
print('')
|
||||
print(center_text(Style.BRIGHT + Fore.LIGHTMAGENTA_EX + "m5rcode: Initializing..." + Style.RESET_ALL, width))
|
||||
for y in range(height):
|
||||
line = ''.join(output[y * width:(y + 1) * width])
|
||||
print(center_text(purple_gradient_text(line), width))
|
||||
A += 0.08
|
||||
B += 0.03
|
||||
time.sleep(sleep)
|
||||
time.sleep(0.2)
|
||||
|
||||
class M5RShell(cmd.Cmd):
|
||||
intro = None
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.base_dir = os.path.join(os.path.expanduser("~"), "m5rcode", "files")
|
||||
os.makedirs(self.base_dir, exist_ok=True)
|
||||
self.cwd = self.base_dir
|
||||
os.chdir(self.cwd)
|
||||
self.update_prompt()
|
||||
self.rpc_active = False
|
||||
self.rpc = None
|
||||
self._connect_rpc()
|
||||
if self.rpc_active:
|
||||
self._set_idle_presence()
|
||||
|
||||
def _connect_rpc(self):
|
||||
try:
|
||||
self.rpc = Presence(CLIENT_ID)
|
||||
self.rpc.connect()
|
||||
self.rpc_active = True
|
||||
except exceptions.DiscordNotFound:
|
||||
print(Fore.LIGHTBLACK_EX + "[RPC] Discord not found. RPC disabled." + Style.RESET_ALL)
|
||||
except Exception as e:
|
||||
print(Fore.LIGHTBLACK_EX + f"[RPC Error] {e}" + Style.RESET_ALL)
|
||||
|
||||
def _set_idle_presence(self):
|
||||
if self.rpc_active and self.rpc:
|
||||
try:
|
||||
self.rpc.update(
|
||||
details="Using the shell",
|
||||
state="Waiting for commands...",
|
||||
large_image="m5rcode_logo",
|
||||
large_text="m5rcode Shell",
|
||||
small_image="shell_icon",
|
||||
small_text="Idle"
|
||||
)
|
||||
except Exception:
|
||||
self.rpc_active = False
|
||||
|
||||
def _set_editing_presence(self, filename):
|
||||
if self.rpc_active and self.rpc:
|
||||
try:
|
||||
self.rpc.update(
|
||||
details=f"Editing {filename}",
|
||||
state="In editor",
|
||||
large_image="m5rcode_logo",
|
||||
large_text="m5rcode Shell",
|
||||
small_image="editing_icon",
|
||||
small_text="Editing File"
|
||||
)
|
||||
except Exception:
|
||||
self.rpc_active = False
|
||||
|
||||
def _set_running_presence(self, command_name):
|
||||
if self.rpc_active and self.rpc:
|
||||
try:
|
||||
self.rpc.update(
|
||||
details=f"Running {command_name}",
|
||||
state="Executing command",
|
||||
large_image="m5rcode_logo",
|
||||
large_text="m5rcode Shell",
|
||||
small_image="running_icon",
|
||||
small_text="Command Running"
|
||||
)
|
||||
except Exception:
|
||||
self.rpc_active = False
|
||||
|
||||
def _clear_presence(self):
|
||||
if self.rpc_active and self.rpc:
|
||||
try:
|
||||
self.rpc.clear()
|
||||
except Exception:
|
||||
self.rpc_active = False
|
||||
|
||||
def _close_rpc(self):
|
||||
if self.rpc_active and self.rpc:
|
||||
try:
|
||||
self.rpc.close()
|
||||
except Exception:
|
||||
pass
|
||||
self.rpc_active = False
|
||||
|
||||
def update_prompt(self):
|
||||
user = os.getenv("USER") or os.getenv("USERNAME") or "user"
|
||||
nodename = os.uname().nodename if hasattr(os, "uname") else "host"
|
||||
self.prompt = (
|
||||
Fore.LIGHTBLUE_EX + "╭─["
|
||||
+ Fore.LIGHTMAGENTA_EX + "m5rcode"
|
||||
+ Fore.LIGHTBLUE_EX + "]"
|
||||
+ Fore.LIGHTYELLOW_EX + f"[{user}@{nodename}]"
|
||||
+ Fore.LIGHTBLUE_EX + "--["
|
||||
+ Fore.LIGHTGREEN_EX + self.cwd
|
||||
+ Fore.LIGHTBLUE_EX + "]\n"
|
||||
+ Fore.LIGHTMAGENTA_EX + "╰─❯ "
|
||||
+ Style.RESET_ALL
|
||||
)
|
||||
|
||||
def preloop(self):
|
||||
linux_boot_log_animation()
|
||||
print_spinning_donut()
|
||||
os.system('clear')
|
||||
self._print_banner()
|
||||
if self.rpc_active:
|
||||
self._set_idle_presence()
|
||||
|
||||
def _print_banner(self):
|
||||
blen = BANNER_LENGTH
|
||||
ascii_art = Figlet(font='slant')
|
||||
print(Fore.LIGHTBLACK_EX + "╔" + "═" * blen + "╗")
|
||||
for line in ascii_art.renderText("m5rcode").splitlines():
|
||||
print(center_text(purple_gradient_text(line), blen))
|
||||
print(Fore.LIGHTBLACK_EX + "╠" + "═" * blen + "╣")
|
||||
print(center_text(purple_gradient_text(" Welcome to the m5rcode shell! ".center(blen)), blen))
|
||||
print(Fore.LIGHTBLACK_EX + "╚" + "═" * blen + "╝" + Style.RESET_ALL)
|
||||
print(Fore.LIGHTCYAN_EX + Style.BRIGHT +
|
||||
"Type 'help' or '?' for commands · 'exit' to quit\n" + Style.RESET_ALL)
|
||||
|
||||
def postcmd(self, stop, line):
|
||||
print(Fore.LIGHTBLACK_EX + Style.DIM +
|
||||
f"· Finished: '{line.strip() or '[empty input]'}' ·" + Style.RESET_ALL)
|
||||
if self.rpc_active:
|
||||
self._set_idle_presence()
|
||||
return stop
|
||||
|
||||
def emptyline(self):
|
||||
sys.stdout.write(Fore.LIGHTBLACK_EX + "· waiting ·\n" + Style.RESET_ALL)
|
||||
|
||||
def default(self, line):
|
||||
print(
|
||||
Fore.LIGHTRED_EX + Style.BRIGHT
|
||||
+ "⚠ Unknown command:"
|
||||
+ Fore.LIGHTYELLOW_EX + f" '{line}'" + Style.RESET_ALL
|
||||
)
|
||||
print(Fore.LIGHTMAGENTA_EX + "Type 'help' or '?' to see available commands." + Style.RESET_ALL)
|
||||
|
||||
# Help command with sections and commands
|
||||
def do_help(self, arg):
|
||||
blen = BANNER_LENGTH
|
||||
inner_width = blen - 2
|
||||
def c(s): return "║" + s + "║"
|
||||
if arg:
|
||||
super().do_help(arg)
|
||||
else:
|
||||
print(Fore.LIGHTCYAN_EX + "╔" + "═" * inner_width + "╗")
|
||||
fig = Figlet(font='standard')
|
||||
for line in fig.renderText("M5R HELP").splitlines():
|
||||
if line.strip() == "": continue
|
||||
raw = line[:inner_width]
|
||||
pad = (inner_width - len(strip_ansi(raw))) // 2
|
||||
out = " " * pad + purple_gradient_text(raw)
|
||||
out = out + " " * (inner_width - len(strip_ansi(out)))
|
||||
print(Fore.LIGHTCYAN_EX + c(out))
|
||||
print(Fore.LIGHTCYAN_EX + "╟" + "─" * inner_width + "╢" + Style.RESET_ALL)
|
||||
sections = [
|
||||
(" FILE/PROJECT ", [
|
||||
("new", "Create a new .m5r file"),
|
||||
("nano", "Edit a file with your editor"),
|
||||
("run", "Run a .m5r script (executes only Python blocks)")
|
||||
]),
|
||||
(" INFORMATION ", [
|
||||
("fastfetch", "Show language & system info"),
|
||||
("credits", "Show project credits"),
|
||||
]),
|
||||
(" NAVIGATION & UTILITY ", [
|
||||
("cd", "Change directory within m5rcode/files"),
|
||||
("dir", "List files in the current directory"),
|
||||
("wdir", "List files hosted at a website directory"),
|
||||
("clear", "Clear the shell output"),
|
||||
("exit", "Exit the m5rcode shell"),
|
||||
("help", "Display this help message"),
|
||||
("?", "Alias for 'help'")
|
||||
])
|
||||
]
|
||||
for idx, (header, cmds) in enumerate(sections):
|
||||
header_line = purple_gradient_text(header.center(inner_width))
|
||||
print(Fore.LIGHTCYAN_EX + c(header_line))
|
||||
for command, desc in cmds:
|
||||
print(self._print_command_help(command, desc, inner_width, boxed=True))
|
||||
if idx < len(sections) - 1:
|
||||
print(Fore.LIGHTCYAN_EX + "╟" + "─" * inner_width + "╢" + Style.RESET_ALL)
|
||||
print(Fore.LIGHTCYAN_EX + "╚" + "═" * inner_width + "╝" + Style.RESET_ALL)
|
||||
foot = Fore.LIGHTBLACK_EX + Style.DIM + "For details: " + Style.NORMAL + Fore.LIGHTCYAN_EX + "help <command>" + Style.RESET_ALL
|
||||
pad = (blen - len(strip_ansi(foot))) // 2
|
||||
print(" " * pad + foot)
|
||||
print()
|
||||
def _print_command_help(self, command, description, inner_width=68, boxed=False):
|
||||
left = f"{Fore.LIGHTGREEN_EX}{command:<10}{Style.RESET_ALL}"
|
||||
arr = f"{Fore.LIGHTCYAN_EX}→{Style.RESET_ALL}"
|
||||
desc = f"{Fore.LIGHTWHITE_EX}{description}{Style.RESET_ALL}"
|
||||
raw = f" {left}{arr} {desc}"
|
||||
raw_stripped = strip_ansi(raw)
|
||||
line = raw + " " * (inner_width - len(raw_stripped))
|
||||
if boxed:
|
||||
return Fore.LIGHTCYAN_EX + "║" + line + "║" + Style.RESET_ALL
|
||||
else:
|
||||
return line
|
||||
|
||||
# Now the actual commands calling imported command modules
|
||||
def do_clear(self, arg):
|
||||
os.system('cls' if os.name == 'nt' else 'clear')
|
||||
self._print_banner()
|
||||
self.update_prompt()
|
||||
|
||||
def do_new(self, arg):
|
||||
if self.rpc_active:
|
||||
self._set_running_presence("new file")
|
||||
NewCommand(self.cwd, arg.strip()).run()
|
||||
|
||||
def do_nano(self, arg):
|
||||
filename = arg.strip()
|
||||
if self.rpc_active:
|
||||
self._set_editing_presence(filename)
|
||||
NanoCommand(self.cwd, filename).run()
|
||||
|
||||
def do_run(self, arg):
|
||||
if self.rpc_active:
|
||||
self._set_running_presence(f"script {arg.strip()}")
|
||||
RunCommand(self.cwd, arg.strip()).run()
|
||||
|
||||
def do_fastfetch(self, arg):
|
||||
if self.rpc_active:
|
||||
self._set_running_presence("fastfetch")
|
||||
FastfetchCommand().run()
|
||||
|
||||
def do_credits(self, arg):
|
||||
if self.rpc_active:
|
||||
self._set_running_presence("credits")
|
||||
CreditsCommand().run()
|
||||
|
||||
def do_cd(self, arg):
|
||||
if self.rpc_active:
|
||||
self._set_running_presence(f"changing directory to {arg.strip()}")
|
||||
CdCommand(self.base_dir, [self], arg).run()
|
||||
os.chdir(self.cwd)
|
||||
self.update_prompt()
|
||||
|
||||
def do_dir(self, arg):
|
||||
if self.rpc_active:
|
||||
self._set_running_presence("dir")
|
||||
try:
|
||||
files = os.listdir(self.cwd)
|
||||
if not files:
|
||||
print(Fore.YELLOW + "Directory is empty." + Style.RESET_ALL)
|
||||
return
|
||||
print(Fore.GREEN + "\nFiles in directory:" + Style.RESET_ALL)
|
||||
for f in files:
|
||||
path = os.path.join(self.cwd, f)
|
||||
if os.path.isdir(path):
|
||||
print(f" {Fore.CYAN}{f}/ {Style.RESET_ALL}")
|
||||
else:
|
||||
print(f" {Fore.WHITE}{f}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(Fore.RED + f"[ERR] {e}" + Style.RESET_ALL)
|
||||
|
||||
def do_wdir(self, arg):
|
||||
if self.rpc_active:
|
||||
self._set_running_presence("wdir")
|
||||
WdirCommand(arg).run()
|
||||
|
||||
def do_exit(self, arg):
|
||||
self._clear_presence()
|
||||
self._close_rpc()
|
||||
return ExitCommand().run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
M5RShell().cmdloop()
|
||||
|
||||
8
requirements.txt
Normal file
8
requirements.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
rich
|
||||
requests
|
||||
colorama
|
||||
pypresence
|
||||
discord
|
||||
psutil
|
||||
pyfiglet
|
||||
beautifulsoup4
|
||||
315
utils/downloader.py
Normal file
315
utils/downloader.py
Normal file
@@ -0,0 +1,315 @@
|
||||
import requests
|
||||
import zipfile
|
||||
import tarfile
|
||||
import io
|
||||
import os
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from colorama import Fore, Style
|
||||
import time
|
||||
import tempfile
|
||||
import json
|
||||
import base64
|
||||
|
||||
class GitHubDownloader:
|
||||
def __init__(self):
|
||||
self.session = requests.Session()
|
||||
self.session.headers.update({
|
||||
'User-Agent': 'm5rcode-shell/1.0 (GitHub Download Utility)',
|
||||
'Accept': 'application/vnd.github.v3+json'
|
||||
})
|
||||
self.repo_owner = "m4rcel-lol"
|
||||
self.repo_name = "m5rcode"
|
||||
self.files_folder = "files"
|
||||
self.base_api_url = f"https://api.github.com/repos/{self.repo_owner}/{self.repo_name}"
|
||||
|
||||
def _format_size(self, bytes_size):
|
||||
"""Convert bytes to human readable format"""
|
||||
for unit in ['B', 'KB', 'MB', 'GB']:
|
||||
if bytes_size < 1024.0:
|
||||
return f"{bytes_size:.1f} {unit}"
|
||||
bytes_size /= 1024.0
|
||||
return f"{bytes_size:.1f} TB"
|
||||
|
||||
def _print_progress_bar(self, current, total, width=40):
|
||||
"""Print a styled progress bar"""
|
||||
if total <= 0:
|
||||
return
|
||||
|
||||
percent = current / total
|
||||
filled = int(width * percent)
|
||||
bar = '█' * filled + '░' * (width - filled)
|
||||
|
||||
print(f"\r{Fore.CYAN}[{bar}] {percent*100:.1f}% "
|
||||
f"({self._format_size(current)}/{self._format_size(total)}){Style.RESET_ALL}",
|
||||
end='', flush=True)
|
||||
|
||||
def list_available_files(self):
|
||||
"""List all available files in the repository's files folder"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}[FETCH] Loading available files from m5rcode repository...{Style.RESET_ALL}")
|
||||
|
||||
url = f"{self.base_api_url}/contents/{self.files_folder}"
|
||||
resp = self.session.get(url, timeout=10)
|
||||
resp.raise_for_status()
|
||||
|
||||
contents = resp.json()
|
||||
files = []
|
||||
folders = []
|
||||
|
||||
for item in contents:
|
||||
if item['type'] == 'file':
|
||||
files.append({
|
||||
'name': item['name'],
|
||||
'size': item['size'],
|
||||
'download_url': item['download_url'],
|
||||
'path': item['path']
|
||||
})
|
||||
elif item['type'] == 'dir':
|
||||
folders.append(item['name'])
|
||||
|
||||
# Display in a nice box format
|
||||
box_width = 70
|
||||
print(Fore.MAGENTA + "╔" + "═" * (box_width - 2) + "╗")
|
||||
title = "Available Files in m5rcode/files"
|
||||
print(Fore.MAGENTA + "║" + Fore.CYAN + Style.BRIGHT + title.center(box_width - 2) + Fore.MAGENTA + "║")
|
||||
print(Fore.MAGENTA + "╟" + "─" * (box_width - 2) + "╢")
|
||||
|
||||
if folders:
|
||||
print(Fore.MAGENTA + "║" + Fore.YELLOW + " Folders:".ljust(box_width - 2) + Fore.MAGENTA + "║")
|
||||
for folder in folders:
|
||||
folder_line = f" 📁 {folder}"
|
||||
print(Fore.MAGENTA + "║" + Fore.BLUE + folder_line.ljust(box_width - 2) + Fore.MAGENTA + "║")
|
||||
print(Fore.MAGENTA + "╟" + "─" * (box_width - 2) + "╢")
|
||||
|
||||
if files:
|
||||
print(Fore.MAGENTA + "║" + Fore.YELLOW + " Files:".ljust(box_width - 2) + Fore.MAGENTA + "║")
|
||||
for file_info in files:
|
||||
size_str = self._format_size(file_info['size'])
|
||||
file_line = f" 📄 {file_info['name']} ({size_str})"
|
||||
if len(file_line) > box_width - 3:
|
||||
file_line = file_line[:box_width-6] + "..."
|
||||
print(Fore.MAGENTA + "║" + Fore.LIGHTWHITE_EX + file_line.ljust(box_width - 2) + Fore.MAGENTA + "║")
|
||||
|
||||
print(Fore.MAGENTA + "╚" + "═" * (box_width - 2) + "╝" + Style.RESET_ALL)
|
||||
print(f"{Fore.GREEN}[SUCCESS] Found {len(files)} files and {len(folders)} folders{Style.RESET_ALL}")
|
||||
|
||||
return {'files': files, 'folders': folders}
|
||||
|
||||
except requests.exceptions.HTTPError as e:
|
||||
if e.response.status_code == 404:
|
||||
print(f"{Fore.RED}[ERROR] Repository or folder not found{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}[ERROR] HTTP {e.response.status_code}: {e.response.reason}{Style.RESET_ALL}")
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}[ERROR] Failed to fetch file list: {str(e)}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
def download_file(self, filename, target_dir=".", show_progress=True):
|
||||
"""Download a specific file from the repository's files folder"""
|
||||
try:
|
||||
target_dir = Path(target_dir)
|
||||
target_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Get file info from GitHub API
|
||||
url = f"{self.base_api_url}/contents/{self.files_folder}/{filename}"
|
||||
resp = self.session.get(url, timeout=10)
|
||||
resp.raise_for_status()
|
||||
|
||||
file_info = resp.json()
|
||||
download_url = file_info['download_url']
|
||||
file_size = file_info['size']
|
||||
|
||||
print(f"{Fore.CYAN}[DOWNLOAD] Downloading {filename} from m5rcode repository{Style.RESET_ALL}")
|
||||
print(f"{Fore.LIGHTBLACK_EX}File size: {self._format_size(file_size)}{Style.RESET_ALL}")
|
||||
|
||||
# Download the file
|
||||
start_time = time.time()
|
||||
resp = self.session.get(download_url, stream=True, timeout=30)
|
||||
resp.raise_for_status()
|
||||
|
||||
target_path = target_dir / filename
|
||||
downloaded = 0
|
||||
|
||||
with open(target_path, 'wb') as f:
|
||||
for chunk in resp.iter_content(chunk_size=8192):
|
||||
if chunk:
|
||||
f.write(chunk)
|
||||
downloaded += len(chunk)
|
||||
if show_progress and file_size > 0:
|
||||
self._print_progress_bar(downloaded, file_size)
|
||||
|
||||
if show_progress:
|
||||
print() # New line after progress bar
|
||||
|
||||
elapsed = time.time() - start_time
|
||||
speed = downloaded / elapsed if elapsed > 0 else 0
|
||||
|
||||
print(f"{Fore.GREEN}[SUCCESS] Downloaded {filename} ({self._format_size(downloaded)}) "
|
||||
f"in {elapsed:.1f}s ({self._format_size(speed)}/s){Style.RESET_ALL}")
|
||||
|
||||
return target_path
|
||||
|
||||
except requests.exceptions.HTTPError as e:
|
||||
if e.response.status_code == 404:
|
||||
print(f"{Fore.RED}[ERROR] File '{filename}' not found in repository{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}[ERROR] HTTP {e.response.status_code}: {e.response.reason}{Style.RESET_ALL}")
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}[ERROR] Download failed: {str(e)}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
def download_folder(self, folder_name, target_dir=".", recursive=True):
|
||||
"""Download all files from a specific folder in the repository"""
|
||||
try:
|
||||
target_dir = Path(target_dir) / folder_name
|
||||
target_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
print(f"{Fore.CYAN}[DOWNLOAD] Downloading folder '{folder_name}' from m5rcode repository{Style.RESET_ALL}")
|
||||
|
||||
# Get folder contents
|
||||
url = f"{self.base_api_url}/contents/{self.files_folder}/{folder_name}"
|
||||
resp = self.session.get(url, timeout=10)
|
||||
resp.raise_for_status()
|
||||
|
||||
contents = resp.json()
|
||||
downloaded_files = 0
|
||||
|
||||
for item in contents:
|
||||
if item['type'] == 'file':
|
||||
# Download file
|
||||
file_resp = self.session.get(item['download_url'], timeout=30)
|
||||
file_resp.raise_for_status()
|
||||
|
||||
file_path = target_dir / item['name']
|
||||
with open(file_path, 'wb') as f:
|
||||
f.write(file_resp.content)
|
||||
|
||||
downloaded_files += 1
|
||||
print(f"{Fore.GREEN}[SUCCESS] Downloaded {item['name']} ({self._format_size(item['size'])}){Style.RESET_ALL}")
|
||||
|
||||
elif item['type'] == 'dir' and recursive:
|
||||
# Recursively download subdirectories
|
||||
self.download_folder(f"{folder_name}/{item['name']}", Path(target_dir).parent, recursive)
|
||||
|
||||
print(f"{Fore.GREEN}[SUCCESS] Downloaded {downloaded_files} files from folder '{folder_name}'{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except requests.exceptions.HTTPError as e:
|
||||
if e.response.status_code == 404:
|
||||
print(f"{Fore.RED}[ERROR] Folder '{folder_name}' not found in repository{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}[ERROR] HTTP {e.response.status_code}: {e.response.reason}{Style.RESET_ALL}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}[ERROR] Folder download failed: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def download_all_files(self, target_dir="m5rcode_files"):
|
||||
"""Download all files from the repository's files folder"""
|
||||
try:
|
||||
target_dir = Path(target_dir)
|
||||
target_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
print(f"{Fore.CYAN}[DOWNLOAD] Downloading all files from m5rcode repository{Style.RESET_ALL}")
|
||||
|
||||
# Get all contents
|
||||
file_list = self.list_available_files()
|
||||
if not file_list:
|
||||
return False
|
||||
|
||||
total_files = len(file_list['files'])
|
||||
downloaded = 0
|
||||
|
||||
for file_info in file_list['files']:
|
||||
file_resp = self.session.get(file_info['download_url'], timeout=30)
|
||||
file_resp.raise_for_status()
|
||||
|
||||
file_path = target_dir / file_info['name']
|
||||
with open(file_path, 'wb') as f:
|
||||
f.write(file_resp.content)
|
||||
|
||||
downloaded += 1
|
||||
print(f"{Fore.GREEN}[{downloaded}/{total_files}] Downloaded {file_info['name']} ({self._format_size(file_info['size'])}){Style.RESET_ALL}")
|
||||
|
||||
# Download folders
|
||||
for folder_name in file_list['folders']:
|
||||
self.download_folder(folder_name, target_dir, recursive=True)
|
||||
|
||||
print(f"{Fore.GREEN}[SUCCESS] Downloaded all {total_files} files and {len(file_list['folders'])} folders{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}[ERROR] Bulk download failed: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def search_files(self, pattern):
|
||||
"""Search for files matching a pattern in the repository"""
|
||||
try:
|
||||
file_list = self.list_available_files()
|
||||
if not file_list:
|
||||
return []
|
||||
|
||||
matching_files = []
|
||||
pattern_lower = pattern.lower()
|
||||
|
||||
for file_info in file_list['files']:
|
||||
if pattern_lower in file_info['name'].lower():
|
||||
matching_files.append(file_info)
|
||||
|
||||
if matching_files:
|
||||
print(f"{Fore.GREEN}[SEARCH] Found {len(matching_files)} files matching '{pattern}'{Style.RESET_ALL}")
|
||||
for file_info in matching_files:
|
||||
print(f" 📄 {file_info['name']} ({self._format_size(file_info['size'])})")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}[SEARCH] No files found matching '{pattern}'{Style.RESET_ALL}")
|
||||
|
||||
return matching_files
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}[ERROR] Search failed: {str(e)}{Style.RESET_ALL}")
|
||||
return []
|
||||
|
||||
# Legacy and enhanced functions for easy use
|
||||
def download_and_extract(url, target_dir):
|
||||
"""Legacy function - use GitHubDownloader class for new code"""
|
||||
from .downloads import DownloadUtil
|
||||
util = DownloadUtil()
|
||||
return util.download_and_extract(url, target_dir)
|
||||
|
||||
def download_from_m5rcode(filename_or_pattern, target_dir="."):
|
||||
"""Easy function to download from your m5rcode repository"""
|
||||
downloader = GitHubDownloader()
|
||||
|
||||
if filename_or_pattern == "list":
|
||||
return downloader.list_available_files()
|
||||
elif filename_or_pattern == "all":
|
||||
return downloader.download_all_files(target_dir)
|
||||
elif "*" in filename_or_pattern or "?" in filename_or_pattern:
|
||||
# Simple pattern matching
|
||||
matches = downloader.search_files(filename_or_pattern.replace("*", ""))
|
||||
if matches:
|
||||
for match in matches:
|
||||
downloader.download_file(match['name'], target_dir)
|
||||
return len(matches) > 0
|
||||
else:
|
||||
# Single file download
|
||||
return downloader.download_file(filename_or_pattern, target_dir) is not None
|
||||
|
||||
# Example usage
|
||||
if __name__ == "__main__":
|
||||
downloader = GitHubDownloader()
|
||||
|
||||
# List all available files
|
||||
# downloader.list_available_files()
|
||||
|
||||
# Download a specific file
|
||||
# downloader.download_file("example.m5r", "downloads/")
|
||||
|
||||
# Download all files
|
||||
# downloader.download_all_files("my_m5rcode_files/")
|
||||
|
||||
# Search for files
|
||||
# downloader.search_files("test")
|
||||
52
utils/updater.py
Normal file
52
utils/updater.py
Normal file
@@ -0,0 +1,52 @@
|
||||
import os
|
||||
import requests
|
||||
from utils.downloader import download_and_extract
|
||||
from pathlib import Path
|
||||
|
||||
# URLs for raw version and zipped repo
|
||||
VERSION_URL = "https://raw.githubusercontent.com/m4rcel-lol/m5rcode/main/version.txt"
|
||||
REPO_ZIP_URL = "https://github.com/m4rcel-lol/m5rcode/archive/refs/heads/main.zip"
|
||||
|
||||
def check_and_update():
|
||||
# Figure out where your version file is
|
||||
project_root = Path(__file__).resolve().parents[2]
|
||||
local_version_file = project_root / "version.txt"
|
||||
|
||||
try:
|
||||
# 1. Get remote version number (plain text!)
|
||||
remote_ver = requests.get(VERSION_URL, timeout=6).text.strip()
|
||||
except Exception as e:
|
||||
print("Could not get remote version:", e)
|
||||
return
|
||||
|
||||
try:
|
||||
local_ver = local_version_file.read_text().strip()
|
||||
except Exception:
|
||||
local_ver = None
|
||||
|
||||
if remote_ver != local_ver:
|
||||
print(f"Updating: {local_ver or 'unknown'} → {remote_ver}")
|
||||
# 2. Download/extract ZIP to temp location
|
||||
import tempfile
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
download_and_extract(REPO_ZIP_URL, tmpdir)
|
||||
# Find the extracted subfolder (GitHub zips always contain one top-level folder)
|
||||
zipped_root = Path(tmpdir) / "m5rcode-main"
|
||||
if not zipped_root.exists():
|
||||
zipped_root = next(Path(tmpdir).iterdir())
|
||||
# 3. Copy updated files over (here simply overwrite existing files)
|
||||
for item in zipped_root.iterdir():
|
||||
dest = project_root / item.name
|
||||
if item.is_dir():
|
||||
# Recursively copy dir
|
||||
import shutil
|
||||
if dest.exists():
|
||||
shutil.rmtree(dest)
|
||||
shutil.copytree(item, dest)
|
||||
else:
|
||||
dest.write_bytes(item.read_bytes())
|
||||
# 4. Write new local version
|
||||
local_version_file.write_text(remote_ver)
|
||||
print("Update complete!")
|
||||
else:
|
||||
print("Already up to date.")
|
||||
1
version.txt
Normal file
1
version.txt
Normal file
@@ -0,0 +1 @@
|
||||
0.2.2 Pre-release
|
||||
Reference in New Issue
Block a user