Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion code/__DEFINES/computer4_defines.dm
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,35 @@
// Well-Known Directories
#define THINKDOS_BIN_DIRECTORY "/bin"
// Constants
#define THINKDOS_MAX_COMMANDS 3 //! The maximum amount of commands
#define THINKDOS_MAX_COMMANDS 10 //! The maximum amount of commands
// Symbols
#define THINKDOS_SYMBOL_SEPARATOR ";" //! Lets you split stdin into distinct commands

//ANSI color helpers.

/// ANSI CSI seq `ESC [`
#define ANSI_CSI "\x1b\x5B"

/// Generate an ANSI SGR escape code dynamically.
#define ANSI_SGR(ID) "[ANSI_CSI][ID]m"
/// Internal only, Variant of `ANSI_SGR` that can const fold and stringifies literally.
#define _ANSI_SGR_CONSTFOLD(ID) (ANSI_CSI + #ID + "m")

#define ANSI_FULL_RESET _ANSI_SGR_CONSTFOLD(0)

#define ANSI_BOLD _ANSI_SGR_CONSTFOLD(1)
#define ANSI_UNBOLD _ANSI_SGR_CONSTFOLD(22)


#define ANSI_FG_RED _ANSI_SGR_CONSTFOLD(31)
#define ANSI_FG_GREEN _ANSI_SGR_CONSTFOLD(32)
#define ANSI_FG_YELLOW _ANSI_SGR_CONSTFOLD(33)
#define ANSI_FG_BLUE _ANSI_SGR_CONSTFOLD(34)

#define ANSI_FG_RESET _ANSI_SGR_CONSTFOLD(39)

// ANSI wrapper helpers.

// Please understand that these might have unexpected styling side effects, as ANSI isn't inherently closed like HTML.

#define ANSI_WRAP_BOLD(bolded_text) (ANSI_BOLD+bolded_text+ANSI_UNBOLD)
16 changes: 8 additions & 8 deletions code/modules/computer4/computer4.dm
Original file line number Diff line number Diff line change
Expand Up @@ -371,10 +371,10 @@ TYPEINFO_DEF(/obj/machinery/computer4)
/obj/machinery/computer4/proc/post_system()
text_buffer = ""

text_buffer += "Initializing system...<br>"
text_buffer += "Initializing system...\n"

if(!internal_disk)
text_buffer = "<font color=red>1701 - NO FIXED DISK</font><br>"
text_buffer = "1701 - NO FIXED DISK\n"

// Os already known.
if(operating_system)
Expand All @@ -387,25 +387,25 @@ TYPEINFO_DEF(/obj/machinery/computer4)
var/datum/c4_file/terminal_program/operating_system/new_os = locate() in inserted_disk?.root.contents

if(new_os)
text_buffer += "Booting from inserted drive...<br>"
text_buffer += "Booting from inserted drive...\n"
set_operating_system(new_os)
else
text_buffer += "<font color=red>Non-system disk or disk error.</font><br>"
text_buffer += "Non-system disk or disk error.\n"

// Okay how about the internal drive?
if(!operating_system && internal_disk)
var/datum/c4_file/terminal_program/operating_system/new_os = locate() in internal_disk?.root.contents

if(new_os)
text_buffer += "Booting from fixed drive...<br>"
text_buffer += "Booting from fixed drive...\n"
set_operating_system(new_os)
else
text_buffer += "<font color=red>Unable to boot from fixed drive.</font><br>"
text_buffer += "Unable to boot from fixed drive.\n"


// Fuck.
if(!operating_system)
text_buffer += "<font color=red>ERR - BOOT FAILURE</font><br>"
text_buffer += "ERR - BOOT FAILURE\n"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldnt you want to color these red


SStgui.update_uis(src)

Expand All @@ -416,7 +416,7 @@ TYPEINFO_DEF(/obj/machinery/computer4)
if(operating_system)
set_operating_system(null)

text_buffer = "Rebooting system...<br>"
text_buffer = "Rebooting system...\n"

tgui_input_history = list()
tgui_input_index = list()
Expand Down
42 changes: 21 additions & 21 deletions code/modules/computer4/data/terminal/directive/directman.dm
Original file line number Diff line number Diff line change
Expand Up @@ -103,23 +103,23 @@
current_menu = DIRECTMAN_MENU_HOME

var/list/out = list(
@"<pre style='margin: 0px'> ┌┬┐┬┬─┐┌─┐┌─┐┌┬┐╔╦╗╔═╗╔╗╔</pre>",
@"<pre style='margin: 0px'> │││├┬┘├┤ │ │ ║║║╠═╣║║║</pre>",
@"<pre style='margin: 0px'> ─┴┘┴┴└─└─┘└─┘ ┴ ╩ ╩╩ ╩╝╚╝</pre>",
"Commands:<br>",
"\[1\] View Current Directives<br>",
@" ┌┬┐┬┬─┐┌─┐┌─┐┌┬┐╔╦╗╔═╗╔╗╔",
@" │││├┬┘├┤ │ │ ║║║╠═╣║║║",
@" ─┴┘┴┴└─└─┘└─┘ ┴ ╩ ╩╩ ╩╝╚╝",
"Commands:",
"\[1\] View Current Directives",
)

if(SSdirectives.get_directives_for_selection())
out += "\[2\] View New Directives<br>"
out += "\[2\] View New Directives"

out += "<br>\[R\] Refresh"
out += "\[R\] Refresh"

get_os().println(jointext(out, null))
get_os().println(jointext(out, "\n"))

/datum/c4_file/terminal_program/directman/proc/view_current()
if(!get_os().get_computer().get_peripheral(PERIPHERAL_TYPE_WIRELESS_CARD))
get_os().println("<b>Error:</b> Unable to locate wireless adapter.")
get_os().println("[ANSI_WRAP_BOLD("Error:")] Unable to locate wireless adapter.")
return

current_menu = DIRECTMAN_MENU_CURRENT
Expand All @@ -130,13 +130,13 @@
for(var/datum/directive/directive as anything in SSdirectives.get_active_directives())
i++
out += "\[[fit_with_zeros("[i]", 2)]\] [directive.name]"
out += "<br>\[B\] Return"
out += "\n\[B\] Return"

get_os().println(jointext(out, "<br>"))
get_os().println(jointext(out, "\n"))

/datum/c4_file/terminal_program/directman/proc/view_directive(index)
if(!get_os().get_computer().get_peripheral(PERIPHERAL_TYPE_WIRELESS_CARD))
get_os().println("<b>Error:</b> Unable to locate wireless adapter.")
get_os().println("[ANSI_WRAP_BOLD("Error:")] Unable to locate wireless adapter.")
return

current_menu = DIRECTMAN_MENU_ACTIVE_DIRECTIVE
Expand All @@ -148,18 +148,18 @@
"Description: [directive.desc]",
"Severity: [directive.severity]",
"Time Given: [active_directives[directive]]",
"<br>\[B\] Return",
"\n\[B\] Return",
)

get_os().println(jointext(out, "<br>"))
get_os().println(jointext(out, "\n"))

/datum/c4_file/terminal_program/directman/proc/view_new()
if(!get_os().get_computer().get_peripheral(PERIPHERAL_TYPE_WIRELESS_CARD))
get_os().println("<b>Error:</b> Unable to locate wireless adapter.")
get_os().println("[ANSI_WRAP_BOLD("Error:")] Unable to locate wireless adapter.")
return

if(!SSdirectives.get_directives_for_selection())
get_os().println("<b>Error:</b> No new directives to display.")
get_os().println("[ANSI_WRAP_BOLD("Error:")] No new directives to display.")
return

current_menu = DIRECTMAN_MENU_NEW_DIRECTIVES
Expand All @@ -170,14 +170,14 @@
for(var/datum/directive/directive as anything in SSdirectives.get_directives_for_selection())
i++
out += "\[[fit_with_zeros("[i]", 2)]\] [directive.name]"
out += "<br>\[B\] Return"
out += "\n\[B\] Return"

get_os().println(jointext(out, "<br>"))
get_os().println(jointext(out, "\n"))

/datum/c4_file/terminal_program/directman/proc/view_new_directive(index)
if(!get_os().get_computer().get_peripheral(PERIPHERAL_TYPE_WIRELESS_CARD))
view_home()
get_os().println("<b>Error:</b> Unable to locate wireless adapter.")
get_os().println("[ANSI_WRAP_BOLD("Error:")] Unable to locate wireless adapter.")
return

get_os().clear_screen(TRUE)
Expand All @@ -190,7 +190,7 @@
"Description: [viewing_directive.desc]",
"Payout: [viewing_directive.reward] mark\s",
"Severity: [viewing_directive.severity]",
"<br>\[B\] Return | \[S\] Select"
"\n\[B\] Return | \[S\] Select"
)

get_os().println(jointext(out, "<br>"))
get_os().println(jointext(out, "\n"))
2 changes: 1 addition & 1 deletion code/modules/computer4/data/terminal/operating_system.dm
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@


var/obj/machinery/computer4/computer = get_computer()
computer.text_buffer += "[text]<br>"
computer.text_buffer += "[text]\n"
if(update_ui)
SStgui.update_uis(computer)
return TRUE
Expand Down
26 changes: 26 additions & 0 deletions code/modules/computer4/data/terminal/test_progs/dbg_ansi.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/datum/c4_file/terminal_program/ansi_test
name = "dbg_ansi"
size = 1

/datum/c4_file/terminal_program/ansi_test/execute(datum/c4_file/terminal_program/operating_system/thinkdos/system, datum/parsed_cmdline/cmdline)
. = ..()
//Iterate and test all SGR parameters in standard form, or special form as needed.

var/print_all = !!cmdline.options.Find("a")


system.println("#----------#-----------#-----------#")
for(var/param_id in 0 to 107)
switch(param_id)
//Unsupported/undefined
if(5-7,20,25-27,50,52,56-57,60-72,76-89,98-99,)
if(print_all)
system.println("| SGR [fit_with_zeros("[param_id]", 3)]: | NOT SUPPORTED |")
continue
if(38,48,58)
system.println("| SGR [fit_with_zeros("[param_id]", 3)]: | Uses Extended Color |")
else
system.println("| SGR [fit_with_zeros("[param_id]", 3)]: | Test Text | [ANSI_SGR(param_id)]Test Text[ANSI_FULL_RESET] |")
system.println("#----------#-----------#-----------#")
system.unload_program(src)
return
30 changes: 15 additions & 15 deletions code/modules/computer4/data/terminal/thinkdos/thinkdos.dm
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,21 @@

/datum/c4_file/terminal_program/operating_system/thinkdos/execute()
if(!initialize_logs())
println("<font color=red>Log system failure.</font>")
println("[ANSI_FG_RED]Log system failure.[ANSI_FG_RESET]")

if(!needs_login)
println("Account system disabled.")

else if(!initialize_accounts())
println("<font color=red>Unable to start account system.</font>")
println("[ANSI_FG_RED]Unable to start account system.[ANSI_FG_RESET]")

change_dir(containing_folder)

var/title_text = list(
@"<pre style='margin: 0px'> ___ _ _ _ ___ ___ ___</pre>",
@"<pre style='margin: 0px'>|_ _|| |_ &lt;_&gt;._ _ | |__| . \| . |/ __&gt;</pre>",
@"<pre style='margin: 0px'> | | | . || || &#39; || / /| | || | |\__ \</pre>",
@"<pre style='margin: 0px'> |_| |_|_||_||_|_||_\_\|___/`___&#39;&lt;___/</pre>",
"[ANSI_FG_RED]", @" ___ _ _ _ ___ ___ ___ ", "\n",
"[ANSI_FG_GREEN]", @"|_ _|| |_ <_>._ _ | |__| . \| . |/ __>", "\n",
"[ANSI_FG_YELLOW]",@" | | | . || || ' || / /| | || | |\__ \", "\n",
"[ANSI_FG_BLUE]", @" |_| |_|_||_||_|_||_\_\|___/`___'<___/", "[ANSI_FG_RESET]"
).Join("")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sovl

println(title_text)

Expand Down Expand Up @@ -172,48 +172,48 @@
login_user.access = text2access(account_access)
set_current_user(login_user)

write_log("<b>LOGIN</b>: [html_encode(account_name)] | [html_encode(account_occupation)]")
println("Welcome [html_encode(account_name)]!<br><b>Current Directory: [current_directory.path_to_string()]</b>")
write_log("[ANSI_WRAP_BOLD("LOGIN")]: [account_name] | [account_occupation]")
println("Welcome [html_encode(account_name)]!\n[ANSI_WRAP_BOLD("Current Directory: [current_directory.path_to_string()]")]")
return TRUE

/datum/c4_file/terminal_program/operating_system/thinkdos/proc/logout()
if(!current_user)
print_error("<b>Error:</b> Account system inactive.")
print_error("[ANSI_WRAP_BOLD("Error")]: Account system inactive.")
return FALSE

write_log("<b>LOGOUT:</b> [html_encode(current_user.registered_name)]")
write_log("[ANSI_WRAP_BOLD("LOGOUT")]: [current_user.registered_name]")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs testing to make sure this actually sanitizes.

set_current_user(null)
return TRUE

/datum/c4_file/terminal_program/operating_system/thinkdos/proc/initialize_accounts()
var/datum/c4_file/folder/account_dir = parse_directory("users")
if(!istype(account_dir))
if(account_dir && !account_dir.containing_folder.try_delete_file(account_dir))
print_error("<b>Error:</b> Unable to write account folder.")
print_error("[ANSI_WRAP_BOLD("Error")]: Unable to write account folder.")
return FALSE

account_dir = new
account_dir.set_name("users")

if(!containing_folder.try_add_file(account_dir))
qdel(account_dir)
print_error("<b>Error:</b> Unable to write account folder.")
print_error("[ANSI_WRAP_BOLD("Error")]: Unable to write account folder.")
return FALSE

RegisterSignal(account_dir, list(COMSIG_COMPUTER4_FILE_RENAMED, COMSIG_COMPUTER4_FILE_ADDED, COMSIG_COMPUTER4_FILE_REMOVED), PROC_REF(user_folder_gone))

var/datum/c4_file/user/user_data = account_dir.get_file("admin", FALSE)
if(!istype(user_data))
if(user_data && !user_data.containing_folder.try_delete_file(user_data))
print_error("<b>Error:</b> Unable to write account folder.")
print_error("[ANSI_WRAP_BOLD("Error")]: Unable to write account folder.")
return FALSE

user_data = new
user_data.set_name("admin")

if(!account_dir.try_add_file(user_data))
qdel(user_data)
print_error("<b>Error:</b> Unable to write account file.")
print_error("[ANSI_WRAP_BOLD("Error")]: Unable to write account file.")
return FALSE

//set_current_user(user_data)
Expand Down Expand Up @@ -255,7 +255,7 @@
command_log = log_file
RegisterSignal(command_log, list(COMSIG_COMPUTER4_FILE_RENAMED, COMSIG_COMPUTER4_FILE_ADDED, COMSIG_PARENT_QDELETING), PROC_REF(log_file_gone))

log_file.data += "<br><b>STARTUP:</b> [stationtime2text()], [stationdate2text()]"
log_file.data += "\n[ANSI_WRAP_BOLD("STARTUP")]: [stationtime2text()], [stationdate2text()]"
return TRUE


Expand Down
1 change: 1 addition & 0 deletions daedalus.dme
Original file line number Diff line number Diff line change
Expand Up @@ -3027,6 +3027,7 @@
#include "code\modules\computer4\data\terminal\rtos\pincode_door.dm"
#include "code\modules\computer4\data\terminal\rtos\simple_door_control.dm"
#include "code\modules\computer4\data\terminal\rtos\slave.dm"
#include "code\modules\computer4\data\terminal\test_progs\dbg_ansi.dm"
#include "code\modules\computer4\data\terminal\test_progs\flagtest.dm"
#include "code\modules\computer4\data\terminal\thinkdos\thinkdos.dm"
#include "code\modules\computer4\data\terminal\thinkdos\thinkdos_commands.dm"
Expand Down
1 change: 1 addition & 0 deletions tgui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"eslint-plugin-simple-import-sort": "^12.1.0",
"eslint-plugin-typescript-sort-keys": "^3.2.0",
"eslint-plugin-unused-imports": "^3.2.0",
"fancy-ansi": "^0.1.3",
"file-loader": "^6.2.0",
"jest": "^29.7.0",
"jest-circus": "^29.7.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import { BooleanLike } from 'common/react';
import { FancyAnsi } from 'fancy-ansi';
import { useEffect } from 'react';

import { Box, Section } from '../../components';
Expand All @@ -16,6 +17,8 @@ type TerminalOutputSectionProps = Pick<
'bgColor' | 'displayHTML' | 'fontColor'
> & { noscroll?: BooleanLike };

const fancyAnsi = new FancyAnsi();

export const TerminalOutputSection = (props: TerminalOutputSectionProps) => {
const { displayHTML, fontColor, bgColor, noscroll } = props;

Expand All @@ -30,6 +33,10 @@ export const TerminalOutputSection = (props: TerminalOutputSectionProps) => {
sectionContentElement.scrollTop = sectionContentElement.scrollHeight;
}, [displayHTML]);

/* Whoops, lummox' JSON encoder is shoddy! We're being sent invalid UTF-16
and need to go back and fix it before passing it into the ANSI decoder. */
let fixed_html = displayHTML.replaceAll('\udc1b', '\u001b'); //Bad surrogate.

return (
<Section
backgroundColor={bgColor}
Expand All @@ -43,7 +50,7 @@ export const TerminalOutputSection = (props: TerminalOutputSectionProps) => {
color={fontColor}
fontSize="1.2em"
preserveWhitespace
dangerouslySetInnerHTML={{ __html: displayHTML }}
dangerouslySetInnerHTML={{ __html: fancyAnsi.toHtml(fixed_html) }}
/>
</Section>
);
Expand Down
1 change: 1 addition & 0 deletions tgui/packages/tgui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"common": "workspace:*",
"dateformat": "^5.0.3",
"dompurify": "^3.2.6",
"fancy-ansi": "^0.1.3",
"js-yaml": "^4.1.0",
"marked": "^4.3.0",
"react": "^19.1.0",
Expand Down
Loading
Loading