diff --git a/core/ansi_escape_parser.js b/core/ansi_escape_parser.js new file mode 100644 index 00000000..89ac9a99 --- /dev/null +++ b/core/ansi_escape_parser.js @@ -0,0 +1,285 @@ +/* jslint node: true */ +'use strict'; + +var events = require('events'); +var util = require('util'); +var miscUtil = require('./misc_util.js'); + +exports.ANSIEscapeParser = ANSIEscapeParser; + + +function ANSIEscapeParser(options) { + var self = this; + + events.EventEmitter.call(this); + + this.column = 1; + this.row = 1; + this.flags = 0x00; + this.scrollBack = 0; + + + + options = miscUtil.valueWithDefault(options, { + mciReplaceChar : '', + termHeight : 25, + termWidth : 80, + }); + + this.mciReplaceChar = miscUtil.valueWithDefault(options.mciReplaceChar, ''); + this.termHeight = miscUtil.valueWithDefault(options.termHeight, 25); + this.termWidth = miscUtil.valueWithDefault(options.termWidth, 80); + + function getArgArray(array) { + var i = array.length; + while(i--) { + array[i] = parseInt(array[i], 10); + } + return array; + } + + self.moveCursor = function(cols, rows) { + self.column += cols; + self.row += rows; + + self.column = Math.max(self.column, 1); + self.column = Math.min(self.column, self.termWidth); + self.row = Math.max(self.row, 1); + self.row = Math.min(self.row, self.termHeight); + + self.emit('move cursor', self.column, self.row); + self.rowUpdated(); + }; + + self.saveCursorPosition = function() { + self.savedPosition = { + row : self.row, + column : self.column + }; + }; + + self.restoreCursorPosition = function() { + self.row = self.savedPosition.row; + self.column = self.savedPosition.column; + delete self.savedPosition; + self.rowUpdated(); + }; + + self.clearScreen = function() { + // :TODO: should be doing something with row/column? + self.emit('clear screen'); + }; + + self.resetColor = function() { + self.fgColor = 7; + self.bgColor = 0; + }; + + self.rowUpdated = function() { + self.emit('row update', self.row + self.scrollBack); + }; + + function literal(text) { + var CR = 0x0d; + var LF = 0x0a; + var charCode; + + var len = text.length; + for(var i = 0; i < len; i++) { + charCode = text.charCodeAt(i) & 0xff; // ensure 8 bit + switch(charCode) { + case CR : + self.column = 1; + break; + + case LF : + self.row++; + self.rowUpdated(); + break; + + default : + // wrap + if(self.column === self.termWidth) { + self.column = 1; + self.row++; + self.rowUpdated(); + } else { + self.column++; + } + break; + } + + if(self.row === 26) { + self.scrollBack++; + self.row--; + self.rowUpdated(); + } + } + + self.emit('chunk', text); + } + + function mci(mciCode, args) { + console.log(mciCode, args); + } + + function getProcessedMCI(mci) { + if(self.mciReplaceChar.length > 0) { + return new Array(mci.length + 1).join(self.mciReplaceChar); + } else { + return mci; + } + } + + function parseMCI(buffer) { + var mciRe = /\%([A-Z]{2}[0-9]{1,2})(?:\(([0-9A-Z,]+)\))*/g; + var pos = 0; + var match; + var mciCode; + var args; + + do { + pos = mciRe.lastIndex; + match = mciRe.exec(buffer); + + if(null !== match) { + if(match.index > pos) { + literal(buffer.slice(pos, match.index)); + } + + mciCode = match[1]; + + if(match[2]) { + args = match[2].split(','); + } else { + args = []; + } + + + self.emit('mci', mciCode, args); + + self.emit('chunk', getProcessedMCI(match[0])); + } + + } while(0 !== mciRe.lastIndex); + + if(pos < buffer.length) { + literal(buffer.slice(pos)); + } + } + + self.parse = function(buffer, savedRe) { + // :TODO: ensure this conforms to ANSI-BBS / CTerm / bansi.txt for movement/etc. + var re = /(?:\x1b\x5b)([\?=;0-9]*?)([ABCDHJKfhlmnpsu])/g; + var pos = 0; + var match; + var opCode; + var args; + + // ignore anything past EOF marker, if any + buffer = buffer.split(String.fromCharCode(0x1a), 1)[0]; + + do { + pos = re.lastIndex; + match = re.exec(buffer); + + if(null !== match) { + if(match.index > pos) { + parseMCI(buffer.slice(pos, match.index)); + } + + opCode = match[2]; + args = getArgArray(match[1].split(';')); + + escape(opCode, args); + + self.emit('chunk', match[0]); + } + + } while(0 !== re.lastIndex); + + if(pos < buffer.length) { + parseMCI(buffer.slice(pos)); + } + + self.emit('complete'); + }; + + function escape(opCode, args) { + var arg; + var i; + var len; + + switch(opCode) { + // cursor up + case 'A' : + arg = args[0] || 1; + self.moveCursor(0, -arg); + break; + + // cursor down + case 'B' : + arg = args[0] || 1; + self.moveCursor(0, arg); + break; + + // cursor forward/right + case 'C' : + arg = args[0] || 1; + self.moveCursor(arg, 0); + break; + + // cursor back/left + case 'D' : + arg = args[0] || 1; + self.moveCursor(-arg, 0); + break; + + case 'f' : // horiz & vertical + case 'H' : // cursor position + self.row = args[0] || 1; + self.column = args[1] || 1; + self.rowUpdated(); + break; + + // save position + case 's' : + self.saveCursorPosition(); + break; + + // restore position + case 'u' : + self.restoreCursorPosition(); + break; + + // set graphic rendition + case 'm' : + for(i = 0, len = args.length; i < len; ++i) { + arg = args[i]; + if(0x00 === arg) { + self.flags = 0x00; + self.resetColor(); + } else { + switch(Math.floor(arg / 10)) { + case 0 : self.flags |= arg; break; + case 3 : self.fgColor = arg; break; + case 4 : self.bgColor = arg; break; + //case 3 : self.fgColor = arg - 30; break; + //case 4 : self.bgColor = arg - 40; break; + } + } + } + break; + + // erase display/screen + case 'J' : + // :TODO: Handle others + if(2 === args[0]) { + self.clearScreen(); + } + break; + } + } +} + +util.inherits(ANSIEscapeParser, events.EventEmitter); \ No newline at end of file diff --git a/core/ansi_term.js b/core/ansi_term.js new file mode 100644 index 00000000..4b7b57bd --- /dev/null +++ b/core/ansi_term.js @@ -0,0 +1,338 @@ +/* jslint node: true */ +'use strict'; + +// +// ANSI Terminal Support +// +// Resources: +// * http://ansi-bbs.org/ +// * http://www.bbsdocumentary.com/library/PROGRAMS/GRAPHICS/ANSI/ansisys.txt +// * http://en.wikipedia.org/wiki/ANSI_escape_code +// + +var assert = require('assert'); +var binary = require('binary'); +var miscUtil = require('./misc_util.js'); + +exports.sgr = sgr; +exports.clearScreen = clearScreen; +exports.clearScreenGoHome = clearScreenGoHome; +exports.normal = normal; +exports.goHome = goHome; +exports.disableVT100LineWrapping = disableVT100LineWrapping; +exports.setSyncTermFont = setSyncTermFont; +exports.fromPipeCode = fromPipeCode; +exports.forEachControlCode = forEachControlCode; + + +// +// See also +// https://github.com/TooTallNate/ansi.js/blob/master/lib/ansi.js + +var ESC_CSI = '\u001b['; + +var CONTROL = { + up : 'A', + down : 'B', + forward : 'C', + back : 'D', + nextLine : 'E', + prevLine : 'F', + horizAbsolute : 'G', + eraseData : 'J', + scrollUp : 'S', + scrollDown : 'T', + savePos : 's', + restorePos : 'u', + queryPos : '6n', + goto : 'H', // row Pr, column Pc -- same as f + gotoAlt : 'f' // same as H +}; + +/* + DECTERM stuff. Probably never need + hide : '?25l', + show : '?25h',*/ + +// +// Select Graphics Rendition +// See http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/src/conio/cterm.txt +// +var SGR = { + reset : 0, + bold : 1, + dim : 2, + blink : 5, + fastBlink : 6, + negative : 7, + hidden : 8, + + normal : 22, + steady : 25, + positive : 27, + + black : 30, + red : 31, + green : 32, + yellow : 33, + blue : 34, + magenta : 35, + cyan : 36, + white : 37, + + blackBG : 40, + redBG : 41, + greenBG : 42, + yellowBG : 43, + blueBG : 44, + magentaBG : 45, + cyanBG : 47, + whiteBG : 47, +}; + +// See http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/src/conio/cterm.txt +// :TODO: document +var SYNC_TERM_FONTS = [ + 'cp437', + 'cp1251', + 'koi8_r', + 'iso8859_2', + 'iso8859_4', + 'cp866', + 'iso8859_9', + 'haik8', + 'iso8859_8', + 'koi8_u', + 'iso8859_15', + 'iso8859_4', + 'koi8_r_b', + 'iso8859_4', + 'iso8859_5', + 'ARMSCII_8', + 'iso8859_15', + 'cp850', + 'cp850', + 'cp885', + 'cp1251', + 'iso8859_7', + 'koi8-r_c', + 'iso8859_4', + 'iso8859_1', + 'cp866', + 'cp437', + 'cp866', + 'cp885', + 'cp866_u', + 'iso8859_1', + 'cp1131', + 'c64_upper', + 'c64_lower', + 'c128_upper', + 'c128_lower', + 'atari', + 'pot_noodle', + 'mo_soul', + 'microknight', + 'topaz' +]; + +// Create methods such as up(), nextLine(),... +Object.keys(CONTROL).forEach(function onControlName(name) { + var code = CONTROL[name]; + + exports[name] = function() { + var c = code; + if(arguments.length > 0) { + // arguments are array like -- we want an array + c = Array.prototype.slice.call(arguments).map(Math.round).join(';') + code; + } + return ESC_CSI + c; + }; +}); + +// Create a reverse map of CONTROL values to their key/names + +/* +var CONTROL_REVERSE_MAP = {}; +Object.keys(CONTROL).forEach(function onControlName(name) { + var code = CONTROL[name]; + + CONTROL_REVERSE_MAP[code] = name; +}); +*/ + +var CONTROL_RESPONSE = { + 'R' : 'position', +}; + +// :TODO: move this to misc utils or such -- use here & parser +function getIntArgArray(array) { + var i = array.length; + while(i--) { + array[i] = parseInt(array[i], 10); + } + return array; +} + +// :TODO: rename this +function forEachControlCode(data, cb) { + //var re = /\u001b\[([0-9\;])*[R]/g; + + var len = data.length; + var pos = 0; + + while(pos < len) { + if(0x1b !== data[pos++] || 0x5b !== data[pos++]) { + continue; + } + + var params = ''; + + while(pos < len) { + var c = data[pos++]; + + if(((c > 64) && (c < 91)) || ((c > 96) && (c < 123))) { + c = String.fromCharCode(c); + var name = CONTROL_RESPONSE[c]; + if(name) { + params = getIntArgArray(params.split(';')); + cb(name, params); + } + } + + params += String.fromCharCode(c); + } + } +} + +// Create various color methods such as white(), yellowBG(), reset(), ... +Object.keys(SGR).forEach(function onSgrName(name) { + var code = SGR[name]; + + exports[name] = function() { + return ESC_CSI + code + 'm'; + }; +}); + +function sgr() { + // + // - Allow an single array or variable number of arguments + // - Each element can be either a integer or string found in SGR + // which in turn maps to a integer + // + if(arguments.length <= 0) { + return ''; + } + + var result = ''; + + // :TODO: this method needs a lot of cleanup! + + var args = Array.isArray(arguments[0]) ? arguments[0] : arguments; + for(var i = 0; i < args.length; i++) { + if(typeof args[i] === 'string') { + if(args[i] in SGR) { + if(result.length > 0) { + result += ';'; + } + result += SGR[args[i]]; + } + } else if(typeof args[i] === 'number') { + if(result.length > 0) { + result += ';'; + } + result += args[i]; + } + } + return ESC_CSI + result + 'm'; +} + +/////////////////////////////////////////////////////////////////////////////// +// Shortcuts for common functions +/////////////////////////////////////////////////////////////////////////////// + +function clearScreen() { + return exports.eraseData(2); +} + +function clearScreenGoHome() { + return exports.goto(1,1) + exports.eraseData(2); +} + +function normal() { + return sgr(['normal', 'reset']); +} + +function goHome() { + return exports.goto(); // no params = home = 1,1 +} + +// +// See http://www.termsys.demon.co.uk/vtANSI_BBS.htm +// +function disableVT100LineWrapping() { + return ESC_CSI + '7l'; +} + +function setSyncTermFont(name, fontPage) { + fontPage = miscUtil.valueWithDefault(fontPage, 0); + + assert(fontPage === 0 || fontPage === 1); // see spec + + var i = SYNC_TERM_FONTS.indexOf(name); + if(-1 != i) { + return ESC_CSI + fontPage + ';' + i + ' D'; + } + return ''; +} + +// Also add: +// * fromRenegade(): |<0-23> +// * fromCelerity(): | +// * fromPCBoard(): (@X@) +// * fromWildcat(): (@@ (same as PCBoard without 'X' prefix) +// * fromWWIV(): <0-7> +// * fromSyncronet(): +// See http://wiki.synchro.net/custom:colors +function fromPipeCode(s) { + if(-1 == s.indexOf('|')) { + return s; // no pipe codes present + } + + var result = ''; + var re = /\|(\d{2,3}|\|)/g; + var m; + var lastIndex = 0; + while((m = re.exec(s))) { + var val = m[1]; + + if('|' == val) { + result += '|'; + continue; + } + + // convert to number + val = parseInt(val, 10); + if(isNaN(val)) { + val = 0; + } + + assert(val >= 0 && val <= 256); + + var attr = ''; + if(7 == val) { + attr = sgr('normal'); + } else if (val < 7 || val >= 16) { + attr = sgr(['normal', val]); + } else if (val <= 15) { + attr = sgr(['normal', val - 8, 'bold']); + } + + result += s.substr(lastIndex, m.index - lastIndex) + attr; + lastIndex = re.lastIndex; + } + + result = (0 === result.length ? s : result + s.substr(lastIndex)); + + return result; +} \ No newline at end of file diff --git a/core/art.js b/core/art.js new file mode 100644 index 00000000..f57888bd --- /dev/null +++ b/core/art.js @@ -0,0 +1,672 @@ +/* jslint node: true */ +'use strict'; + +var fs = require('fs'); +var paths = require('path'); +var assert = require('assert'); +var iconv = require('iconv-lite'); +var conf = require('./config.js'); +var miscUtil = require('./misc_util.js'); +var binary = require('binary'); +var events = require('events'); +var util = require('util'); +var ansi = require('./ansi_term.js'); +var aep = require('./ansi_escape_parser.js'); + +exports.getArt = getArt; +exports.getArtFromPath = getArtFromPath; +exports.display = display; +exports.defaultEncodingFromExtension = defaultEncodingFromExtension; +exports.ArtDisplayer = ArtDisplayer; + + +var SAUCE_SIZE = 128; +var SAUCE_ID = new Buffer([0x53, 0x41, 0x55, 0x43, 0x45]); // 'SAUCE' +var COMNT_ID = new Buffer([0x43, 0x4f, 0x4d, 0x4e, 0x54]); // 'COMNT' + +// :TODO: Return MCI code information +// :TODO: process SAUCE comments +// :TODO: return font + font mapped information from SAUCE + +var SUPPORTED_ART_TYPES = { + // :TODO: the defualt encoding are really useless if they are all the same ... + // perhaps .ansamiga and .ascamiga could be supported as well as overrides via conf + '.ans' : { name : 'ANSI', defaultEncoding : 'cp437', eof : 0x1a }, + '.asc' : { name : 'ASCII', defaultEncoding : 'cp437', eof : 0x1a }, + '.pcb' : { name : 'PCBoard', defaultEncoding : 'cp437', eof : 0x1a }, + '.bbs' : { name : 'Wildcat', defaultEncoding : 'cp437', eof : 0x1a }, + '.txt' : { name : 'Text', defaultEncoding : 'cp437', eof : 0x1a }, // :TODO: think about this more... + // :TODO: extentions for wwiv, renegade, celerity, syncronet, ... + // :TODO: extension for atari + // :TODO: extension for topaz ansi/ascii. +}; + +// +// See +// http://www.acid.org/info/sauce/sauce.htm +// +// :TODO: Move all SAUCE stuff to sauce.js +function readSAUCE(data, cb) { + if(data.length < SAUCE_SIZE) { + cb(new Error('No SAUCE record present')); + return; + } + + var offset = data.length - SAUCE_SIZE; + var sauceRec = data.slice(offset); + + binary.parse(sauceRec) + .buffer('id', 5) + .buffer('version', 2) + .buffer('title', 35) + .buffer('author', 20) + .buffer('group', 20) + .buffer('date', 8) + .word32lu('fileSize') + .word8('dataType') + .word8('fileType') + .word16lu('tinfo1') + .word16lu('tinfo2') + .word16lu('tinfo3') + .word16lu('tinfo4') + .word8('numComments') + .word8('flags') + .buffer('tinfos', 22) // SAUCE 00.5 + .tap(function onVars(vars) { + + if(!SAUCE_ID.equals(vars.id)) { + cb(new Error('No SAUCE record present')); + return; + } + + var ver = vars.version.toString('cp437'); + + if('00' !== ver) { + cb(new Error('Unsupported SAUCE version: ' + ver)); + return; + } + + var sauce = { + id : vars.id.toString('cp437'), + version : vars.version.toString('cp437'), + title : vars.title.toString('cp437').trim(), + author : vars.author.toString('cp437').trim(), + group : vars.group.toString('cp437').trim(), + date : vars.date.toString('cp437').trim(), + fileSize : vars.fileSize, + dataType : vars.dataType, + fileType : vars.fileType, + tinfo1 : vars.tinfo1, + tinfo2 : vars.tinfo2, + tinfo3 : vars.tinfo3, + tinfo4 : vars.tinfo4, + numComments : vars.numComments, + flags : vars.flags, + tinfos : vars.tinfos, + }; + + var dt = SAUCE_DATA_TYPES[sauce.dataType]; + if(dt && dt.parser) { + sauce[dt.name] = dt.parser(sauce); + } + + cb(null, sauce); + }); +} + +// :TODO: These need completed: +var SAUCE_DATA_TYPES = {}; +SAUCE_DATA_TYPES[0] = { name : 'None' }; +SAUCE_DATA_TYPES[1] = { name : 'Character', parser : parseCharacterSAUCE }; +SAUCE_DATA_TYPES[2] = 'Bitmap'; +SAUCE_DATA_TYPES[3] = 'Vector'; +SAUCE_DATA_TYPES[4] = 'Audio'; +SAUCE_DATA_TYPES[5] = 'BinaryText'; +SAUCE_DATA_TYPES[6] = 'XBin'; +SAUCE_DATA_TYPES[7] = 'Archive'; +SAUCE_DATA_TYPES[8] = 'Executable'; + +var SAUCE_CHARACTER_FILE_TYPES = {}; +SAUCE_CHARACTER_FILE_TYPES[0] = 'ASCII'; +SAUCE_CHARACTER_FILE_TYPES[1] = 'ANSi'; +SAUCE_CHARACTER_FILE_TYPES[2] = 'ANSiMation'; +SAUCE_CHARACTER_FILE_TYPES[3] = 'RIP script'; +SAUCE_CHARACTER_FILE_TYPES[4] = 'PCBoard'; +SAUCE_CHARACTER_FILE_TYPES[5] = 'Avatar'; +SAUCE_CHARACTER_FILE_TYPES[6] = 'HTML'; +SAUCE_CHARACTER_FILE_TYPES[7] = 'Source'; +SAUCE_CHARACTER_FILE_TYPES[8] = 'TundraDraw'; + +// +// Map of SAUCE font -> encoding hint +// +// Note that this is the same mapping that x84 uses. Be compatible! +// +var SAUCE_FONT_TO_ENCODING_HINT = { + 'Amiga MicroKnight' : 'amiga', + 'Amiga MicroKnight+' : 'amiga', + 'Amiga mOsOul' : 'amiga', + 'Amiga P0T-NOoDLE' : 'amiga', + 'Amiga Topaz 1' : 'amiga', + 'Amiga Topaz 1+' : 'amiga', + 'Amiga Topaz 2' : 'amiga', + 'Amiga Topaz 2+' : 'amiga', + 'Atari ATASCII' : 'atari', + 'IBM EGA43' : 'cp437', + 'IBM EGA' : 'cp437', + 'IBM VGA25G' : 'cp437', + 'IBM VGA50' : 'cp437', + 'IBM VGA' : 'cp437', +}; + +['437', '720', '737', '775', '819', '850', '852', '855', '857', '858', +'860', '861', '862', '863', '864', '865', '866', '869', '872'].forEach(function onPage(page) { + var codec = 'cp' + page; + SAUCE_FONT_TO_ENCODING_HINT['IBM EGA43 ' + page] = codec; + SAUCE_FONT_TO_ENCODING_HINT['IBM EGA ' + page] = codec; + SAUCE_FONT_TO_ENCODING_HINT['IBM VGA25g ' + page] = codec; + SAUCE_FONT_TO_ENCODING_HINT['IBM VGA50 ' + page] = codec; + SAUCE_FONT_TO_ENCODING_HINT['IBM VGA ' + page] = codec; +}); + +function parseCharacterSAUCE(sauce) { + var result = {}; + + result.fileType = SAUCE_CHARACTER_FILE_TYPES[sauce.fileType] || 'Unknown'; + + if(sauce.fileType === 0 || sauce.fileType === 1 || sauce.fileType === 2) { + var i = 0; + while(i < sauce.tinfos.length && sauce.tinfos[i] !== 0x00) { + ++i; + } + var fontName = sauce.tinfos.slice(0, i).toString('cp437'); + if(fontName.length > 0) { + result.fontName = fontName; + } + } + + return result; +} + +function sliceAtEOF(data, eofMarker) { + var eof = data.length; + // :TODO: max scan back or other beter way of doing this?! + for(var i = data.length - 1; i > 0; i--) { + if(data[i] === eofMarker) { + eof = i; + break; + } + } + return data.slice(0, eof); +} + +function getArtFromPath(path, options, cb) { + fs.readFile(path, function onData(err, data) { + if(err) { + cb(err); + return; + } + + // + // Convert from encodedAs -> j + // + var ext = paths.extname(path).toLowerCase(); + var encoding = options.encodedAs || defaultEncodingFromExtension(ext); + + // :TODO: how are BOM's currently handled if present? Are they removed? Do we need to? + + function sliceOfData() { + if(options.fullFile === true) { + return iconv.decode(data, encoding); + } else { + var eofMarker = defaultEofFromExtension(ext); + return iconv.decode(sliceAtEOF(data, eofMarker), encoding); + } + } + + function getResult(sauce) { + var result = { + data : sliceOfData(), + fromPath : path, + }; + + if(sauce) { + result.sauce = sauce; + } + + return result; + } + + if(options.readSauce === true) { + readSAUCE(data, function onSauce(err, sauce) { + if(err) { + cb(null, getResult()); + } else { + // + // If a encoding was not provided & we have a mapping from + // the information provided by SAUCE, use that. + // + if(!options.encodedAs) { + if(sauce.Character && sauce.Character.fontName) { + var enc = SAUCE_FONT_TO_ENCODING_HINT[sauce.Character.fontName]; + if(enc) { + encoding = enc; + } + } + } + cb(null, getResult(sauce)); + } + }); + } else { + cb(null, getResult()); + } + }); +} + +function getArt(name, options, cb) { + var ext = paths.extname(name); + + options.basePath = miscUtil.valueWithDefault(options.basePath, conf.config.paths.art); + options.asAnsi = miscUtil.valueWithDefault(options.asAnsi, true); + + // :TODO: make use of asAnsi option and convert from supported -> ansi + + if('' !== ext) { + options.types = [ ext.toLowerCase() ]; + } else { + if(typeof options.types === 'undefined') { + options.types = Object.keys(SUPPORTED_ART_TYPES); + } else if(typeof options.types === 'string') { + options.types = [ options.types.toLowerCase() ]; + } + } + + // If an extension is provided, just read the file now + if('' !== ext) { + var directPath = paths.join(options.basePath, name); + getArtFromPath(directPath, options, cb); + return; + } + + fs.readdir(options.basePath, function onFiles(err, files) { + if(err) { + cb(err); + return; + } + + var filtered = files.filter(function onFile(file) { + // + // Ignore anything not allowed in |options.types| + // + var fext = paths.extname(file); + if(options.types.indexOf(fext.toLowerCase()) < 0) { + return false; + } + + var bn = paths.basename(file, fext).toLowerCase(); + if(options.random) { + var suppliedBn = paths.basename(name, fext).toLowerCase(); + // + // Random selection enabled. We'll allow for + // basename1.ext, basename2.ext, ... + // + if(bn.indexOf(suppliedBn) !== 0) { + return false; + } + var num = bn.substr(suppliedBn.length); + if(num.length > 0) { + if(isNaN(parseInt(num, 10))) { + return false; + } + } + } else { + // + // We've already validated the extension (above). Must be an exact + // match to basename here + // + if(bn != paths.basename(name, fext).toLowerCase()) { + return false; + } + } + return true; + }); + + if(filtered.length > 0) { + // + // We should now have: + // - Exactly (1) item in |filtered| if non-random + // - 1:n items in |filtered| to choose from if random + // + var readPath; + if(options.random) { + readPath = paths.join(options.basePath, filtered[Math.floor(Math.random() * filtered.length)]); + } else { + assert(1 === filtered.length); + readPath = paths.join(options.basePath, filtered[0]); + } + + getArtFromPath(readPath, options, cb); + } else { + cb(new Error('No matching art for supplied criteria')); + } + }); +} + +// :TODO: need a showArt() +// - center (if term width > 81) +// - interruptable +// - pausable: by user key and/or by page size (e..g term height) + + +function defaultEncodingFromExtension(ext) { + return SUPPORTED_ART_TYPES[ext.toLowerCase()].defaultEncoding; +} + +function defaultEofFromExtension(ext) { + return SUPPORTED_ART_TYPES[ext.toLowerCase()].eof; +} + +function ArtDisplayer(client) { + if(!(this instanceof ArtDisplayer)) { + return new ArtDisplayer(client); + } + + events.EventEmitter.call(this); + + this.client = client; +} + +util.inherits(ArtDisplayer, events.EventEmitter); + +// :TODO: change to display(art, options, cb) +// cb(err, mci) + +function display(art, options, cb) { + if(!art || 0 === art.length) { + cb(new Error('Missing or empty art')); + return; + } + + if('undefined' === typeof options) { + cb(new Error('Missing options')); + return; + } + + if('undefined' === typeof options.client) { + cb(new Error('Missing client in options')); + return; + } + + var cancelKeys = miscUtil.valueWithDefault(options.cancelKeys, []); + var pauseKeys = miscUtil.valueWithDefault(options.pauseKeys, []); + var pauseAtTermHeight = miscUtil.valueWithDefault(options.pauseAtTermHeight, false); + var mciReplaceChar = miscUtil.valueWithDefault(options.mciReplaceChar, ''); + + // :TODO: support pause/cancel & pause @ termHeight + var canceled = false; + + var parser = new aep.ANSIEscapeParser({ + mciReplaceChar : mciReplaceChar, + termHeight : options.client.term.termHeight, + termWidth : options.client.term.termWidth, + }); + + var mci = {}; + var mciPosQueue = []; + var emitter = null; + var parseComplete = false; + + parser.on('mci', function onMCI(mciCode, args) { + if(mci[mciCode]) { + mci[mciCode].altColor = { + fg : parser.fgColor, + bg : parser.bgColor, + flags : parser.flags, + }; + } else { + mci[mciCode] = { + args : args, + color : { + fg : parser.fgColor, + bg : parser.bgColor, + flags : parser.flags, + } + }; + + mciPosQueue.push(mciCode); + + if(!emitter) { + emitter = options.client.on('onPosition', function onPosition(pos) { + if(mciPosQueue.length > 0) { + var forMciCode = mciPosQueue.shift(); + mci[forMciCode].position = pos; + + if(parseComplete && 0 === mciPosQueue.length) { + cb(null, mci); + } + } + }); + } + + options.client.term.write(ansi.queryPos()); + } + }); + + parser.on('chunk', function onChunk(chunk) { + options.client.term.write(chunk); + }); + + parser.on('complete', function onComplete() { + parseComplete = true; + + if(0 === mciPosQueue.length) { + cb(null, mci); + } + }); + + parser.parse(art); +} + +ArtDisplayer.prototype.display = function(art, options) { + var client = this.client; + var self = this; + + var cancelKeys = miscUtil.valueWithDefault(options.cancelKeys, []); + var pauseKeys = miscUtil.valueWithDefault(options.pauseKeys, []); + var pauseAtTermHeight = miscUtil.valueWithDefault(options.pauseAtTermHeight, false); + + var canceled = false; + if(cancelKeys.length > 0 || pauseKeys.length > 0) { + var onDataKeyCheck = function(data) { + var key = String.fromCharCode(data[0]); + if(-1 !== cancelKeys.indexOf(key)) { + canceled = true; + removeDataListener(); + } + }; + client.on('data', onDataKeyCheck); + } + + function removeDataListener() { + client.removeListener('data', onDataKeyCheck); + } + + // + // Try to split lines supporting various linebreaks we may encounter: + // - DOS \r\n + // - *nix \n + // - Old Apple \r + // - Unicode PARAGRAPH SEPARATOR (U+2029) and LINE SEPARATOR (U+2028) + // + // See also http://stackoverflow.com/questions/5034781/js-regex-to-split-by-line + // + var lines = art.split(/\r?\n|\r|[\u2028\u2029]/); + var i = 0; + var count = lines.length; + if(0 === count) { + return; + } + + var termHeight = client.term.termHeight; + + var aep = require('./ansi_escape_parser.js'); + var p = new aep.ANSIEscapeParser(); + + var currentRow = 0; + var lastRow = 0; + p.on('row update', function onRowUpdated(row) { + currentRow = row; + }); + + //-------- + var mci = {}; + var mciPosQueue = []; + var parseComplete = false; + + var emitter = null; + + p.on('mci', function onMCI(mciCode, args) { + if(mci[mciCode]) { + mci[mciCode].fgColorAlt = p.fgColor; + mci[mciCode].bgColorAlt = p.bgColor; + mci[mciCode].flagsAlt = p.flags; + } else { + mci[mciCode] = { + args : args, + fgColor : p.fgColor, + bgColor : p.bgColor, + flags : p.flags, + }; + + mciPosQueue.push(mciCode); + + if(!emitter) { + emitter = client.on('onPosition', function onPosition(pos) { + if(mciPosQueue.length > 0) { + var mc = mciPosQueue.shift(); + console.log('position @ ' + mc + ': ' + pos); + mci[mc].pos = pos; + + if(parseComplete && 0 === mciPosQueue.length) { + //console.log(mci); + var p1 = mci['LV1'].pos; + client.term.write(ansi.sgr(['red'])); + var g = ansi.goto(p1); + console.log(g); + client.term.write(ansi.goto(p1[0], p1[1])); + client.term.write('Hello, World'); + } + } + }); + } + } + }); + + p.on('chunk', function onChunk(chunk) { + client.term.write(chunk); + }); + + p.on('complete', function onComplete() { + //console.log(mci); + parseComplete = true; + if(0 === mciPosQueue.length) { + console.log('mci from complete'); + console.log(mci); + } + }); + + p.parse(art); + + + //----------- + /* + var line; + (function nextLine() { + if(i === count) { + self.emit('complete'); + removeDataListener(); + return; + } + + if(canceled) { + self.emit('canceled'); + removeDataListener(); + return; + } + + line = lines[i]; + client.term.write(line + '\n'); + p.parse(line + '\r\n'); + i++; + + if(pauseAtTermHeight && currentRow !== lastRow && (0 === currentRow % termHeight)) { + lastRow = currentRow; + client.getch(function onKey(k) { + nextLine(); + }); + } else { + setTimeout(nextLine, 20); + } + })(); + + */ + +/* + + (function nextLine() { + if(i === count) { + client.emit('complete', true); + removeDataListener(); + return; + } + + if(canceled) { + console.log('canceled'); + client.emit('canceled'); + removeDataListener(); + return; + } + + client.term.write(lines[i] + '\n'); + + // + // :TODO: support pauseAtTermHeight: + // + // - All cursor movement should be recorded for pauseAtTermHeight support & + // handling > termWidth scenarios + // - MCI codes should be processed + // - All other ANSI/CSI ignored + // - Count normal chars + // + + //setTimeout(nextLine, 20); + //i++; + + if(pauseAtTermHeight && i > 0 && (0 === i % termHeight)) { + console.log('pausing @ ' + i); + client.getch(function onKey() { + i++; + nextLine(); + }); + } else { + i++; + // :TODO: If local, use setTimeout(nextLine, 20) or so -- allow to pause/cancel + //process.nextTick(nextLine); + setTimeout(nextLine, 20); + } + + })(); +*/ +}; + +// +// ANSI parser for quick scanning & handling +// of basic ANSI sequences that can be used for output to clients: +// +function ANSIOutputParser(ansi) { + // + // cb's + // - onMCI + // - onTermHeight + // - +} \ No newline at end of file diff --git a/core/bbs.js b/core/bbs.js new file mode 100644 index 00000000..23a9a18f --- /dev/null +++ b/core/bbs.js @@ -0,0 +1,155 @@ +/* jslint node: true */ +'use strict'; + +// ENiGMA½ +var conf = require('./config.js'); +var modules = require('./modules.js'); +var logger = require('./logger.js'); +var miscUtil = require('./misc_util.js'); + +var iconv = require('iconv-lite'); +var paths = require('path'); + +exports.bbsMain = function() { + var mainArgs = parseArgs(); + + var configPathSupplied = false; + var configPath = conf.defaultPath(); + + if(mainArgs.indexOf('--help') > 0) { + // :TODO: display help + } else { + var argCount = mainArgs.length; + for(var i = 0; i < argCount; ++i) { + var arg = mainArgs[i]; + if('--config' == arg) { + configPathSupplied = true; + configPath = mainArgs[i + 1]; + } + } + } + + try { + conf.initFromFile(configPath); + } catch(e) { + // + // If the user supplied a config and we can't read, parse, whatever + // then output a error and bail. + // + if(configPathSupplied) { + if(e.code === 'ENOENT') { + console.error('Configuration file does not exist: ' + configPath); + } + return; + } + + console.log('No configuration file found, creating defaults.'); + conf.createDefault(); + } + + logger.init(); + + preServingInit(); + + startListening(); + + process.on('SIGINT', function onSigInt() { + // :TODO: for any client in |clientConnections|, if 'ready', send a "Server Disconnecting" + semi-gracefull hangup + // e.g. client.disconnectNow() + + logger.log.info('Process interrupted, shutting down'); + process.exit(); + }); +}; + +function parseArgs() { + var args = []; + process.argv.slice(2).forEach(function(val, index, array) { + args.push(val); + }); + + return args; +} + +function preServingInit() { + iconv.extendNodeEncodings(); +} + +var clientConnections = []; + +function startListening() { + if(!conf.config.servers) { + // :TODO: Log error ... output to stderr as well. We can do it all with the logger + return []; + } + + modules.loadModulesForCategory('servers', function onServerModule(err, module) { + if(err) { + logger.log.info(err); + return; + } + + var port = parseInt(module.runtime.config.port); + if(isNaN(port)) { + logger.log.error({ port : module.runtime.config.port, server : module.moduleInfo.name }, 'Cannot load server (Invalid port)'); + return; + } + + var server = module.createServer(); + + // :TODO: handle maxConnections, e.g. conf.maxConnections + + server.on('client', function onClient(client) { + // + // Start tracking the client. We'll assign it an ID which is + // just the index in our connections array. + // + if(typeof client.runtime === 'undefined') { + client.runtime = {}; + } + + addNewClient(client); + //client.runtime.id = clientConnections.push(client) - 1; + + //logger.log.info({ clientId : client.runtime.id, from : client.address(), server : module.moduleInfo.name }, 'Client connected'); + + client.on('ready', function onClientReady() { + // Go to module -- use default error handler + modules.goto(conf.config.entryMod, client); + }); + + client.on('end', function onClientEnd() { + logger.log.info({ clientId : client.runtime.id }, 'Client disconnected'); + + removeClient(client); + }); + + client.on('error', function onClientError(err) { + logger.log.info({ clientId : client.runtime.id }, 'Connection error: %s' % err.message); + }); + + client.on('close', function onClientClose(hadError) { + var l = hadError ? logger.log.info : logger.log.debug; + l({ clientId : client.runtime.id }, 'Connection closed'); + removeClient(client); + }); + }); + + server.listen(port); + logger.log.info({ server : module.moduleInfo.name, port : port }, 'Listening for connections'); + }); +} + +function addNewClient(client) { + var id = client.runtime.id = clientConnections.push(client) - 1; + logger.log.debug('Connection count is now %d', clientConnections.length); + return id; +} + +function removeClient(client) { + var i = clientConnections.indexOf(client); + if(i > -1) { + clientConnections.splice(i, 1); + logger.log.debug('Connection count is now %d', clientConnections.length); + } +} \ No newline at end of file diff --git a/core/client.js b/core/client.js new file mode 100644 index 00000000..b2bc01a7 --- /dev/null +++ b/core/client.js @@ -0,0 +1,89 @@ +/* jslint node: true */ +'use strict'; + +var stream = require('stream'); +var term = require('./client_term.js'); +var assert = require('assert'); +var miscUtil = require('./misc_util.js'); +var ansi = require('./ansi_term.js'); +var logger = require('./logger.js'); + +exports.Client = Client; + +function Client(input, output) { + stream.call(this); + + var self = this; + + this.input = input; + this.output = output; + this.term = new term.ClientTerminal(this.output); + + self.on('data', function onData(data) { + console.log('data: ' + data.length); + handleANSIControlResponse(data); + }); + + function handleANSIControlResponse(data) { + console.log(data); + ansi.forEachControlCode(data, function onControlResponse(name, params) { + var eventName = 'on' + name[0].toUpperCase() + name.substr(1); + console.log(eventName + ': ' + params); + self.emit(eventName, params); + }); + } +} + +require('util').inherits(Client, stream); + +Client.prototype.end = function () { + return this.output.end.apply(this.output, arguments); +}; + +Client.prototype.destroy = function () { + return this.output.destroy.apply(this.output, arguments); +}; + +Client.prototype.destroySoon = function () { + return this.output.destroySoon.apply(this.output, arguments); +}; + +Client.prototype.getch = function(cb) { + this.input.once('data', function onData(data) { + // :TODO: needs work. What about F keys and the like? + assert(data.length === 1); + cb(data); + }); +}; + +Client.prototype.address = function() { + return this.input.address(); +}; + +/////////////////////////////////////////////////////////////////////////////// +// Default error handlers +/////////////////////////////////////////////////////////////////////////////// + +Client.prototype.defaultHandlerMissingMod = function(err) { + var self = this; + + function handler(err) { + logger.log.error(err); + + self.term.write('An unrecoverable error has been encountered!\n'); + self.term.write('This has been logged for your SysOp to review.\n'); + self.term.write('\nGoodbye!\n'); + + + //self.term.write(err); + + //if(miscUtil.isDevelopment() && err.stack) { + // self.term.write('\n' + err.stack + '\n'); + //} + + self.end(); + } + + return handler; +}; + diff --git a/core/client_term.js b/core/client_term.js new file mode 100644 index 00000000..df569366 --- /dev/null +++ b/core/client_term.js @@ -0,0 +1,101 @@ +/* jslint node: true */ +'use strict'; + +// ENiGMA½ +var logger = require('./logger.js'); + +var iconv = require('iconv-lite'); +var assert = require('assert'); + +iconv.extendNodeEncodings(); + +exports.ClientTerminal = ClientTerminal; + +function ClientTerminal(output) { + this.output = output; + + var self = this; + + var outputEncoding = 'cp437'; + assert(iconv.encodingExists(outputEncoding)); + + // convert line feeds such as \n -> \r\n + this.convertLF = true; + + // + // Some terminal we handle specially + // They can also be found in this.env{} + // + var termType = 'unknown'; + var termHeight = 0; + var termWidth = 0; + + // Raw values set by e.g. telnet NAWS, ENVIRONMENT, etc. + this.env = {}; + + Object.defineProperty(this, 'outputEncoding', { + get : function() { + return outputEncoding; + }, + set : function(enc) { + if(iconv.encodingExists(enc)) { + outputEncoding = enc; + } else { + logger.log.warn({ encoding : enc }, 'Unknown encoding'); + } + } + }); + + Object.defineProperty(this, 'termType', { + get : function() { + return termType; + }, + set : function(ttype) { + termType = ttype.toLowerCase(); + + // + // ANSI terminals should be encoded to CP437 + // + if('ansi' == termType) { + this.outputEncoding = 'cp437'; + } else { + // :TODO: See how x84 does this -- only set if local/remote are binary + this.outputEncoding = 'utf8'; + } + } + }); + + Object.defineProperty(this, 'termWidth', { + get : function() { + return termWidth; + }, + set : function(width) { + if(width > 0) { + termWidth = width; + } + } + }); + + Object.defineProperty(this, 'termHeight', { + get : function() { + return termHeight; + }, + set : function(height) { + if(height > 0) { + termHeight = height; + } + } + }); +} + +ClientTerminal.prototype.isANSI = function() { + return 'ansi' === this.termType; +}; + +ClientTerminal.prototype.write = function(s) { + if(this.convertLF && typeof s === 'string') { + s = s.replace(/\n/g, '\r\n'); + } + + this.output.write(iconv.encode(s, this.outputEncoding)); +}; diff --git a/core/config.js b/core/config.js new file mode 100644 index 00000000..6614c833 --- /dev/null +++ b/core/config.js @@ -0,0 +1,50 @@ +/* jslint node: true */ +'use strict'; + +var fs = require('fs'); +var paths = require('path'); +var miscUtil = require('./misc_util.js'); + +module.exports = { + config : undefined, + + defaultPath : function() { + var base = miscUtil.resolvePath('~/'); + if(base) { + return paths.join(base, '.enigmabbs', 'config.json'); + } + }, + + initFromFile : function(path, cb) { + var data = fs.readFileSync(path, 'utf8'); + this.config = JSON.parse(data); + }, + + createDefault : function() { + this.config = { + bbsName : 'Another Fine ENiGMA½ BBS', + + entryMod : 'connect', + + paths : { + mods : paths.join(__dirname, './../mods/'), + servers : paths.join(__dirname, './servers/'), + art : paths.join(__dirname, './../mods/art/'), + logs : paths.join(__dirname, './../logs/'), // :TODO: set up based on system, e.g. /var/logs/enigmabbs or such + }, + + servers : { + telnet : { + port : 8888, + enabled : true, + }, + ssh : { + port : 8889, + enabled : false, + rsaPrivateKey : paths.join(__dirname, './../misc/default_key.rsa'), + dsaPrivateKey : paths.join(__dirname, './../misc/default_key.dsa'), + } + }, + }; + } +}; \ No newline at end of file diff --git a/core/line_editor.js b/core/line_editor.js new file mode 100644 index 00000000..b3cd1726 --- /dev/null +++ b/core/line_editor.js @@ -0,0 +1,64 @@ +"use strict"; + +var assert = require('assert'); +var miscUtil = require('./misc_util.js'); + +exports.LineEditor = LineEditor; + +var STANDARD_KEYSET = { + refresh : [ 12 ], + backspace : [ 8, 127 ], + backword : [ 23 ], + enter : [ 10 ], + exit : [ 27 ], +}; + +// :TODO: Rename to TextEdit +// :TODO: TextEdit should be single or multi line + + +function LineEditor(client, options) { + var self = this; + + self.client = client; + self.valueText = ''; + + if(typeof options !== 'undefined') { + self.options.keyset = miscUtil.valueWithDefault(options.keyset, STANDARD_KEYSET); + } else { + self.options = { + keyset : STANDARD_KEYSET, + }; + } + + + this.client.on('data', function onData(data) { + assert(1 === data.length); + self.onCh(data); + + }); +}; + +LineEditor.prototype.isKey = function(setName, ch) { + return this.options.keyset[setName].indexOf(ch) > -1; +} + +LineEditor.prototype.onCh = function(ch) { + if(this.isKey('refresh', ch)) { + + } else if(this.isKey('backspace', ch)) { + + } else if(this.isKey('backword', ch)) { + + } else if(this.isKey('enter', ch)) { + + } else if(this.isKey('exit', ch)) { + + } else { + + // :TODO: filter out chars + // :TODO: check max width + this.valueText += ch; + this.client.term.write(ch); + } +}; diff --git a/core/logger.js b/core/logger.js new file mode 100644 index 00000000..a9c35e18 --- /dev/null +++ b/core/logger.js @@ -0,0 +1,39 @@ +"use strict"; + +var bunyan = require('bunyan'); +var miscUtil = require('./misc_util.js'); +var paths = require('path'); +var conf = require('./config.js'); + +module.exports = { + log : undefined, + + init : function() { + //var ringBufferLimit = miscUtil.valueWithDefault(config.logRingBufferLimit, 100); + var logPath = miscUtil.valueWithDefault(conf.config.paths.logs); + var logFile = paths.join(logPath, 'enigma-bbs.log'); + + // :TODO: make this configurable -- + // user should be able to configure rotations, levels to file vs ringBuffer, + // completely disable logging, etc. + + this.log = bunyan.createLogger({ + name : 'ENiGMA½ BBS', + streams : [ + { + type : 'rotating-file', + path : logFile, + period : '1d', + count : 3, + level : 'trace' + } + /*, + { + type : 'raw', + stream : ringBuffer, + level : 'trace' + }*/ + ] + }); + } +}; diff --git a/core/misc_util.js b/core/misc_util.js new file mode 100644 index 00000000..1a19dc5d --- /dev/null +++ b/core/misc_util.js @@ -0,0 +1,28 @@ +"use strict"; + +var paths = require('path'); + +exports.isProduction = isProduction; +exports.isDevelopment = isDevelopment; +exports.valueWithDefault = valueWithDefault; +exports.resolvePath = resolvePath; + +function isProduction() { + var env = process.env.NODE_ENV || 'dev'; + return 'production' === env; +}; + +function isDevelopment() { + return (!(isProduction())); +}; + +function valueWithDefault(val, defVal) { + return (typeof val !== 'undefined' ? val : defVal); +}; + +function resolvePath(path) { + if(path.substr(0, 2) === '~/') { + path = (process.env.HOME || process.env.HOMEPATH || process.env.HOMEDIR || process.cwd()) + path.substr(1); + } + return paths.resolve(path); +}; \ No newline at end of file diff --git a/core/modules.js b/core/modules.js new file mode 100644 index 00000000..b2ca3464 --- /dev/null +++ b/core/modules.js @@ -0,0 +1,85 @@ +/* jslint node: true */ +'use strict'; + +var fs = require('fs'); +var paths = require('path'); +var conf = require('./config.js'); +var miscUtil = require('./misc_util.js'); +var logger = require('./logger.js'); + +// exports +exports.loadModule = loadModule; +exports.loadModulesForCategory = loadModulesForCategory; + +exports.goto = goto; + +function loadModule(name, category, cb) { + var config = conf.config; + var path = config.paths[category]; + + if(!path) { + cb(new Error('not sure where to look for "' + name + '" of category "' + category + '"')); + return; + } + + // update conf to point at this module's section, if any + config = config[category] ? config[category][name] : null; + + if(config && false === config.enabled) { + cb(new Error('module "' + name + '" is disabled')); + return; + } + + try { + var mod = require(paths.join(path, name + '.js')); + + if(!mod.moduleInfo) { + cb(new Error('module is missing \'moduleInfo\' section')); + return; + } + + mod.runtime = { + config : config + }; + + cb(null, mod); + } catch(e) { + cb(e); + } +}; + +function loadModulesForCategory(category, cb) { + var path = conf.config.paths[category]; + + fs.readdir(path, function onFiles(err, files) { + if(err) { + cb(err); + return; + } + + var filtered = files.filter(function onFilter(file) { return '.js' === paths.extname(file); }); + filtered.forEach(function onFile(file) { + var modName = paths.basename(file, '.js'); + loadModule(paths.basename(file, '.js'), category, cb); + }); + }); +}; + + +function goto(name, client, cb) { + // Assign a default missing module handler callback if none was provided + cb = miscUtil.valueWithDefault(cb, client.defaultHandlerMissingMod()); + + loadModule(name, 'mods', function onMod(err, mod) { + if(err) { + cb(err); + } else { + try { + logger.log.debug({ moduleName : name }, 'Goto module'); + mod.entryPoint(client); + } catch (e) { + cb(e); + } + } + }); +}; diff --git a/core/servers/ssh.js b/core/servers/ssh.js new file mode 100644 index 00000000..150b4c8c --- /dev/null +++ b/core/servers/ssh.js @@ -0,0 +1,30 @@ +"use strict"; + +var libssh = require('ssh'); +var conf = require('../config.js'); + +/* + Notes on getting libssh to work. This will ultimately require some contribs back + * Can't install without --nodedir= as had to upgrade node on the box for other reasons + * From ssh dir, node-gyp --nodedir=... configure build + * nan is out of date and doesn't work with existing node. Had to update. ( was "~0.6.0") (npm update after this) + * +*/ + +exports.moduleInfo = { + name : 'SSH', + desc : 'SSH Server', + author : 'NuSkooler' +}; + +function createServer() { + var server = libssh.createServer( + conf.config.servers.ssh.rsaPrivateKey, + conf.config.servers.ssh.dsaPrivateKey); + + server.on('connection', function onConnection(session) { + console.log('ermergerd') + }); + + return server; +} \ No newline at end of file diff --git a/core/servers/telnet.js b/core/servers/telnet.js new file mode 100644 index 00000000..a10a735c --- /dev/null +++ b/core/servers/telnet.js @@ -0,0 +1,729 @@ +/* jslint node: true */ +'use strict'; + +// ENiGMA½ +var baseClient = require('../client.js'); +var logger = require('../logger.js'); + +var net = require('net'); +var buffers = require('buffers'); +var binary = require('binary'); +var stream = require('stream'); +var assert = require('assert'); + +//var debug = require('debug')('telnet'); + +exports.moduleInfo = { + name : 'Telnet', + desc : 'Telnet Server', + author : 'NuSkooler' +}; + +exports.createServer = createServer; + +// +// Telnet Protocol Resources +// * http://pcmicro.com/netfoss/telnet.html +// * http://mud-dev.wikidot.com/telnet:negotiation +// + +/* + TODO: + * Document COMMANDS -- add any missing + * Document OPTIONS -- add any missing + * Internally handle OPTIONS: + * Some should be emitted generically + * Some shoudl be handled internally -- denied, handled, etc. + * + + * Allow term (ttype) to be set by environ sub negotiation + + * Process terms in loop.... research needed + + * Handle will/won't + * Handle do's, .. + * Some won't should close connection + + * Options/Commands we don't understand shouldn't crash the server!! + + +*/ + +var COMMANDS = { + SE : 240, // End of Sub-Negotation Parameters + NOP : 241, // No Operation + DM : 242, // Data Mark + BRK : 243, // Break + IP : 244, // Interrupt Process + AO : 245, // Abort Output + AYT : 246, // Are You There? + EC : 247, // Erase Character + EL : 248, // Erase Line + GA : 249, // Go Ahead + SB : 250, // Start Sub-Negotiation Parameters + WILL : 251, // + WONT : 252, + DO : 253, + DONT : 254, + IAC : 255, // (Data Byte) +}; + +// +// Resources: +// * http://www.faqs.org/rfcs/rfc1572.html +// +var SB_COMMANDS = { + IS : 0, + SEND : 1, + INFO : 2, +}; + +// +// Telnet Options +// +// Resources +// * http://mars.netanya.ac.il/~unesco/cdrom/booklet/HTML/NETWORKING/node300.html +// +var OPTIONS = { + TRANSMIT_BINARY : 0, // http://tools.ietf.org/html/rfc856 + ECHO : 1, // http://tools.ietf.org/html/rfc857 + // RECONNECTION : 2 + SUPPRESS_GO_AHEAD : 3, // aka 'SGA': RFC 858 @ http://tools.ietf.org/html/rfc858 + //APPROX_MESSAGE_SIZE : 4 + STATUS : 5, // http://tools.ietf.org/html/rfc859 + TIMING_MARK : 6, // http://tools.ietf.org/html/rfc860 + //RC_TRANS_AND_ECHO : 7, // aka 'RCTE' @ http://www.rfc-base.org/txt/rfc-726.txt + //OUPUT_LINE_WIDTH : 8, + //OUTPUT_PAGE_SIZE : 9, // + //OUTPUT_CARRIAGE_RETURN_DISP : 10, // RFC 652 + //OUTPUT_HORIZ_TABSTOPS : 11, // RFC 653 + //OUTPUT_HORIZ_TAB_DISP : 12, // RFC 654 + //OUTPUT_FORMFEED_DISP : 13, // RFC 655 + //OUTPUT_VERT_TABSTOPS : 14, // RFC 656 + //OUTPUT_VERT_TAB_DISP : 15, // RFC 657 + //OUTPUT_LF_DISP : 16, // RFC 658 + //EXTENDED_ASCII : 17, // RFC 659 + //LOGOUT : 18, // RFC 727 + //BYTE_MACRO : 19, // RFC 753 + //DATA_ENTRY_TERMINAL : 20, // RFC 1043 + //SUPDUP : 21, // RFC 736 + //SUPDUP_OUTPUT : 22, // RFC 749 + //SEND_LOCATION : 23, // RFC 779 + TERMINAL_TYPE : 24, // aka 'TTYPE': RFC 1091 @ http://tools.ietf.org/html/rfc1091 + //END_OF_RECORD : 25, // RFC 885 + //TACACS_USER_ID : 26, // RFC 927 + //OUTPUT_MARKING : 27, // RFC 933 + //TERMINCAL_LOCATION_NUMBER : 28, // RFC 946 + //TELNET_3270_REGIME : 29, // RFC 1041 + WINDOW_SIZE : 31, // aka 'NAWS': RFC 1073 @ http://tools.ietf.org/html/rfc1073 + TERMINAL_SPEED : 32, // RFC 1079 @ http://tools.ietf.org/html/rfc1079 + REMOTE_FLOW_CONTROL : 33, // RFC 1072 @ http://tools.ietf.org/html/rfc1372 + LINEMODE : 34, // RFC 1184 @ http://tools.ietf.org/html/rfc1184 + X_DISPLAY_LOCATION : 35, // aka 'XDISPLOC': RFC 1096 @ http://tools.ietf.org/html/rfc1096 + NEW_ENVIRONMENT_DEP : 36, // aka 'NEW-ENVIRON': RFC 1408 @ http://tools.ietf.org/html/rfc1408 (note: RFC 1572 is an update to this) + AUTHENTICATION : 37, // RFC 2941 @ http://tools.ietf.org/html/rfc2941 + ENCRYPT : 38, // RFC 2946 @ http://tools.ietf.org/html/rfc2946 + NEW_ENVIRONMENT : 39, // aka 'NEW-ENVIRON': RFC 1572 @ http://tools.ietf.org/html/rfc1572 (note: update to RFC 1408) + //TN3270E : 40, // RFC 2355 + //XAUTH : 41, + //CHARSET : 42, // RFC 2066 + //REMOTE_SERIAL_PORT : 43, + //COM_PORT_CONTROL : 44, // RFC 2217 + //SUPRESS_LOCAL_ECHO : 45, + //START_TLS : 46, + //KERMIT : 47, // RFC 2840 + //SEND_URL : 48, + //FORWARD_X : 49, + + //PRAGMA_LOGON : 138, + //SSPI_LOGON : 139, + //PRAGMA_HEARTBEAT : 140 + + EXTENDED_OPTIONS_LIST : 255, // RFC 861 (STD 32) +} + +// Commands used within NEW_ENVIRONMENT[_DEP] +var NEW_ENVIRONMENT_COMMANDS = { + VAR : 0, + VALUE : 1, + ESC : 2, + USERVAR : 3, +}; + +var IAC_BUF = new Buffer([ COMMANDS.IAC ]); +var SB_BUF = new Buffer([ COMMANDS.SB ]); +var SE_BUF = new Buffer([ COMMANDS.SE ]); +var IAC_SE_BUF = new Buffer([ COMMANDS.IAC, COMMANDS.SE ]); + +var COMMAND_NAMES = Object.keys(COMMANDS).reduce(function(names, name) { + names[COMMANDS[name]] = name.toLowerCase(); + return names; +}, {}); + +var COMMAND_IMPLS = {}; +['do', 'dont', 'will', 'wont', 'sb'].forEach(function(command) { + var code = COMMANDS[command.toUpperCase()]; + COMMAND_IMPLS[code] = function(bufs, i, event) { + if(bufs.length < (i + 1)) { + return MORE_DATA_REQUIRED; + } + return parseOption(bufs, i, event); + } +}); + +// :TODO: See TooTallNate's telnet.js: Handle COMMAND_IMPL for IAC in binary mode + +// Create option names such as 'transmit binary' -> OPTIONS.TRANSMIT_BINARY +var OPTION_NAMES = Object.keys(OPTIONS).reduce(function(names, name) { + names[OPTIONS[name]] = name.toLowerCase().replace(/_/g, ' '); + return names; +}, {}); + +var OPTION_IMPLS = {}; +// :TODO: fill in the rest... +OPTION_IMPLS.NO_ARGS = +OPTION_IMPLS[OPTIONS.ECHO] = +OPTION_IMPLS[OPTIONS.STATUS] = +OPTION_IMPLS[OPTIONS.LINEMODE] = +OPTION_IMPLS[OPTIONS.TRANSMIT_BINARY] = +OPTION_IMPLS[OPTIONS.AUTHENTICATION] = +OPTION_IMPLS[OPTIONS.TERMINAL_SPEED] = +//OPTION_IMPLS[OPTIONS.TERMINAL_TYPE] = +OPTION_IMPLS[OPTIONS.REMOTE_FLOW_CONTROL] = +OPTION_IMPLS[OPTIONS.X_DISPLAY_LOCATION] = + +OPTION_IMPLS[OPTIONS.SUPPRESS_GO_AHEAD] = function(bufs, i, event) { + event.buf = bufs.splice(0, i).toBuffer(); + return event; +}; + +OPTION_IMPLS[OPTIONS.TERMINAL_TYPE] = function(bufs, i, event) { + if(event.commandCode !== COMMANDS.SB) { + OPTION_IMPLS.NO_ARGS(bufs, i, event); + } else { + // We need 4 bytes header + data + IAC SE + if(bufs.length < 7) { + return MORE_DATA_REQUIRED; + } + + var end = bufs.indexOf(IAC_SE_BUF, 5); // look past header bytes + if(-1 === end) { + return MORE_DATA_REQUIRED; + } + + // eat up and process the header + var buf = bufs.splice(0, 4).toBuffer(); + binary.parse(buf) + .word8('iac1') + .word8('sb') + .word8('ttype') + .word8('is') + .tap(function(vars) { + assert(vars.iac1 === COMMANDS.IAC); + assert(vars.sb === COMMANDS.SB); + assert(vars.ttype === OPTIONS.TERMINAL_TYPE); + assert(vars.is === SB_COMMANDS.IS); + }); + + // eat up the rest + end -= 4; + buf = bufs.splice(0, end).toBuffer(); + + // + // From this point -> |end| is our ttype + // + // Look for trailing NULL(s). Client such as Netrunner do this. + // + var trimAt = 0; + for(; trimAt < buf.length; ++trimAt) { + if(0x00 === buf[trimAt]) { + break; + } + } + + event.ttype = buf.toString('ascii', 0, trimAt); + + // pop off the terminating IAC SE + bufs.splice(0, 2); + } + + return event; +}; + +OPTION_IMPLS[OPTIONS.WINDOW_SIZE] = function(bufs, i, event) { + if(event.commandCode !== COMMANDS.SB) { + OPTION_IMPLS.NO_ARGS(bufs, i, event); + } else { + // we need 9 bytes + if(bufs.length < 9) { + return MORE_DATA_REQUIRED; + } + + event.buf = bufs.splice(0, 9).toBuffer(); + binary.parse(event.buf) + .word8('iac1') + .word8('sb') + .word8('naws') + .word16bu('width') + .word16bu('height') + .word8('iac2') + .word8('se') + .tap(function(vars) { + assert(vars.iac1 == COMMANDS.IAC); + assert(vars.sb == COMMANDS.SB); + assert(vars.naws == OPTIONS.WINDOW_SIZE); + assert(vars.iac2 == COMMANDS.IAC); + assert(vars.se == COMMANDS.SE); + + event.cols = event.columns = event.width = vars.width; + event.rows = event.height = vars.height; + }); + } + return event; +}; + +// Build an array of delimiters for parsing NEW_ENVIRONMENT[_DEP] +var NEW_ENVIRONMENT_DELIMITERS = []; +Object.keys(NEW_ENVIRONMENT_COMMANDS).forEach(function onKey(k) { + NEW_ENVIRONMENT_DELIMITERS.push(NEW_ENVIRONMENT_COMMANDS[k]); +}); + +// Handle the deprecated RFC 1408 & the updated RFC 1572: +OPTION_IMPLS[OPTIONS.NEW_ENVIRONMENT_DEP] = +OPTION_IMPLS[OPTIONS.NEW_ENVIRONMENT] = function(bufs, i, event) { + if(event.commandCode !== COMMANDS.SB) { + OPTION_IMPLS.NO_ARGS(bufs, i, event); + } else { + // We need 4 bytes header + payload + IAC SE + if(bufs.length < 7) { + return MORE_DATA_REQUIRED; + } + + var end = bufs.indexOf(IAC_SE_BUF, 5); // look past header bytes + if(-1 === end) { + return MORE_DATA_REQUIRED; + } + + // eat up and process the header + var buf = bufs.splice(0, 4).toBuffer(); + binary.parse(buf) + .word8('iac1') + .word8('sb') + .word8('newEnv') + .word8('isOrInfo') // initial=IS, updates=INFO + .tap(function(vars) { + assert(vars.iac1 === COMMANDS.IAC); + assert(vars.sb === COMMANDS.SB); + assert(vars.newEnv === OPTIONS.NEW_ENVIRONMENT || vars.newEnv === OPTIONS.NEW_ENVIRONMENT_DEP); + assert(vars.isOrInfo === SB_COMMANDS.IS || vars.isOrInfo === SB_COMMANDS.INFO); + + event.type = vars.isOrInfo; + + if(vars.newEnv === OPTIONS.NEW_ENVIRONMENT_DEP) { + logger.log.warn('Handling deprecated RFC 1408 NEW-ENVIRON'); + } + }); + + // eat up the rest + end -= 4; + buf = bufs.splice(0, end).toBuffer(); + + // + // This part can become messy. The basic spec is: + // IAC SB NEW-ENVIRON IS type ... [ VALUE ... ] [ type ... [ VALUE ... ] [ ... ] ] IAC SE + // + // See RFC 1572 @ http://www.faqs.org/rfcs/rfc1572.html + // + // Start by splitting up the remaining buffer. Keep the delimiters + // as prefixes we can use for processing. + // + // :TODO: Currently not supporting ESCaped values (ESC + ). Probably not really in the wild, but we should be compliant + var params = []; + var p = 0; + for(var j = 0, l = buf.length; j < l; ++j) { + if(NEW_ENVIRONMENT_DELIMITERS.indexOf(buf[j]) === -1) { + continue; + } + + params.push(buf.slice(p, j)); + p = j; + } + + // remainder + if(p < l) { + params.push(buf.slice(p, l)); + } + + var varName; + event.envVars = {}; + // :TODO: handle cases where a variable was present in a previous exchange, but missing here...e.g removed + for(var j = 0; j < params.length; ++j) { + if(params[j].length < 2) { + continue; + } + + var cmd = params[j].readUInt8(); + if(cmd === NEW_ENVIRONMENT_COMMANDS.VAR || cmd === NEW_ENVIRONMENT_COMMANDS.USERVAR) { + varName = params[j].slice(1).toString('utf8'); // :TODO: what encoding should this really be? + } else { + event.envVars[varName] = params[j].slice(1).toString('utf8'); // :TODO: again, what encoding? + } + } + + // pop off remaining IAC SE + bufs.splice(0, 2); + } + + return event; +}; + +var MORE_DATA_REQUIRED = 0xfeedface; + +function parseBufs(bufs) { + assert(bufs.length >= 2); + assert(bufs.get(0) === COMMANDS.IAC) + return parseCommand(bufs, 1, {}); +} + +function parseCommand(bufs, i, event) { + var command = bufs.get(i); // :TODO: fix deprecation... [i] is not the same + event.commandCode = command; + event.command = COMMAND_NAMES[command]; + + var handler = COMMAND_IMPLS[command]; + if(handler) { + //return COMMAND_IMPLS[command](bufs, i + 1, event); + return handler(bufs, i + 1, event); + } else { + assert(2 == bufs.length); // IAC + COMMAND + event.buf = bufs.splice(0, 2).toBuffer(); + return event; + } +} + +function parseOption(bufs, i, event) { + var option = bufs.get(i); // :TODO: fix deprecation... [i] is not the same + event.optionCode = option; + event.option = OPTION_NAMES[option]; + return OPTION_IMPLS[option](bufs, i + 1, event); +} + + +function TelnetClient(input, output) { + baseClient.Client.apply(this, arguments); + + var self = this; + + var bufs = buffers(); + this.bufs = bufs; + + var readyFired = false; + var encodingSet = false; + + this.negotiationsComplete = false; // are we in the 'negotiation' phase? + this.didReady = false; // have we emit the 'ready' event? + + this.input.on('data', function onData(b) { + bufs.push(b); + + var i; + while((i = bufs.indexOf(IAC_BUF)) >= 0) { + // :TODO: Android client Irssi ConnectBot asserts here: + assert(bufs.length > (i + 1), 'bufs.length=' + bufs.length + ' i=' + i + ' bufs=' + bufs); + + if(i > 0) { + self.emit('data', bufs.splice(0, i).toBuffer()); + } + + i = parseBufs(bufs); + + if(MORE_DATA_REQUIRED === i) { + break; + } else { + //self.emit('event', i); // generic event + //self.emit(i.command, i); // "will", "wont", ... + + if(i.option) { + self.emit(i.option, i); // "transmit binary", "echo", ... + } + + self.handleTelnetEvent(i); + + if(i.data) { + self.emit('data', i.data); + } + } + } + + if(MORE_DATA_REQUIRED !== i && bufs.length > 0) { + // + // Standard data payload. This can still be "non-user" data + // such as ANSI control, but we don't handle that here. + // + self.emit('data', bufs.splice(0).toBuffer()); + } + + }); + + this.input.on('end', function() { + self.emit('end'); + }); +} + +require('util').inherits(TelnetClient, baseClient.Client); + +/////////////////////////////////////////////////////////////////////////////// +// Telnet Command/Option handling +/////////////////////////////////////////////////////////////////////////////// +TelnetClient.prototype.handleTelnetEvent = function(evt) { + // handler name e.g. 'handleWontCommand' + var handlerName = 'handle' + evt.command.charAt(0).toUpperCase() + evt.command.substr(1) + 'Command'; + + if(this[handlerName]) { + // specialized + this[handlerName](evt); + } else { + // generic-ish + this.handleMiscCommand(evt); + } +}; + +TelnetClient.prototype.handleWillCommand = function(evt) { + if('terminal type' === evt.option) { + // + // See RFC 1091 @ http://www.faqs.org/rfcs/rfc1091.html + // + this.requestTerminalType(); + } else if('environment variables' === evt.option) { + // + // See RFC 1572 @ http://www.faqs.org/rfcs/rfc1572.html + // + this.requestEnvironmentVariables(); + } else { + // :TODO: temporary: + console.log('will ' + JSON.stringify(evt)); + } +}; + +TelnetClient.prototype.handleWontCommand = function(evt) { + console.log('wont ' + JSON.stringify(evt)); +}; + +TelnetClient.prototype.handleDoCommand = function(evt) { + // :TODO: handle the rest, e.g. echo nd the like + + if('linemode' === evt.option) { + // + // Client wants to enable linemode editing. Denied. + // + this.wont.linemode(); + } else if('encrypt' === evt.option) { + // + // Client wants to enable encryption. Denied. + // + this.wont.encrypt(); + } else { + // :TODO: temporary: + console.log('do ' + JSON.stringify(evt)); + } +}; + +TelnetClient.prototype.handleDontCommand = function(evt) { + console.log('dont ' + JSON.stringify(evt)); +}; + +TelnetClient.prototype.setTermType = function(ttype) { + this.term.env['TERM'] = ttype; + this.term.termType = ttype; + + logger.log.debug({ termType : ttype }, 'Set terminal type'); +} + +TelnetClient.prototype.handleSbCommand = function(evt) { + var self = this; + + if('terminal type' === evt.option) { + // + // See RFC 1091 @ http://www.faqs.org/rfcs/rfc1091.html + // + // :TODO: According to RFC 1091 @ http://www.faqs.org/rfcs/rfc1091.html + // We should keep asking until we see a repeat. From there, determine the best type/etc. + self.setTermType(evt.ttype); + + self.negotiationsComplete = true; // :TODO: throw in a array of what we've taken care. Complete = array satisified or timeout + + if(!self.didReady) { + self.didReady = true; + self.emit('ready'); + } + } else if('new environment' === evt.option) { + // + // Handling is as follows: + // * Map 'TERM' -> 'termType' and only update if ours is 'unknown' + // * Map COLUMNS -> 'termWidth' and only update if ours is 0 + // * Map ROWS -> 'termHeight' and only update if ours is 0 + // * Add any new variables, ignore any existing + // + Object.keys(evt.envVars).forEach(function onEnv(name) { + if('TERM' === name && 'unknown' === self.term.termType) { + self.setTermType(evt.envVars[name]); + } else if('COLUMNS' === name && 0 === self.term.termWidth) { + self.term.termWidth = parseInt(evt.envVars[name]); + } else if('ROWS' === name && 0 === self.term.termHeight) { + self.term.termHeight = parseInt(evt.envVars[name]); + } else { + if(name in self.term.env) { + assert(evt.type === SB_COMMANDS.INFO); + + logger.log.warn( + { varName : name, value : evt.envVars[name], existingValue : self.term.env[name] }, + 'Environment variable already exists'); + } else { + self.term.env[name] = evt.envVars[name]; + } + } + }); + } else if('window size' === evt.option) { + // + // Update termWidth & termHeight. + // Set LINES and COLUMNS environment variables as well. + // + self.term.termWidth = evt.width; + self.term.termHeight = evt.height; + + if(evt.width > 0) { + self.term.env['COLUMNS'] = evt.height; + } + + if(evt.height > 0) { + self.term.env['ROWS'] = evt.height; + } + + logger.log.debug({ termWidth : evt.width , termHeight : evt.height }, 'Window size updated'); + } else { + console.log('unhandled SB: ' + JSON.stringify(evt)); + } +}; + +var IGNORED_COMMANDS = []; +[ COMMANDS.EL, COMMANDS.GA, COMMANDS.NOP, COMMANDS.DM, COMMANDS.BRK ].forEach(function onCommandCode(cc) { + IGNORED_COMMANDS.push(cc); +}); + + +TelnetClient.prototype.handleMiscCommand = function(evt) { + assert(evt.command !== 'undefined' && evt.command.length > 0); + + // + // See: + // * RFC 854 @ http://tools.ietf.org/html/rfc854 + // + if('ip' === evt.command) { + // Interrupt Process (IP) + logger.log.debug('Interrupt Process (IP) - Ending'); + this.input.end(); + } else if('ayt' === evt.command) { + this.output.write('\b'); + logger.log.debug('Are You There (AYT) - Replied "\\b"'); + } else if(IGNORED_COMMANDS.indexOf(evt.commandCode)) { + logger.log.debug({ evt : evt }, 'Ignoring command'); + } else { + logger.log.warn({ evt : evt }, 'Unknown command'); + } +}; + +TelnetClient.prototype.requestTerminalType = function() { + var buf = Buffer([ COMMANDS.IAC, COMMANDS.SB, OPTIONS.TERMINAL_TYPE, SB_COMMANDS.SEND, COMMANDS.IAC, COMMANDS.SE ]); + /* + var buf = Buffer(6); + buf[0] = COMMANDS.IAC; + buf[1] = COMMANDS.SB; + buf[2] = OPTIONS.TERMINAL_TYPE; + buf[3] = SB_COMMANDS.SEND; + buf[4] = COMMANDS.IAC; + buf[5] = COMMANDS.SE; + */ + + return this.output.write(buf); +}; + +var WANTED_ENVIRONMENT_VARIABLES = [ 'LINES', 'COLUMNS', 'TERM' ]; + +TelnetClient.prototype.requestEnvironmentVariables = function() { + var bufs = buffers(); + + bufs.push(new Buffer([ COMMANDS.IAC, COMMANDS.SB, OPTIONS.NEW_ENVIRONMENT, SB_COMMANDS.SEND ])); + + for(var i = 0; i < WANTED_ENVIRONMENT_VARIABLES.length; ++i) { + bufs.push(new Buffer( [ ENVIRONMENT_VARIABLES_COMMANDS.VAR ])); + bufs.push(new Buffer(WANTED_ENVIRONMENT_VARIABLES[i])); // :TODO: encoding here?! UTF-8 will work, but shoudl be more explicit + } + + bufs.push(new Buffer([ COMMANDS.IAC, COMMANDS.SE ])); + + return this.output.write(bufs.toBuffer()); +}; + +TelnetClient.prototype.banner = function() { + + // :TODO: See x84 implementation here. + // First, we should probably buffer then send. + + this.will.echo(); + + this.will.suppress_go_ahead(); + this.do.suppress_go_ahead(); + + this.do.transmit_binary(); + this.will.transmit_binary(); + + this.do.terminal_type(); + + this.do.window_size(); + this.do.new_environment(); +} + +function Command(command, client) { + this.command = COMMANDS[command.toUpperCase()]; + this.client = client; +}; + +// Create Command objects with echo, transmit_binary, ... +Object.keys(OPTIONS).forEach(function(name) { + var code = OPTIONS[name]; + + Command.prototype[name.toLowerCase()] = function() { + var buf = Buffer(3); + buf[0] = COMMANDS.IAC; + buf[1] = this.command; + buf[2] = code; + return this.client.output.write(buf); + } +}); + +// Create do, dont, etc. methods on Client +['do', 'dont', 'will', 'wont'].forEach(function(command) { + function get() { + return new Command(command, this); + } + + Object.defineProperty(TelnetClient.prototype, command, { + get : get, + enumerable : true, + configurable : true + }); +}); + + +function createServer() { + var server = net.createServer(function onConnection(sock) { + var self = this; + var client = new TelnetClient(sock, sock); + + client.banner(); + + self.emit('client', client); + }); + + return server; +}; diff --git a/main.js b/main.js new file mode 100644 index 00000000..1ad3b05b --- /dev/null +++ b/main.js @@ -0,0 +1,11 @@ +"use strict"; + +var net = require('net'); +var fs = require('fs'); +var paths = require('path'); + +// ENiGMA½ +var bbs = require('./core/bbs.js') + +// The entry point. +bbs.bbsMain(); \ No newline at end of file diff --git a/misc/default_key.dsa b/misc/default_key.dsa new file mode 100644 index 00000000..35cfd46b --- /dev/null +++ b/misc/default_key.dsa @@ -0,0 +1,12 @@ +-----BEGIN DSA PRIVATE KEY----- +MIIBugIBAAKBgQDA9HhqZZrq167Vz/AJECJGjJB0DKu72Qji0HfZxDBw/mrsRh89 +W/0LQSIOBROIGczGyxuelUAwQxzx5sN3LBABPGutKat4FbxsKO5Ix6VODt5gmT82 +eyXm9XpV49EDRoO3AxZJoc+Fmiy7meqtAoGNG9Y4K7fRsq9NTNMgt5w4RQIVAIDp +xj5whr2w95anI/UBs9GPvodpAoGABzR9OqO/yrgOmjq+vg6+T+RRQNB7dRjfsY9A +NJFKC15ZNsLFXJSyKV067GW/6AYYaqJokz93ZS/azuNRm4GesbvIltri3MXZ0cRF +m9zazKS4OlCwCySBIai4Hjv4oNSV+hiYqEsNYA8fubP9fILXioD0ojwIC7tJam9d +UTPOhNMCgYAh8P2Cu6hE6qJy9NToeaimRcIT3UCqnqORWrelOymmrX2v9wboB2DB +Ehm4RpJhPps8NnxIg4fDxARqBVpgAkizfWlzdJMw5JFz3qm5To7MMD4AJ9ZbtpHE +uTGglej7v5jTM+qk6hlB5mNgvqHgtKkyLLMZz2PTzRo1+bJ6bA0c2AIUFUxwM+1H +zd9qN20heajSqKqiZ4g= +-----END DSA PRIVATE KEY----- diff --git a/misc/default_key.dsa.pub b/misc/default_key.dsa.pub new file mode 100644 index 00000000..cd62c525 --- /dev/null +++ b/misc/default_key.dsa.pub @@ -0,0 +1 @@ +ssh-dss AAAAB3NzaC1kc3MAAACBAMD0eGplmurXrtXP8AkQIkaMkHQMq7vZCOLQd9nEMHD+auxGHz1b/QtBIg4FE4gZzMbLG56VQDBDHPHmw3csEAE8a60pq3gVvGwo7kjHpU4O3mCZPzZ7Jeb1elXj0QNGg7cDFkmhz4WaLLuZ6q0CgY0b1jgrt9Gyr01M0yC3nDhFAAAAFQCA6cY+cIa9sPeWpyP1AbPRj76HaQAAAIAHNH06o7/KuA6aOr6+Dr5P5FFA0Ht1GN+xj0A0kUoLXlk2wsVclLIpXTrsZb/oBhhqomiTP3dlL9rO41GbgZ6xu8iW2uLcxdnRxEWb3NrMpLg6ULALJIEhqLgeO/ig1JX6GJioSw1gDx+5s/18gteKgPSiPAgLu0lqb11RM86E0wAAAIAh8P2Cu6hE6qJy9NToeaimRcIT3UCqnqORWrelOymmrX2v9wboB2DBEhm4RpJhPps8NnxIg4fDxARqBVpgAkizfWlzdJMw5JFz3qm5To7MMD4AJ9ZbtpHEuTGglej7v5jTM+qk6hlB5mNgvqHgtKkyLLMZz2PTzRo1+bJ6bA0c2A== ENiGMA½ Default DSA Key diff --git a/misc/default_key.rsa b/misc/default_key.rsa new file mode 100644 index 00000000..1e122239 --- /dev/null +++ b/misc/default_key.rsa @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAns9TDs2FY3dXmGtE5a3tu5GS3I9/O9a9NU1yPrcuwhXgOL3D +hPYjbWT8ypKZyMMwdRNxmfnXNQjooBt2CdcVjezrWPTfexQRNNEfcQ7JmUPKrtQQ +ZZRU+ubFMycgNyDt3w3SwgisBFcb9qiFj1Y12jpKpkvnB+HtuV5HA0XODoI4OA93 +mef02q+rbHfyT3EDlwBLJcZiew/ALlOiB/bACHXkYoal979HRTV4aW8Xx2WxstLD +bBaZSuQJQFNLR2DsivHHs0JlxcO4D6oBiw8mTr1SRrunDldmndCYPp66gpsJf+7B +5PwrQ20hOG+UEp7CReBvX4y7AD2TP6nKjJ96EQIDAQABAoIBADQtMcC/TX/sid8N +/0jm6CCkJ3hHvSnP18JXe52XWclynoSF9GMO63zZnSSBHf2UA6uzg7NvHoHSN3vD +CfkZrX8/w+MqMPKjGTQdMRNYJGwylbjy+6AkJ/d3Mnsn87GpI/4Q8JnHd5zFce6M +V7VMQc09MaGJdpJkbFJm4zX0DUs+gN2liq+lOQWVBBY3baAS9ZhDGAnxgDzCteRQ +lJYbKbO4MqwNvzKRgUjCNMpf28tUXu1JzK7NosQ0eA8yz3il8MQuD7+1Kmaxrkhv +/FhV9GaVod4HB7iwVyM4cvwXDGtMg8W0IU6aaqmctWvEckCnImuqRrzxseeAQA8K +fgTeIwECgYEA0Q38Rk1SryCMKtJZisQCwsNN8eFs7tT9QhpJxQG4JsJtjid4q+Ze +YMOVDwSdlsPu3XPGql/88mawjOSQLw2bQ9kOOjm3/1G1XoKqCajmWyvYznLc22/b +wN3zeubkk2/PHPP8h43WZCLxnfbMGyEXxCmOZYz/AIv3CKlA5KnM9ZkCgYEAwnjl +ZB+kh8AnejDhcCiNbk0Bt8xYMHiewSU5jW3sCrXpcZ7fPLERJhxiMEVT7WfnIdb7 +z1OZOqEnl+XhqGIJRLRgM2FrFSHgWbdATcSNLYaMVrMwGOcaHB/Ww2ffVhRa2B5H +16yol6oeya7OubQE+4JeqPU3thCR+OGFCgl8AzkCgYAgLr/8bqDg9ui9Gt7P/vbR +1tEkAGkNp1pJl9RtcKNRzDKjZK4uAGvdB57lzfcYo5cdHlqiFd7Rzj81oWYU6EpT +upF3IzWRJ5n9L65PiGHV/T0GIyae3Wx+F20JKYxcqiS/N0xT7eDoT3zUrY6kSobn +GuKh/X+wLNvdyHiF+yHjWQKBgEWn6/Iq61G8X+eCbp0vyUjCyGS/Cq+B5MslvDGO +N8e2VMFfuHISsiPGctz7WA5m8wDJ8jPWq8G2S2o66tLXGp7DRNnigOp20dcQrjWs +uyydTtMXJ5kj3dRJCzG+mN5KOCfE4NK9gz2fr5tpw538fvRfQ+ycuLG7ya0tieuI +biTxAoGAZAglKOHJIqLXoBPT3GH2nDl0BDEJ/3Ob7g54nupz753LO/6MlFGGX7Cg +X360g9Snk08+pYDos6ayT/vmeegbqfV7A+1sXHsGGuYyaLSkTjdcIUV/eF5h/HbE +CnRfXZe2jsFzYNK835BcYIndDIElyW6jipkgkwwWCOMqPC9Ulto= +-----END RSA PRIVATE KEY----- diff --git a/misc/default_key.rsa.pub b/misc/default_key.rsa.pub new file mode 100644 index 00000000..f265c28d --- /dev/null +++ b/misc/default_key.rsa.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCez1MOzYVjd1eYa0Tlre27kZLcj3871r01TXI+ty7CFeA4vcOE9iNtZPzKkpnIwzB1E3GZ+dc1COigG3YJ1xWN7OtY9N97FBE00R9xDsmZQ8qu1BBllFT65sUzJyA3IO3fDdLCCKwEVxv2qIWPVjXaOkqmS+cH4e25XkcDRc4Ogjg4D3eZ5/Tar6tsd/JPcQOXAEslxmJ7D8AuU6IH9sAIdeRihqX3v0dFNXhpbxfHZbGy0sNsFplK5AlAU0tHYOyK8cezQmXFw7gPqgGLDyZOvVJGu6cOV2ad0Jg+nrqCmwl/7sHk/CtDbSE4b5QSnsJF4G9fjLsAPZM/qcqMn3oR ENiGMA½ Default RSA Key diff --git a/mods/art/CONNECT1.ANS b/mods/art/CONNECT1.ANS new file mode 100644 index 00000000..2c9425c6 Binary files /dev/null and b/mods/art/CONNECT1.ANS differ diff --git a/mods/art/DM-ENIG.ANS b/mods/art/DM-ENIG.ANS new file mode 100644 index 00000000..b09c5ad7 --- /dev/null +++ b/mods/art/DM-ENIG.ANS @@ -0,0 +1,19 @@ + °°°°°°°°°ܲÜÜ  °  ÜÜ  + °°°°°° ÜÜßÛÛÛ²ÛÛÛÛÛß  °°  °°° °°°° ° ÜÜÜßßßÜܲÛÛÛÛÛß + °ÜÜÜßßÞÛÛÛÛ²²ÛÝ °°  ÜÜÜ ÜÜ °ÜÜÜßßÛ²²ÛÛÝ +ßÜÜÜÛÛÛ²Û  °ÛÛÛ²²²±±ÜÜÜßßÛÛÛÛ²ÛßÛ²²²²±ÜÜܲ²²²°° °°ÞÛÛÛÛÛ² + ÞÛ²²ÛÛÛÝÞÛ²²±±±°Ý ²±ÜÜ°±ß Þ²²²²ÛÝ Þ²²±±±±±²²²±±±Þ²²ÛÛÛÝ + ÛÛÛÛ²²ß     ²²±±°°°ÛÛ° °°°°Ý  ±±±²² ²²±±°° °±±±°°°°±±²²ÛÝ   +°°²Û²²±°°°° °  ßßß°°ÛÛÜÞ°°°°±±°° °° °° °°±± Þ °±°°°°Þ °°°°°°° °° ° °°°°°±±²°°   °Þ²±±°° ÜÜ    °°±±²Û  Þ±±°°°  °°°±± Þ°°°°±Ý Ü ÞÛ °°±±  +Ü Þ±°° °°Ý  Ü ÜÛÛÜÜÜ ÜÛ²²²ÛÛÝÞܲß ÞÛ²²±°° °°±±±± Ü ±±±±²Û ÞÛÜÛÛ²Û ÞÛÛÛÛ°  ÞÜ  +°Ý ° °°±²Ý °°ÛÜ ÛÛ²²ÛÛÛÛÛÛ²ÛÛ²Ý °° ßßßßÛ²±±ßÞ±±²² Þ°Ý Û²²ÛÛÝ Û°°ÛÛÝ °°°°ÛÛÝ ÛÛ° +ß  °°°±±²² ßß²ß ÞÛÛÛÛÛÛ²ÛÛÛÛÛÛ² ßßß² ßß ²²ÛÝ ß²ß ÞÛ²Û²² ÜÜÜ ß Þ±±°°°Ý ß²ß +Þ±±±²²ÛÝ  ÛÛÛÛÛÛÛÛÛÛÛÛÛÛßßÛÛ²Ûßß ßßßßß Û²²±±±± +Û²²ÛÛÛÛ ÞÛÛÛÛÛ²Û Ûßß Þ²ÜÛÛ²²²ÛÝ + ÜÜßß ßßßÜÜÜ ÛÛÛÛ²ß ÜÜÛÛÛÛÞÛÛ²ÛÛ  + °° ßßßßß°°° °° enigma«bbs softÛÛÛ²²ÛÝ° ÞÛÛÛÛ² °° +dangermouse °°Û²ÛÛÛÛÛ²Û²Û ° +  °°  Û²ÛÛÛÛÜÜÜÜßßßßßßßßÜÜÜ +ÞÛÛßß°°°°° + ° ß °°°°°°°°°° + \ No newline at end of file diff --git a/mods/art/DM-ENIG.PCB b/mods/art/DM-ENIG.PCB new file mode 100644 index 00000000..247bd14a --- /dev/null +++ b/mods/art/DM-ENIG.PCB @@ -0,0 +1,19 @@ +@X0F @X04°°°°°°°°° @X0FܲÜÜ @X0E° @X0FÜÜ +@X0F @X04°°°@X4F°@X04°° @X0FÜÜßÛÛÛ²@X7FÛ@X4FÛÛ@X0FÛÛß @X07°° @X0E°°° °°°° ° @X0FÜÜÜßßßÜܲÛÛÛÛÛß +@X0F @X04° @X0FÜÜÜßß ÞÛ@X7FÛÛÛ@X4F²²@X0FÛÝ @X07°° @X0FÜÜÜ ÜÜ @X0E°@X0FÜÜÜßß Û@X4F²²@X0FÛÛÝ +@X0FßÜÜÜÛ@X7FÛÛ@X0F²Û @X04° @X0FÛ@X7FÛÛ²²²±±@X0F ÜÜÜßßÛÛÛÛ@X4F²Û@X0Fß Û²@X7F²²²±@X0FÜÜܲ@X7F²²²@X0F°° °° ÞÛÛÛÛÛ² +@X0F Þ@X7FÛ@X4F²²@X7FÛÛÛ@X0FÝ ÞÛ@X7F²²±±±°@X0FÝ @X7F²±@X0FÜÜ@X7F°±@X0Fß Þ@X7F²²²²Û@X0FÝ Þ@X7F²²±±±±±²²²±±±@X0F Þ@X7F²²@X0FÛ@X4FÛ@X0FÛÝ +@X0F @X7FÛÛÛÛ²²ß@X07 @X7F²²±±°°°@X07ÛÛ@X7F° °°°@X78°@X07Ý @X78 @X7F±±±²²@X08 @X7F²²±±°° °±±±°°°@X0F @X7F°±±²²Û@X0FÝ +@X0F°°²Û@X7F²²±°°@X0F°° ° @X07ßßß@X7F°°@X07ÛÛÜÞ@X7F°°@X78°°±±@X0F°° °° °@X7F° °°±± @X07Þ@X7F °±°@X78°°°@X07Þ@X7F °°@X78°°°@X0F°° °° ° °°@X7F°°°±±²@X0F°° +@X0F °Þ@X7F²±±°° @X07ÜÜ @X78°°±±²@X08Û Þ@X78±±°°@X7F° @X78°@X7F°@X78°±±@X08 @X07Þ@X7F°@X78°°°±@X08Ý @X01Ü @X07ÞÛ@X7F °°±±@X07 +@X01Ü @X07Þ@X7F±°° @X78°°@X08Ý @X01Ü @X08ÜÛÛÜÜÜ ÜÛ@X78²²²@X08ÛÛÝ@X01ÞÜ²ß @X08ÞÛ@X78²²±°° °°±±±±@X07 @X01Ü @X78±±±±²@X08Û @X01ÞÛÜÛÛ²Û @X07ÞÛÛÛÛ@X7F° @X07 @X01ÞÜ +@X19°@X01Ý @X7F° @X78°°±²@X08Ý @X19°°@X01ÛÜ @X08ÛÛ@X78²²@X08ÛÛÛÛÛÛ@X78²@X08ÛÛ²Ý @X19°°@X07 @X08ßßßßÛ@X78²±±@X08ßÞ@X78±±²²@X08 @X01Þ@X19°@X01Ý @X08Û@X78²²Û@X08ÛÝ @X01Û@X19°°@X01ÛÛÝ @X78°°°°@X07ÛÛÝ @X01ÛÛ@X19°@X07 +@X01ß @X78°°°±±²²@X08 @X01ßß²ß @X08ÞÛÛ@X78ÛÛ@X08ÛÛ²ÛÛÛÛÛÛ² @X01ßßß² ß @X08ß @X78²²@X08ÛÝ @X01ß²ß @X08ÞÛ²Û²² ÜÜÜ @X01ß @X08Þ@X78±±°°°@X07Ý @X01ß²ß +@X01 @X08Þ@X78±±±²²@X08ÛÝ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛßß ÛÛ² Ûßß ßßß ßß Û@X78²²±±±±@X07 +@X0F @X08Û@X78²²ÛÛÛ@X08Û ÞÛÛÛÛÛ²Û Ûßß Þ² Ü ÛÛ@X78²²²@X08ÛÝ +@X08 ÜÜßß ßßßÜÜÜ ÛÛÛÛ²ß ÜÜÛÛÛÛ ÞÛ@X78Û²@X08ÛÛ +@X08 °° ßßßßß @X0C°°° °° @X0Feni@X4Fg@X0Fma« @X07bbs soft @X08ÛÛÛ@X78²²@X08ÛÝ @X04° @X08ÞÛÛÛÛ² @X04°° +@X08dangermouse °° Û²ÛÛÛÛ Û@X78²Û@X08²Û @X04° +@X04 @X08°° Û²ÛÛÛÛÜÜÜÜßßßßßßßßÜÜÜ +@X08 ÞÛÛßß @X04°@X4F°@X04°°° +@X04 @X08° ß @X04°°°°°°°°°° \ No newline at end of file diff --git a/mods/art/DM-ENIG2-MATRIX.ANS b/mods/art/DM-ENIG2-MATRIX.ANS new file mode 100644 index 00000000..f6158c20 Binary files /dev/null and b/mods/art/DM-ENIG2-MATRIX.ANS differ diff --git a/mods/art/DM-ENIG2.ANS b/mods/art/DM-ENIG2.ANS new file mode 100644 index 00000000..06366ab8 --- /dev/null +++ b/mods/art/DM-ENIG2.ANS @@ -0,0 +1,21 @@ +  ° ÜÜÛÛÛÛÜÜ + °  ° ÛÛ ÛßßÛÛÛÜÜÜÛ²Ûßßþ ° +° ÜÜÜ  ÜÞÛÛÛÛÛ ÛÛÛÝ Û²²²²²ßÛÛÜÜ  ° + ÛÛ²²² þßß²²±°ÜÜÝ ÜÜÛÜ ßÛÛÛÛß Ü  ÜÜܲ²²±±±±Þ²²²Û + Þ²²±±±Ý±ßß²²ßþ±²ß þßßÛÛ²Ü ßßÛÜÛßß ÜÜܲÛÛ²ßßþ ÜÛ²²²²±±°°°Ý °±±²²Ý + Û²±°°°° °°°  Ü Ü Ü °  Þ²±±ÛÜܲÜÜÛ ²±±²²Ý  °°  ²±±±°°°° ÛÛ  °°° Þ°°°±±   +  ±°ß ß °°°  ²²ßÜÛßß²²ß²Ý °° ±±°°°°ÛÛÛÛ°°°°±±  °°°° Þ±°°°ß ßß²ßß °  ß²°ßß  ° +°  ÜÜÜ²Ü   Üܲ Û²ßß ÛÛÛ°Ûß ° Þ °° Û ±±Û²Û°°°°Û  °°°  ßß Ü °° °  Ü²Ü ÜÜ  °° +°°  Û²Ûßß ßßß ÜÜ Û²ÛÛß   Û±²ßß ÞÛÛ²²ÛÛßß° ÜÜÜÜ   ²ÜÜ²ß  °°°°  ²ßÛßß²ß  °°  + ÛÛ²ÛÛÛÜÜÜÜÛ ²²Û ß² ß ÛÛÛÛßÝ ß²ß ßÛÛ²Û   ÞÛ²²ÛÛÝ +  °° ßßß²ßß ß  °°  ßßß  °°  ÜÜÛÛÛ²²Û  °°  ßß²ßß  °° + ±±±±±°°° °°°±±±±±±±±  ±± ±±±± ßßß  ±±±±   ÜÛÛÜÜ ±±±± +²²²²²²²²²²²²²²²² ²²²²²² ²²²²²²²²²²²²²²²²²²²   ß²Û  ²²² ß ßß  ßßßß  ßßßß  +ÜÜÜÜÜÝdangermouse help c²²o  mm a n d shelp +ß ßßßß + +ESC - Exit ENiGMA« F3 - Configure +SPC - Logon Local °F4 - User Editor +F1  - Init Modem °F5 - DOS Shell +F2  - Hangup Modem F6 - Review Logs + \ No newline at end of file diff --git a/mods/art/IC-CS.ANS b/mods/art/IC-CS.ANS new file mode 100644 index 00000000..0a63a522 --- /dev/null +++ b/mods/art/IC-CS.ANS @@ -0,0 +1,551 @@ + + +ÜÜÜÜÜÜÜÜ ÜÜÜÜÜÜÜÜÜ +Û°°°°°°Û° Û°°°°°°° +Û° +ßß²±±²ßß° ²±±² +ßßßßß° + ßßÝ ßßݱ²²±°  +±²²±° + ±Ü±±±°°°° +° °°°°°° + ß ß ß ß±±±±°  +±±±±°° +°°Üܲ²²²ÜÜ ²²² +²ÜÜÜÜÜ° + °°ÛÛÛÛÛÛÛÛ° Û +ÛÛÛÛÛÛÛÛ°° +ßßßßßßßß° ßßßßßßßßß°° +°ßßÝ ßßÝ ßßÝ ßßÜ Û Ý ßßß ßßß ßßß ßßÝ ßßÝ +±± +ÜÜÜÜܱßß ±±ß ±± +±±±±±±±±± +±±°° +ßßßßßßßÛÜÜß ß ß ßßß ßß ßßß ßßß ß ßßß ßßß ß ß° +ßß°°±Ü +ßßÜ°°°°°²± + ÜÜÜÜÜÜÜÜ°°±²Û +ÜÛÛßßßßß ßßß Ü°°°°±°²Ý +ß°°°±Þ° °²Ý +ÜÜÛßß ÜÜ °°°°°±Ý ²²±±² +² +ܲßß °° ° °°±Ý ° ±Ý Û²²° +²Ý± + ÜÜÜßß ²²ÜÜÜÜÜÜܲ²²ÜÜ ° ° ±±² ° +°°Ý Þ²± ÞÛ²Û Ý ²Û° +ß²²ÝßßßÜܲ²ßß ßßÛÜ °° +± ²ÛÝ °±± Û²² Û²ÛÝ Û ²±Ý °  + ß +ÜÜß ÜÜܲßܲ²ßß ÜÜ ßßÜÜ ß ±±² Þ² +Û ±²ÝÞ²Þ²Ý Þ²±²ÝÞ °±²± Üß + Ü + ÜÜßßÜܲ²ßßßÜ²ß ßßÜ ßß ß²Ü ß ²²Û Þ² +Ý ²ÛݲÝÞ²Ý ² +±°±Ý ±°±Ý Ü ² Üß Ü²² +ß +ÛßÜÛßß ÜÛß ÜÜÜ ÜÜ°°°Ü ßÜ Û² +Û²±±Þ²Û²Ý ²² +Þ±°°ÝÞ ²²±ß ÜÜÜÜ ÜÜ +²ß +ÜÛß ²²ÜÛßÜßß Ü Ü²±±ßß ßß +°Ü Üܲ²Ü Þ²Ý +ß°ÝÞ±²Þ²Û²²± +°±Ý ²ßÜܲ²Ûßß Ü +Ûß +ßÜÜÞ²ÜÛß ÜÜßܲ²ßÜÜÛßß +ÜÜ Ü²²ßÜßßÛÜ ²ÝÞ +±°±ÛÞ²Þ±±² +ܲ²ßßÜÜß ÜÛß Ü Üß +°°ÝÞ ÛßÜ°°ܲßÜ +²²ßÜ²ß ßßÜ Û²ß ßß +±Ü ß ÛݲÝÞ± +ÝÞ ÞÝÛ²±Ü²²ß ÜßßÜÜß Üßß ÜÜßß + ° +°°°Û °°°Û²ÝÞ²ß  +²²ßܱ Û²ßÜß Ü ßÜ Þ + Û Û²²Þ Þ ²ÛÜ²ß ßÜ +Üß ÜÜÜÛßß ÜÜ°±²Ü +°°Û °°°Û²ÝÞ²Ý  +²²° ßÞ± Û²Ýß Ü ßÜ + Þ Ý ²ÝÞ²ÝÞÛ ÜÜßßß Ü ß +²ßß ÜÜß°± +°ÝÛ ÝÞ°Û²ÝÞ +²Ý ²²°°ÝÞ±ÝÞ +²Û ±±²²ÜÜÜ ß ßÞ Û  +²Ý Þ Üß ßßÛÜßÜ ÜÜ ß ÜÜÜܲ²²²ß +°ÞÝÞÝ°ÝÞ²ÝÞ²Ý  +²²°°ÝÞ²Ý Û² +ÝÞÞ±±±±±±±²²²ÜÜÜÜÛ ÞÝ ÛÝ +ÜÜÜ ÞÝßß²²ÜÜ ÜÜÜÜÜÜ +°ÞÞÜßÞÞ²ÝÞ² +Ý ²²ÝÝ°°²²Þ +Þ²ÝÞ±Þ°±±°ß°°°° +°±²Û²ÝÛ Ý Ûß ÜßßÜ +Ü ÜÜ ²ÞÛÜ ßß²Ü ÜÜÜßß +ÝÝ°°°Ý²ÝÞ²ÝÞ +Þ²ÝÞÝ°²Û Ý +Û²Þ޲ݰ°±°°°± +°°°°±²Û²ÝÝÝÞß Ü  +±° °²ÝÞÝß²Ü ÜßßÜ ßß +ÛÞ°°°Þ²Þ²ÝÞÝ +²²ÛÞ Þ²Ý°Þ +²Ý°Ý²Ý°°± +°°±±±°°°²Û²Ý ß ÜßÜ Ü +ßÞ±°°°ÛÞ² ² +Þ ±Ý ÜßÜÜßÜ +ßÛ +°°°°Ý²ÝÛÛÞ +°Þ²ÝÝÞ ÝÛ²Þ + ۲ݰ°Þ²Þ +°°°°±±±°°°²Û²Û ÞÝ + ±° °°ÝÝÞÝ²Þ +Û ° ÞÝ°ß Û +°°°°ÝÛÞ²Ý±Ý +Û²Û ²Þ² ÝÝ +²ÛÞ°°Þ²Þ +°°°±±±°°°²Û²ÝÝ +Û ±° °°±ÝݲÞÝÛ +Û °ÞÞÜ°ß  +Û +°°°°ÞÝÛÛÞ²Ý +²ÝÛݲÝÛÝ°ÞÞ +²ÛÞ°°ÝÛÝ +°°°±²±°°°²Û²Ý +Û Þ±° °°±±Û Þ +ÝÛÝÝ° ²²²°Þ +  +°°±±ÞÞÛݲ²ÞÛÝÛÞ +²ÞÛÞÝÝ޲ݰ°°° +ÞÛÞ±±²²°°°²Û +²ÝÞÞÝÜß +° °°±±±ÛÞÞÝÛÛÛ  +°Þ±±²Û  +±±²²ÝÛÛÞÞ±Þ +²ÞݲÝÛÝ°ÝÝÞÛÝ +°°°°ÝÞݱ±²²°°°² +²±ÞÞÝ²Û   +°°±±²ÛÞݲÛÝ +Ý °°±±Û +²²²²ÞÛÝÛÞ°Þ +²ÞÝÛ ÛÝ°Þ ²Ý +°Û°°°Û ²²°° +°²²±ÝÞ Û  +²Û °°±±Ýݲݲ +ÞÞÛÞÝ Þ±°°± +Þ +²±±ÝÛÛÞÝ°ÝÛÝ +ÛÝÝÞÛÞ°Þ Ý² +Ý° Û°°°Û  +²°°°²²±Þ ÛÞ  +²Ý°°±±ÝÝ²Ý +²ÞÝÛÞÝÝ ±±±°± +±°°Þ²ÝÛÝ°ÝÛÝÛÝÞÞ +ÛÞÝÝÝÛÞÛÞ +°Û°°±±Üß ß +°²²±ÝÝ ÞÝ°² +Þ°±±ÛÞ² ²² Û +Þ²Þ±²±° +°ÛÛÞ²ÞÛÞ°Ý +ÛÝÛÞÞÞÛÞÝ +Ý°ÞÝ²Þ Û +°°±±²²Üßܲ²± +ÞÝ° Þݱ±ÝÞÞÝÝ +²ÞÞÝÝÛ²Ý  +±²²± +۰ݱÝÞÛÞ° +°ÞÝÛÞÞÝÛÝ +ÝÝ°ÞÝÞÝ °°±±²²°°°² +²±ÝÝÛÞ  +ݲޱÛÝÛÝ +ݲÞÞÝÞÛÛ² +Þ±²² +°°Ý±ÝÝÛÞ +°°ÞÝÛÞÞÝ +ÛÝ°ÞÞÝÛ ÛÞ +°°±±²²°°°²²±ÞÝ Ü Ý +ÝÛÞ °ÞÝ +ݲÝÛÞÞÝÝ +ݲÞÛÛ²±°±² +Û°Þ°ÞÝÛÞ +°°ÝÝÝÛÞÛ +ÞÛÞÝÝ°ÞÝÞÝ°±±²²° +°°²²±Ý²±Ý Þ +Ý Þ²ÞÝÛÝ  +°Þݱ²ÞÝÛÞ +ÝݲÞÛÛÛ²ÝÞ +±² +ßÜÝ°ÝßÞ ßÛßÜÝÛ +ÞÛÝÛÞ°ÜÛÝÛ ß +Üß±²²°°°²²±²± +²Ý ±°ÞÝ°°ÝÛ +Þ²ÞÞÛÞÝÝÛÞ +ÝÛÛÛÛ²°± + Þ ß ÞßÜ ßÞÝÛÞßÝÛÝ +°ÛÛ ÝÞÜß +ܲ°°°²Û²Ý Þ +Ý Þ²±²ÞÝ +°°ÝÛÞÝÝÝÝ +²ÞÞÝÞÝÛÛÛÛÛÝÞ +° + ÜÜ Ý ßÞÛ ÞÞÞÝ +ßÞ °°Ý±²²²°°°² +ÛÛ ß ± ²Ý Þ±² +ÞÝ°°Ýݲ²ÞÛ +ÝÛßÞÞÝÛÛÛÛÛÛÞ +± + ° ßÛÛÜÜÜ ÞÝ Û Û °°±±Þ +²²²°°°²ÛÛÝ ± Þ +±±Üݱ±²Þ +Þ°°Ý±²²ÝÞÞݲ +ÝÞÝÛÞ²²²²²Û² +° Ü ßßÛÛÛÛÛÞÝÜ Û Þ ßß +°°°°°°°°²ÛÛÛÛÜ ßß Ü± +²Ý±²ÛÝ°°Ý±±±±Þ±ÞÝ±Ý +±ÝÛÞ±±±±±²² +± ßßß ²Ý ÞÝ ÝßßßÛÛ +ÛÛßÞÝ Ü±²²ÝÞ²Û ° +ÞÞ°°°°ÝÛÞ +°°°ÝÛÞ°°°°°±± +ÝßßÜÜ Û ÞÝ Þ ß ÜÜ +Ü ß ßþ²Ý±Ý +±°°°°°Ý°°ß° +ßÞÝ°°ß°°Þ +ÞßßÜ ß°° +² ÞÝßßßÜÜÜß  +ß ÜÜܲ °±ß±±±± +Û ß ß ÝÛÞß Üß Þ ß + ÜßßÜ +Üß Û Û ßÜÜ +±ÛÛ°ÜÝ °° Ûß +ÞÝÞ +²²ß ܱ°² ÛßÝÝ Þ +ÞÛÝ°ßÜÝÛ ÜÜÜÜÜÜ +ßÜÜÜÜÜÜÜ +Ûܲ±°ÞÝ° Þݲ +ÞÛßÜ°ßÜÜ ß Ü  +ßßÛÛÝÞßßß Ýßß +²±°°ÞÝ Ý±ÛÛÛ² +ßÜܲ²Ü Ü ßß Ýßß  + °° Þ +ß°°° ± ÞÜÞ° +Û²ÛÛÛßÞÛÜÞÜÜÜ +ÜÜÜܲ±° °±±°Ý °± +°±±° °ÝÞݲ +Þ²²²ÛÜÛ²²Þ +Ü۲߰°°Û²²² Þ²Ü +Û²±° °±°Ý°±² + °±²²±°Þ±²±²Ü²²²² +ܲÛÛÛßÛÜÜ°°°°ÝÞÛ +°²Ûܱ° °Þ±±²° +±²²²±°°Þ°²±±±²²Û +²ÛÛÛÛÛÛÜßÛÛ² +ÜÜ°°ÞÛ²²Û±° ²±²  +° + ܲ°°²Þ²°²²ÛÛÜÛÛ +²²ÛÛÛÛÛ²²²±²²Û°Û²±²² +ݱ°±²Û°± +ÜÜÜÜÜܱ۱²Û +ßÜÛÛ²²ßßÛÛ²²²±±±°±²²²Ü +Û²Û°±°Þ°°  +°± + °°±±²ÜÜ Ü °ß Ü +ÝÞ°Û²ÛÛÛÛ²ßÜÜÛÛÛÜÜ +ßß²²²±²²ÛÞ²ÛÛÛÝ°  +±° °±² + ßßßÜÜ°±²Û°ÜÜß +²ÞÛÛ²ÛßÛÜß +±²ÛÛÛÛÛÛÛÜÛ²²ßÛÝ Þ +²ÛÛß° °° °±² +ßß Ü±±ßÜß°²² +Ý޲ݱÛÛ²± +²ÛÛÝ ÜÜß²²ÛÛÛ +Û²ÜßßÛܲÜÜÛß +±° Þ±°°±²Û +ÛßÜÛÛÛ°°±±Þ²°²²Üß +Þ°Û²±°±²ÛÝ ßß² +ßßß²Û۲ܲ ß +²²±° °±²ßܱ²Û +ÜÛÛÛÛÛ°°±Ý²²°²Û +²Ý°°°²ÞÛÛ +²±²ÛÛܲ±°ßßßßßß²±° °°° +²Ü°±²ÛÛ +ÛÛÛÛÛÛ°°±Þ²²°²Û +²°°±±±Þ±ßßßß ÜÜ +ßÛܲ±°Ü° ßÛÜÜÜÜÜÜÜ °°°°° +ÞÜÜܲ²ÛÛ +ÛÛÛÛÛ°°±±²²°²Û²Ý°± +±±±±° þ ßßß²ÛÜÜÛ +²±°±²ÝÝ°²±°°Ü  +°°ßßßßÛßßßßÛÛÛÜÜßß +ÛÛÛ +ÛÛÛÛÛ°°Ý±²²°²Û +²°±±²²²± ° °±Üß²°ÛÜÜ + Þ²ÛÛÛßßßßÛ  +°²±°ÛÛÛ ÛÛÝÞÛÜÜßßÜܱ± +ßßÛÛÛÛÛ +ÛÛÛÛ°°±Ü²²°²Û² +Ý°±²²²²²±°²±²± +Û²ÝÞ Þ²ÛÛ²ÜÜßßÝÞ +°Û²±ÛÛÛÝÞßÜÛÛÛÛÝÞ Üß +±²°±²ÛÛ +ÛÛÛÛ°°±±²²°²Û²ÜÜ  +ßßß²±°²±°²ß Ü  +Ü²Ü ßßݲ±°°°°²ÛÛÛßÜ Û +ÛÛÛÛÛ ÛÛÛÛ°²Üß²°²Û +ÛÛß ß±±²²°²Û²Ý +²±ݱ²ÛÞÜÜ ÜÜß²±ÜÜܲ +Û²ßß²²²±° °°ÛÛÛÛßÛÝÞÛÛÛ +ÛÛÛÞÛÛÛÛÛ°²²²²±² +ÜÜ ß°±±²²°²Û² +±°Û²ÛÛ²ÛÛÞÛÛÝÛÝÜÛÛÛÜÜÜÜÜ +ßÜÜÜÜ Þ +°°ÛÛÛÛÛÛ ÛßÛÛÛÛÝÛÛÛÛÛÛ°²² +²²±² +ÛÛ°°ÜÜݲ°²Û +²ÝÞÞÛÛÞÛÛÛÛÛÛÛÛÞÛÛÛÛÛÛ°± +ݱ²Ý²ÛݱÞÞ±  +°°°ÛÛÛÛÛ ÛÛÝÞÛÛÛÛÛÛÛÛÛÛ°±² +²°²Û +ßÛ°°±±²²°²Û²² +ÝÞÛÝÛÛÞÛÛßÛÛÛÛ° ÛÛ°±²Ý±²²ÛÛ²Ý +Ý²Ý °°ÛÛÛÛÛ ÛÛ Û  + ÛÛÛÛÛÛÛÛ°±±±²°²Û +Ü ß±±Þ²°²Û² +ÝÞÜ ßßÛ ÛÝÞÛÝÛÛÝ°± Û°±²Û +ݱ²ßÛÛÞÞÞ±  +Þ°°ÛÛÛÛ ÛßßÛ ÝÞ° +ÛÛÛÛÛÛÛ°±±±±°²Ý° +Û°°Ü ²²°²Û +²±°Þ²Û ÛÜÛ ßÛ ÛÛ ²ÝÛ + °±²Þ²Ý²ÛÝß ß  +°°°ÛÛÛ ÛÛÛÛÛ ÝÞßÜßÛÛÛÛÛ +°±±±°²ÛÞ +°°±Ý²°²Û²ÝÞ +±ݲÛÝÞÛÛÝÞÛÛÝÜÛÝÜßÜßßßÜÜ  +ÜÜ ÜÞÞ± °°ÛÛÝ ÛÛÛÛ  + °°ÝÞÛÛÛ°±±±±°² +Ý Ý +°ß±±²Ý°²Û² +²ÝÞÛÛ ÛÛÛÜÛÛÛÜÛÛÛÛÛÝÛÛÛÞ +°Ý²ÛÛÞ Ý²Ý Þ +°°ßÝ ÛÛÛÛÛÛ ß° +°°°Û ÛÛ°±±±°²Û +°Þ +Ý°±²²°ÝÛ²ÝÞÝ °²Ý +Þ²ÛÝÞÛÛÞÛÛÛÛ° ÛÛ°Û°±²²² +ÛÝÝÞÞ± °ßÜÝ ÛÛÛÛÛÛÛ  + Ý °°ßßÛÛÛ°±±± +°²ÝÛ ° +Þ±±²²°²Þ²± °±² ÛÛÝÛÛ +±Û°±Ý °± °±²Þ² +Û²Þß ß Ü° +Û ÛÛÛÛÛÛÛ ÛÝÞ °ÛÝÞÜÜÜßß +±°²ÛÛ +±±²²°²Û²Ý °°²ÜÜÜ ß ßÛ +ßÞ±²ß °Ýß°Ý ßß +Þ° ÛÛÛÛÛÛÛ ÛÛ   +ÝÞ Û ÛÛ°±±Ü°² +ÝÝ Û +²²²°²²Û²°°°±²²²²²±°°±ÜÜÜÜ +ÜÜÜܲ²±°°  +ÝÛÛÛÛÛÛÛ ÛÛÝÞÛÝÞÛÛÝÞ°±±±°²Û +Ý +°°±²²Û²Ý °°°±²²²²²±±°±² +°±²²±°±²²±°  +° ÞÛÛÛÛÛÛ ÛÛÛ ÛÛÝÞ°° +ܱ±±±°²ÝÝ Ý Ý +ßß²²ÛÛß°°±±±±±±°±Û +±²²±²±°° Þßß  +ÛÛÛßÜÛÛÛÛÝÞÛ°ÝÞÝÞ±±± +±±°²ÛÛ ÞÝÞ +ÜÜÜ°Û±²²±°° °°  +° ÛÜÜßÜÛÛ °°Ý +°°±±±±±±²°²ÛÝÜ Û Û +ßßßßßßß ±° ß ÝÞ +°°°°°°±Þ±±Ýß Þ +±²²²°²ÛÝ ÞÝÞÝÞÝ + ÜÜßßß°±²²±° ßßß ßß +°°°±±±²²Üܲ²²²²°²Ûß  +° Û Ý Û +ÜÜÜÜ ßÛ²±° °±ܱݲ±° °±ß Û + ÜÜ ° Ü ßßßßßßß Ý° ÞÝ Þ +²²ÛÜÜÜ°±²²ÛÛÜÜܲ²Ü²±°± +Þ²²²±°°±ßÝ°ÞÝ Þ²Ý° Üß  +Þ²Ý ² ÛÝ Û ± Û Þ +ßßßßÜÜ°±²ß²²ÛÛÛ²Ü +²±±Þ²Ý²±° +Û °ÞÝ ²²Üß ²± +± ÞÝ Þ ° ÞÝ Ý +²ßÜÜßÜ°±ß²Û +²Û²²Ü²Û² ܱ +Û° ÞÝ ²±° Ý Û² +Ý Û ÞÝ Ý ²Þ + ßßÜÜ²Þ²Û +±²²²²²ÜÛ²°ÞÝ° Û + Üß޲ݰ Û Þ²Û ² +° Ý Þ ÞÝ +ßܲ޲²Û²±±±±±²² +Ý°ÞÝÞ Û ° Þ²ݱ ÞÝ ÞÝ Û ÞÝ ± Þ  +² +ÝÞÝݲ޲۲±°°°° +ßÜÛ °Û Þ Û Ü ° ²ݱ ÞÝ ² +Ý°ÞÝ ÞÝ ² Ý Ý ² +ÞÝ ÛÜܲ²Û²±ßßß  +۲ݰ Û Ý Û ÞÝ° ²Ý °ÞÝ  +Û°ÞÛ° ²± Þ Ý°ÞÝ +Û ÞÜÜÜÜÜÜÛÛÛÛ +²Û ÜÜßÞÝ Û²ß ° Ý Ý Û Û °ÞÝ  +°ÞÝ ÛÛ ²° Þ Þ°ÞÝ +Û Ý°²±±²ÛÛÛ²²²Ý  +²²ß ß Û²ßÞÝ Þ ÞÝ Þ Û + ÞÝÛ Û ÛÝÞ ÛÝ ²Ý° ÞÝ Ý Û +ÜÜ ÞÝ°²°±±²ÛÛ²²± +Ý ±²ß Üß Û² +Û °Ý Ý ÞÝ ÞÝ Ý²Û Û ÞÝÞ ÛÝ ÞÝÞ ÞÝ Ý Û + ßßÜ°²°±±²ÛÛ²²±Ý  +°ÛßÜß Ü²ß Ý Û° Ý ÝÛ ÞÝ  + Û° ÝÛ Þ ÞÝÞÝ Þ Þ ÞÝ °Þ + Þ ßÜ°²°±±²ÛÛ²²± +Ý ßÜßÜÝ Ü²ß Û ÞÝ°ÞÝÝ Þ Þ +Ý Û Û ° ÞÝÝ°ÞÝÞÛ Þ Þ Û Þ Þ + Üß ßÜ °²°±±²ÛÛ²²± +ßÜßÜßÜ°Ûß Ý Û °Û ° +ÞÝÞ Þ Û Û ÞÝÞÝ° Û ±ÞÝ Û Þ Û Û °Ý +ÜßÝ °²°±±²ÛÛ²²ßÜ +°Û °ßÜß Û Þݱ Ý +± ÞÝ Ý Û Þ Þ °Û ° Û ² Û ÞÝ °Û Þ °Ý° +Þ °²°±±²ÛÛ²²±±ß +ÜßÜßÜßÜ°Û Û ² ° Û +²ÝÞ ÞÝ Ý ± ß °Þݱ Ý Û °ÛÝ Þ ° +ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ +ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ +ÛÛÛÛÛ Prophet is Copyright of Rob  +Liefeld - Image ComicsÛÛÛÛÛÛÛÛÛÛÛÛ  +ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß +ßßßßßßßßßßßßßßßßßßßßßßßß +ÝÜß +ÜÜÜßßÜ +ßßßܱ°±ÜÜÜ +ßßßßßßßÜÜ  +ÞÝ +²±°°±²²±° °²±°°±² +²±°°±²²±°²±°°±²²± +°²±°°±²²±°Ü +°°°Ü ܲ²ÛÜ ß ²±° +°±²²±° +ßßßßßßßßßß ßßßßßßßßß + ±²²²²±ßßß +ßßßßßßÜßßßßßßßßß ß +°±±²²²²²Û ßßßßßßßß +ß + ܱܰ±²Ü±Ü  +²²±Ü ±²² +ßܱÜß±²²Ü° +°±²ß±²²ß±²² +Ü±Ü +ß±²²±Ü±²²± +Ü°±²±ß ²±° +²±°±²ß±²² +±Üß±±²±²² +²Üß °±²ß +±²²±Ü + ß±°°°Ü ²²±°°°° +ß ±°°°Üß  +°°±ß ß±°°° +Üß±²²±°Ü +ßßÜ°°±ß±°° +°Ü + ß°±²²Û²±°ß°±±± +Ü ÜÜß°±±²²±°° +Û²²±±°°°ÝÞ°Ü +ß°°±²Û ²Ü Û +²²±±°°°Û²²±±°°°Ý +ßßßß ßßß ß±²²²Üß +°ÜßßßßßßßÜßßßß +ßßßßÜÛÛ°°Üßßßß +Ü°±²Üßßßßßßßß ßß +ßßßßßß +Üß²Û²ßܲ²±±°°ÛÛÛÛÛÛÛÛ +°ÛÛÛÛÛÛÛ°°±±²²Ü°°±²Ü +ܲ±°Üßܲ²±±°°ÛÛÛÛÛÛÛÛÛ +°±°ÛÛÛßßÛÛÛ°°±±²²Ü°°±² +Ü +ÜÜÛÛÛÜÜ ß²±°Ü²²±±°°ÛÛÛÛÛ +ÛÛÛÛ°°±±²±±ßÜÛÛÜßÛÛÛ +°°±±²²Ü°°±²Ü +Þ²²ßßßß²²²Üß²² +±±°°ÛÛÛÛÛÛÛÛÛ°°±±²²ÛßÜ +²²²²²ÝÞÛÛÛÛÛ°°±±²Ü°°±² +ÜÜ²Ü +±±Ý Üß±ßܲ²±±°° +ÛÛÛÛÛÛÛÛÛÛ°°±±²±Üß ±±± +ÛÛÛÛ°°±±²²ß°±²²ßÞ +±±±Ý +°°Ýßܲ±°ß²²±±°° +ÛÛÛÛÛÛÛÛÛÛ°°±°°Û °°° +ÛÛ°°±±²²ß°±²²ß +°°° +Þ ßß²±°ß²²± +±°ßßßÛÛÛÛÛÛÛÛ°ÛÛÛ   +°°±±²²ß°±²²ßÞ +  +°°°°°°°°°°°Üß²±°ßß +ÜÜ°°°ÜßÛÛÛÛßßÛÛÛÛÝÞ +°°±±²²ßßßÜÜÜÞ +°Ý +ßßßßßßß±±±Ý ßßßÜ +±±ßßÜß±±ßßÜ +±±ÜßÛÛÝÞ±ÝÞ +²²ßßܱ±±ß +±±Ü ÜÜÜ±Ý +Þ²²²²²Ü ß ßßß ² +ÝÞß²²ßÜß² +°ÝÞ²ÝÞß° +Üß²ß ßÝÞ²²ßß +²²Ý ÜÜÜ ²Ý +²²²ß²²Ü²ßßÞ +²ÝÜÝÞ²ÞÝÞÝÞÝ +Þ²ÝÞ±²²ß Ü  +ܲ²Ü ²Ý Þ²Ü +²²ß ß²Ý +Üܱ±±Ý Þ±Ý  +ܲ±±ÝÞ +±ÝÞ ±±±±±Ü ß Ü +ÜÜܱ±±±±Ý±Ý Þݱ +±±±Ý + ° °±²Û°°°°°°°°°°ÜÜ°° +ÜÜÜÜÜ °°ÜÜß ÝÞ +°°ÝÞ°°°°°°°°°ßßßßßßß  +°°°°°°ÜÜ Ü Þ° +ÜÛÛÛÛßß ßß ßÜ ßÜß +²±ÜßßÞÝßßßßÜÜÜßß ÞÛÛÝ ßÜßß +ßßß ßßÜ + °Ýß²²±°ß°±² +²ßßß +°°ß²²±²²ß +±°ß²ß +ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄı±ÜÄÄÄÄÄ Cru +sty's SpamLan +d ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +ÄÄSysOpsú WarlockÄÄÄIJ² +ßÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ914.9 +44.8046ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +ÄÄÄÄÄÄÄÄÄÄDeadpool [GRF]ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +ÄÄÄÄÄÄÄÄÄÄÄÄ96oo - 14.4 only +ÄÄÄÄÄÄÄÄÄÄÄÄÄ +ÄÄGRF Affiliate BoardÄÄÄÄÄÄÄÄÄ +ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[24oo by  +invite]ÄÄÄÄÄÄÄ +ÄÄArt, Music, & Anarchy ONLY! +ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄRunning Obli +vion/2 Registered +ÄÄAll Artists, Coders, & M +usicians welcomeÄÄÄÄÄÄÄÄImpac +TNeT, ToXiCNeTÄÄÄÄÄÄÄÄÄÄ +ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ!ÄIcy of  +ACiD Producti +onsÄ!ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + +Greets to all ex-Valiant guys!7.28.93 \ No newline at end of file diff --git a/mods/art/MATRIX1.ANS b/mods/art/MATRIX1.ANS new file mode 100644 index 00000000..b09c5ad7 --- /dev/null +++ b/mods/art/MATRIX1.ANS @@ -0,0 +1,19 @@ + °°°°°°°°°ܲÜÜ  °  ÜÜ  + °°°°°° ÜÜßÛÛÛ²ÛÛÛÛÛß  °°  °°° °°°° ° ÜÜÜßßßÜܲÛÛÛÛÛß + °ÜÜÜßßÞÛÛÛÛ²²ÛÝ °°  ÜÜÜ ÜÜ °ÜÜÜßßÛ²²ÛÛÝ +ßÜÜÜÛÛÛ²Û  °ÛÛÛ²²²±±ÜÜÜßßÛÛÛÛ²ÛßÛ²²²²±ÜÜܲ²²²°° °°ÞÛÛÛÛÛ² + ÞÛ²²ÛÛÛÝÞÛ²²±±±°Ý ²±ÜÜ°±ß Þ²²²²ÛÝ Þ²²±±±±±²²²±±±Þ²²ÛÛÛÝ + ÛÛÛÛ²²ß     ²²±±°°°ÛÛ° °°°°Ý  ±±±²² ²²±±°° °±±±°°°°±±²²ÛÝ   +°°²Û²²±°°°° °  ßßß°°ÛÛÜÞ°°°°±±°° °° °° °°±± Þ °±°°°°Þ °°°°°°° °° ° °°°°°±±²°°   °Þ²±±°° ÜÜ    °°±±²Û  Þ±±°°°  °°°±± Þ°°°°±Ý Ü ÞÛ °°±±  +Ü Þ±°° °°Ý  Ü ÜÛÛÜÜÜ ÜÛ²²²ÛÛÝÞܲß ÞÛ²²±°° °°±±±± Ü ±±±±²Û ÞÛÜÛÛ²Û ÞÛÛÛÛ°  ÞÜ  +°Ý ° °°±²Ý °°ÛÜ ÛÛ²²ÛÛÛÛÛÛ²ÛÛ²Ý °° ßßßßÛ²±±ßÞ±±²² Þ°Ý Û²²ÛÛÝ Û°°ÛÛÝ °°°°ÛÛÝ ÛÛ° +ß  °°°±±²² ßß²ß ÞÛÛÛÛÛÛ²ÛÛÛÛÛÛ² ßßß² ßß ²²ÛÝ ß²ß ÞÛ²Û²² ÜÜÜ ß Þ±±°°°Ý ß²ß +Þ±±±²²ÛÝ  ÛÛÛÛÛÛÛÛÛÛÛÛÛÛßßÛÛ²Ûßß ßßßßß Û²²±±±± +Û²²ÛÛÛÛ ÞÛÛÛÛÛ²Û Ûßß Þ²ÜÛÛ²²²ÛÝ + ÜÜßß ßßßÜÜÜ ÛÛÛÛ²ß ÜÜÛÛÛÛÞÛÛ²ÛÛ  + °° ßßßßß°°° °° enigma«bbs softÛÛÛ²²ÛÝ° ÞÛÛÛÛ² °° +dangermouse °°Û²ÛÛÛÛÛ²Û²Û ° +  °°  Û²ÛÛÛÛÜÜÜÜßßßßßßßßÜÜÜ +ÞÛÛßß°°°°° + ° ß °°°°°°°°°° + \ No newline at end of file diff --git a/mods/art/SO-CC1.ANS b/mods/art/SO-CC1.ANS new file mode 100644 index 00000000..daa276b7 --- /dev/null +++ b/mods/art/SO-CC1.ANS @@ -0,0 +1,823 @@ +ܲ±ßܲ±° ° Ü °°±°±±² +²±²²Û²²±²±°±°° °Û °ÛÛÛÛÛÛÛÛ +ÜßßßßßßßßÜßß Üßܲ² Üßß² ß +°ÜÜß°²° ° ÜÜß ° °°°°°±²²Û²² +Û²ÛÛ²²ÜßßßßßßßßßÜÜܲÜÜ ÜÜ ² +ßßß ßß ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ Ü°°°Û +°ÜÜ°ßÜÛÛ° Û +° °Ü±²²ß ßß ßÜ ÜÜ Ü²ß ßßß ²ßÜÜÜÜÜÜÜÜÜÜÜ +ßÛÛÛÛÛ Ý ÜÜÜÜÜÜÜ Þ °Û +°ÛÛÛÜßßß ßßßßß Ü²Ü +Ü Ü²ß ßßß ÜÜÜÜÜÛ²²±²±°±° ° Û ÛÛÛÛÛÛÛÛÛ +°°Ý ÜÛÛÛßß ßþ Þ ±Üßß +ÜÜ ²Ûܲ²ßß ß ÜÜÜÜÜÜ°°±±°±²±²Û²² +²Û²²Û²±²±°° °Û Û ÛÛÛ +°ÛÛÛÛ ÝÞÛÛÛÜÞ °ܲÜß ßÜÜÜ +ÜßßÛÛÛÛÛ°ÛÛ°°±±±²±±±² +Û²²Û²±ßßßß²±°±°°±° ° Üßß²ßßßß Ý  +ßÛÛÛÛÛÛÛÛÜÜÜ Þ ÛßÜÜßÛÛÛÛÛÛÛÛÛ + ÛÛ° °°±°°±±±±±²²²Û²ß  +Ü°°°Ü ßß²ß Ü ÜÜÜ ÜܲÜÜ Ý ßßßßß +ÜÛÛÛÝÞ ÜÛÛÛÛÛÛÛÛ°ÛÛ Û +°Û Û Û°°±±±²±²² +ßßß² ±°±Ü±°Ý ß ßßß²ß ß ÜÜÜÜÜ +ÜÜ ÝÜÜÛÛÛßß Þ ÜÛ°ÛÛ°°  +ÛÛÛ ÛÛÛÛ ÛÛÛÛ°°±² +ß þ ßßÜÜ ±°±²²²±ݲ ܲ۲± +±±°±°°°°°Û° Ý ÜÜÛÛßßßÞ  +°°±°ÛÛÛÛÛÛÛ°Û °ÜÜßß +ßß ß ß±²Û²²ÝÞ°± +²±±±±²Þ ²Û²²²±²±°±°±°°°°° +Ý Ûßß ÜÜ ß þ Þ Û °ÛÛÛ +Üßßß²ß ß° ÜܲÜÜܱ +°Ü ÜÜÜß² °°°±°°±±±Þ  +Þ²ÛÛ²²²²±±±°°±°°°ÝÞÝ ÜÛß ßÜ Þ ß +ÜßßÜܲßß Üܲ Ü°±²Û²ÛÛ²²±° +Üß°ßÜ Þ±°°°°±°±ÞÝ  +ß °ß ß ßßßßß ÝÞ ÛÛÞÝ ÞÜß ÜÜÜ +ß±²Û² °°±²²²±ß°ßß²±°ÝÞ +°ÝÝÞ°°°°Üß°± +ÞÝ Ü²Ü Ü²Ü þ Ü Ý ÞÛÝÛÛ ÞÜ +ßÛ°Û°±²Û² +°°±±±ßßßßßßß°²±° Þ +°Ü°ß°°±²Þ +°²Ý Þ²Üß²²Üß²²ÛÜ ßÜÜ Ý ÛÛÛÜÜ Ü +ÜÛÛÝ Þ ß°Û°±°°±²² +°°±°±°Ü°±±°°Ü°ß±° +Þݲݱܰ°±ÝÞݲ  ° + ÜÜß°Üß²°°° +Üß Ý ßÛÛÛÛÛÛÛÛß Þ ²° +°±°±²²ÛÞ°°°±°°±²±°ßß +°°ß°ÝßÜßÞ°±²±ÝÞ²ÞÝ +Û °±°° °ÛÜßß± ßß°² Ý +ßßßßÞ Ü°±²±²Û²Ý°  +°°±°Þ°±²±°±Ü ²Ýß²Ü Ü +°°±²ÝÞ Þ²±ÝÞ +°°±°°° °ÛÜ ±Ü Ýßßßßßßßßßßßßßßß  +±±²Û²ÛÛß Ü °° +ÞÝ°°±±°Üß²ß Üß ÜÜ + °°°Þ²° ݲ °Ý  +°°°±±°°°Û² ß Ý S™-CC1.ANS Þ +²Ûßß Ü Þ² ±Ý° +²°Þ±°Ý ²ßÜÜ + ° °° °ÛßÞ²Þ +° °Ý ßÜß° °±±ß Ü °  +ßßßßßßßßßßßßßßßß °ß ܲ +°Ý ²ß °Ý°Ý +°Ý°°°Þ °Þ + °°° °°°°ÝÞ²°ÜÝ °°Ý±  +± ²Üß°ß Üܲ±° +°ܲ ß þ °ß± +ÜÜßßÜÜ°ÝÞ +Û°° ß ß ÜÞ°  +ÞÝÞÝ°Ý  Ü°°° +°°°°°°° ßßÜ°Þ +°°  ÝÞ²°ß²  +Ü °±±ßÞܱ²Ü° +ÞßÜ°ßÜ  ÜÛ° +Û°±Ý +°Üß Ü  ²ÝÞÝ ß +  °Þ Þ  + °°°°°°°°°°° ÝÞ°°°°°° +ÝÞ ß ° ² ß +°ßß  ܲ²²²² +Ý ° Üß° +Ý°Û°Û°°°± + Ü°°ÜÝÞÛÝÞ  Ü +  Þ ° °°°°°°°°°°° +ÝÞÝÞ°°°°°Û² Ý + Ü ß  ßÞ +²±²²²² Ü þß° +°Üß°Û°°° +°±±ß² °Û Ý°Ü + °Ý  Ý °°°°° +°°°°°°   °°°°Ûß + Þß     ±. Ü +±²±²±² °Ý°° +Ü ß²ÜÛÛ°Û +ßÜÜß °±Ý°± °°° + Þ°Þ °°°°°°°°°°°Ü +°ÝÞ°°°ß °Ü +Ü±Ü  Þ°°±°±±² +ÝÞ Þ±Ý +Þ²ÜܲÜÛ° +ß Ü°±ß°² °°°° +ÝÞÝÞ °°°°°°°°°°°°° °° +²  °Ü²  .þß² +Þ°ÛÝ  °°°±²± +Ý Ü±²  °ßÛ +°Û°Üß +ÜÛ °ß þÞ °°°°Ý + °  °°°°°°°°°°°° °± +²  Ü°ß ÜÝ +Ü ÜÜ  ±ß + ÜܲÜÜßß°±°°Þ±ß + ²°Ü ß°±° +Ý +°ß  Üß°Û + °°°°°  °ÝÞ°°°°° +°°°°ÜÜÜÛÛ²°  °Ý Þ +°²ÜÜ ßÜß +²ÛÛ²±° ÜÜßß±² +Ý ß °±°  °±± + S™ß °Ü  +Û°ßß ß +ß ²ß   +ßßÜÜÛÛÛÛÛÛÛÛÛ Þ +°±Ý ±°±²Ûß + ÜÛ²  ²ÛÛ +²±° ° °±ÜßþÜ  + °±±  °± + °Ü ßß° ÜÜ²Ü   +ܲÜ  ÜÜ° ß +²ÛÛÛÛÛÛÛÛݲ±²± ßßß +ÜÜÛÝÞÛÛÛÜ ßÛ²²±°° °°° + ß°Üß °°°±± + Üß +°²ßÜܲ°ܲßß ß  +     ß° +²Ü ²ÛÛÛÛÛÛÛ²²±±²ÝܲßÛÛÛÛ + ÛÛÛÛÜ ßÛ²±±°°°± +ÝÝ  Ü ² ° +°±ÝÞÝ + ± °Üß    +ß² .þß   Ü ß +°² ²ÛÛÛÛÛÛÜ +²±²²ÜÛÛÛÛÛÛÛÝÞÛÛÛÛ²Üß +²²±°°±± ² °Þ + ßܲ °ÛÛ ° +ß°² ܱÜ ° + ܲÜßÜÜ ܲßܱ +Üß  ß°Ü +²ÛÛÛÛÛÛ²²²Û²ÛÛÛÛÛÛ +ÛÛÜÛÛÛÛÛÛ²Üß±°±±² ± + ² ܲ±°Þ°Û +° +±° ²° °Ü  + ßß±ÜÜÜÜÛ² +ßß Üܲܰ°±²ÛÛÛÛÛÛ²²² +ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ²±Ü°±²² + ± Ý ²Ûܱ ÛÛ Þ +Þ²° ²°°±°°ÜÜÜ + ÜÜÜÜÜÛÛÛÛÛÛ²°°±²ÛÛÛÛ +ÛÛ²²²²ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ²±°°±²Ý +ܱ² Ý°±²Û² ÞÛÝÞ +Üß²°°ß±±°°°°°°±Ü²ÛÛ +ÛÛÛÛÛÛÛÛÛ²°°²ÛÛÛÛÛÛÛ²²²²²Û²ÛÛ +ÛÛÛÛÛÛÛÛ²²±°±±²Ý ß. +± ²Û² ²ÝÞ +ßßÜß²Ü ²±°°°°±±² +ÛÛÛÛÛÛÛÛÛÛÛÛÛ²°±²ÛÛÛÛÛÛÛÛ²±²²²²ÛÛ +ÛÛÛÛÛÛÛ²²±±±²² ÝþÜ +² ÜÝß ² +²ÝÞ +²Ü ß þ°Üß±°°°°°±² +ÛÛÛÛÛÛÛÛÛÛÛÛÛ±°±²ÛÛÛÛÛÛ² +Û²°±²ÛÛÛÛÛÛÛÛÛÛ²±±±²²Ý +ßÜ°±²ÛÛÜß±Ý +° +Û²² Ü  ß ß±°±°°±±²²ÛÛÛÛÛÛ +ÛÛÛÛ²°°ÝßÛÛÛßßßß +°°ßÜÛÛÛÛÛÛÛÛÛÛ²±±ÜÜ +ßß°²ÛÛܲÛܲ° +ÜÜ  ± +Û²ÞݲÝ Ü°  ß²±±°°±±Û²Û +ÛÛÛÛÛÛÛÛ²±±ßß°°°Ü +°ÜÛÛÛÛÛÛÛÛÛÛÛÛÛ²²±±²ÛÛ  +ÞßÛÜßßÜ°ßß + ÜÛݲ +Û ²ÝÛ ² ݱ²Ü ß²±±°° +±±²Û²ÛÛÛÛÛÛÛ²²Ý Ü +±°²ÛÛÛÛÛßßßß  ÜÛ²±²±² +ÛÝ Ü  ²²±°ßßÜ +߲ݰݲ +ÛÝÛÞݲ°ÝÝ°±²ÛÜ +ß²±±°°±±±²Û²ÛÛÛÛÛ²±Ü +°±²±ßßßÜÜÜ²ß ° ÞÛ +²²±±²ÛÛ ß²Ýß  Þ +°  ÜßÝ°Ý +± +°ÛÜß Üß . +²ßß²Û   ß²±±°±°±²±²²Û +²±°ßß߱ܲ۰۲ßÜ +±°ܱ° Û²Û²±²²ÛÝÜ + Þ ܲ Û ² + Þ°Ý° ± +ÛÛ°°Ü ²Ü² +Üß ²Ûݲ° ß²±±±°±°±±² +Ü  °ßß ÜÜÜܱßÜ +²ßÜ°² Û²²²²²²Û +ßÞ°Ý ²  +ÝÞݳް ޲ݲ +° +Û°ßßܱßÛßÛÝ +Üß ² Þ±²  ß²²±°°°± +°° ß °²ÜÜßßßßÜ +ܲß²ßÜÜÛÛ²±² +Ûß ° ±² Þß°°  +  ° ݲ ÝÞ° +Ü ßß°ß±²Ûß±²ß +ß ÜÞ²Ý ±  ß²±±°° ° +Ü ßßß ßßßßß +ÜÜÜÛÛ²Û²²Ûß +  Û°²²Ý °±°  +Ý°Ý ²± Þ± +ÛÛÜ ܲÜÜܱßÜ þß² Û + ² ßß +Ü° °° °ÜÜÜÜ +ÜÜÜÛÛÛÛÛÛ²Ûß  +Ý°±²ÛÛ °² +°±° °±²  Þ° +ÞÛÝÜ°Þ²Þ²ÝÜ° ²°ÜÝÞ²Þ +°  °° ßß °±² +Û²ÛÛÛÛÛÛÛÛ²²ß ß + °²Û²ÛÛ °²±° +Ý °°± °± +²ÛÛÛÜßÜ ° °° +²²ÝÞ° ±²   +°   °°  ßß + °±±²±²²²Û²ßß  ß + °±²ÛÛÛÛÛ ²Û +°±°² +ÞÛÛ°ÛÛ² ÝÞ°°°² +² ±± ²Ý Ý +   ° °°° ßßßß  +  ß °°²ÛÛÛÛÛÛÛ +²  °  ² °± + ÝÛÛ ÛÛ  °°±°°²ÝÞ +ݲ ß ²  °± +° ° Þ °° +²ÛÛÛÛÛÛÛÛÝ  ° +ܲÝÞ±° + ÞßÛ° ÛÝÞ²°°°°°²ÜßÞ²±Ü + ±  ± ° ° ° +  ° ±²²ÛÛÛÛßß Ü° +ß²²  °  + ²ßÛ°Ûݲ±°°±°±°°ÛÞÛ ²ÛÜ + ° ²°°° °  +Û °±²ßßß ÜÜÜ +ßܲ°Ü  ßß² + Þ°Üß°° °²°±±°± +°°Ý°Û ²ÛÛÜ ÞÝ +   °°  ° + ²ßß Ü þÜ +ßÜÜß²°±ßÜ²ß ²Ý +²Ü +  ²°Üß±°ݲ°°±±± +±°Ý°°Û °² +Û°Üß   ° ° ° +     ÜÜß +±ßÜܱܲ°ßß²Ü + °ß ÜÝß   +±² + ²°± ±° ²°°± +°±±Ý°°°° ° +ÛÛ°°Ü  °  +  Ü°Ü ° +Ü°±Ü²°°ß°±ß߲߲ +ܱܲ߰ÛÜß + ÜÛÛ² ÞÝ  ° +  ß²° ÛÛ ²°°±±±± +Þ°±°°Üß°Û +°°Û  °ß±° +²°±² ß²Ü Ü²ÜßÜ°ß± +²ß²ßßßß Ü Û +ÛÛ²² ÝÞÛ + ÜÞ +ßÛ² Ü°°ß +°ÜßÛÜß°°±±±Þ±° +±°°°Ü° °°Û   +  ß ßß  ß ß + þ Ü Ü ÜÜÜÛß +°ÝÞ²Û²±  ² + ° ÝÞ +°° ² ²ß°°ÜÛÜß +°°±°Þ°±±±°±°±°°±°° +° °  °²Ü²Ý²ÛÞÜÛÜÛÛÜÛÛÛÛÛ +Û²ÛÛÛ² ²±²± °  +Ý Ü ²S™ + ° ° ² Üß±°Ü Û Û +°°Ý°±²±±±±²±°±±°Ý + °±²±²±²² ÞÛÝÛÛÛÛÛ²ÛÛ +ÛÛÛÛÛÛÛÛÜÜ °° +Ûß° Þ± Þ +Ý +°ßß² °Ü² Üß±²Ý  +ÛÜ߰ݱ°±±²±²²²±²±²± +  ²Û²Û²²Û ²Û ÛÛ +²ÛÛÛÛÛÛÛÛÛßßßßß +°²Þ Üܱ²ÝÞ +° þ Þ +°°²ßÜÜßÜ Þ²ß±²° ÛÛÜÝ +°°°±±Üß²²²±±±°Ý  + ²ßß²Û²ÝÛÛ Û²Û²ßß°Ü°° +ÜÜÜ°±°±Üß ÞÛ±±² + ßÝ ßÜÞ +ÜÛÛ²ÛÛ² Ü Þ +°Ü±±°ÛÛÛÝÜß°° +±±²²²±±±±±ÝÞ² +²²ÜÜÛÝÞÛݲÛ² °Ü +±°°±²±²²²±²± ÜÞ²±² +ÝÞ² ß² Ý +ÛÛÛÛÛ² ²²° ² +°°±°°ÛÛÝÛÛ Û°°±±Þ +±±±±±° ²²Üß²ÛÛ + Þ²ÝÞ²Ý Þ±²± +²²Û²ÛÛÛ²Û Þ² ±° +± ±Ý +ÛÛÛÛÛ ²Û²Ý° ²Û°°° +ÛÛ ÛÛÛ ²ß°±Ý°± +ݱ±± Þ²ÛÜ ² +Ûݲ²ÛÞ±°Ü²²Û²ÛÛÛÛÛÛ +ÛÛ Üß ²Üß +Þ°  Þ  Ý +ÛÛÛÛÝÞÛ²Üß²  Üß° +ÛÛÛÞÛÛÛÛ ±²ßÝ°°±Þ±° +ÛÛÛÛ ß ÞÛÝÞ²±²Û² +ÛÛÛÛÛÛÛÛß°²ÜßÞÜßß ÜÝ + Þ +ÛÛÛÝ Û° ²Û±° +ÞÝÞ ÛÝÞÛ°ÛÛ²Ü ²Þ +²°° ²Ý ÞÛÛÛÛ   +²ÛÛÞÛ²ÛÛÛÛÛÛÛÛÛÛ± °ßÜÜÛ²° ° +ÝÞ ß²Ü   +ÛÛÛ ° Ý°Û²° Ü + ² ÛÛ ÛÛÛÛÛÛ²Û ²ÝßÝ Þ +  ÞÛÛÛÛÝ ßÛÛ ÛÛÛÛÛÛÛÛÛÛß +°ÜÛÜÛÛß°± ²ßܲ ß  +Ý +ÛÛÝÞ±² Û²° ܲÝ +Þ°ÞÝÞßÛÛÛ°ÛÛÛÛ ²   +°ÛÛÛÛÛ ²ÞÛ ÛÛÛÛÛÛÛÛß ÜÛÛ +ÛÛß²°  þ ܲÝÞ °Ý +ÞÛ +ÛÛ ²²ß°²° +ßÛÛÝÞ±ß ÞÝÞÛ°° +ÛÛÛÛÝ°Ý   +ÛÛÛÛÛÝÛ Û ÛÛÛÛÛÛß ÜÛÛ +ÛßÜ ± ܲ ÞÛÛ² +Ý Þ ÛÛ +Û ±² ±°° +²ÛÛÛ ² ² ° +ÛÞÛÛ °ÛÞÛ²ÞÛ ÛÛ² +ÛÛÝÛÝÛÝÛÛÛÛß ÜÛÛß² ß°þ + Þß ÜÛß ÜÜÜ ÝÞÛÛ +ßÛ°ß±²° ²ÛÛ +ÛݱÜÛÝ °²ÛÛÛ° +Û°ÝÛ²Þ°ÞÛÛÛ +ÛÛÝÞÛ ²ÛÛß ÜÛßÜ ß þ + Ü ² ÜÛÛÝ ßß²°° +Ü ÛÛ +²° ±° °±²²Û +Û Û°Û Þ° +ÝÛÛÛÞ°°ÛÞݲ°Ý +ÛÛ²Û²Û ÛÛÝ Üßܲß ±²Ý° + ܱßßܲÛÛßÜ +°ÛÜÜ ßÝÞÛÛ +²° ° °ß²²²ßÜÛ +°° ÝÞÛ²ÞÛ°Þ +°°°ÝÝ°ÛÝ +ßÝßÛÛÛÝ²ß ß °ß Ü ß  ÜÜ + ±²ÛÛÛÝÞÛÛ°Û°² +Ý ßÛÛ +ܲ°ß ²Û²²ßÜÛ +°± ² °ßÜÝ°Ý +°±°°Ý ÞÛ ° + °Ü ß Ü +Ü ßÜßÜÝܲ² ± +²ÛÛÛÛ² ßÜÜßÛßÜ ÞÛÛ +Þ²ß ²²ÛßÜÛ°°± +²Ý Ü°°ÝÛÞ±° +°°Û ÛÝ ° ²ÞÝß +²Ü ²Þ ß± þ þß + ܱ²ÛÛÛÛÛß ß°ß + þܲ ÛÛÛ +ß  ²ÛßÜÛ°°±ßÜ +²± Þ°°Û² ±±±°°° + Þ ß²ß þ °  +ß  þß  ܲ ±²²±²Û²ÛÛÛÛ + ܲÛÜ ÝÞÛÛÛ + ° ßÜÛ°°±ßÜ +²²± °ÛÛ² °±²±°° +° þÜß²°Ü +ÜÜÜþ²±ßßܲßܱ²ÜÛ²ÛÛÛÛ +ÛÛÛÝÞß Þß²Ý ÞÛÛÛ + Ü° °°ßßܱ²±° ° +ÛÛ² Û±±±±±°Ý  °²ÜܲÜÜ +ßßß    °Üܱ²ÜÜÛÛÛÛÛÛÛÛÛÛ +ßßßß  ÛÛÛÛ +Û°°°ÜÜ°°±°±ß ° +Û²ßÜ °²±±²±± °²Û± +ßß Üܲ±° ±²±ß±²°²ÛÛÛÛÛÛÛÛÛÛÛßÜ +°ßß± ÞÛÛÛÛ +°° ²ßßÜ°ß.  +° ßÜÛ °²²²±²² + Üܲ²Û²Û² ޲۲ݲ۲ÛÛÛ +ÛÛÛÛÛÛÛÛÝÞݱ² ÞÜ ° +ÞÛÛÛ² +°ßßÜßß Ü°  Ü° +ßÜ ÛÛ°²Û²±²² +°±Û²²²²²ÛÛÛ²ÞÝÛ²ÛÛÞÛ²ÛÛÛÛÛÛÛ²ÛÛÛÛ + °²°²²Ü° +Ý ° Þ²Û²± +Ü°ß ܲ²° ÜÛßÜÛ +ÛÛ°²ÛÛ²²ß ÜÝ °±² +²²²Û²Û²ÛÛÝÛݲ²²ÝÞÛÛÛ²ÛÛ²ÛÛÛÛÛÛÛÝ +ÞÜÞ±²±±° ± ²±²±ß +ßÜÛß°ß° ߲ܰ +ßÜÛÛ°²Û²Ûß ܲß  ±² +Û²²Û²ÛÛÛÛÜ۲ݲ±²²ÞÛ²Û²Û²±²ÛÛÛßß +°ßÞ±²± Þ²  +±±± Ü +ß±°° ßÜÝÛ°°ßÜÛ° +±²ßßßܲ±ß ÜÜÜ +²Û²ÛÛÛÛ²Û²²±²Þ±²ÝÞ²Û²±²Û²ÛÛß +ܲß ß°Üß° ß + ²Ý°±°ÝÞ² +ßÜ° ÝÞ°ÞÛ° +ݲÛßß Üܲ²±ß Ü Þ +°²ßß ² +ÛÛÛ²²±²±±²Ý°±±°²±²±²Û²ÛÛ  þ +Ü ÜÝ ÜÜ °ÞÝÞ +°°ÝÞÝ +ÜÜÜÜÜßß²ÜÜßÜÜÜ°±²²±ßÜÛ² þ +Üܲ²Ü ÛÛ²Û²²±°°°±°°± ± +²Û²ÛÛÛÛÝÞÝ °°ÛÜܲÝÞ +°Ý Ý Þ°°° + Û +Üßß²ÝßßßÜÜÛÛ°°±±²±°°Ûß   +²Û°°ßÜß ßÛÛÛ²² +±°±° °° ²ÛßßßÜ ßÜ +°ßßß²Üß °° ± +°Þ°°°ÝÞ +°°Ü ²Þ°Û²ÛÛ +°°±²±±° ßÜþ Þß°± +ÝßÜ°ßÜ ÜÜ +ß²±°± ° Þß Ü ÜÝÞ² Ü ßßßß +ܱ°± ÞÝ °°±°° +Ý +° ² ²±°ÛÞÛ°°±°±°° +ßÜß²Ü߲߱ +Þ² Ü°±ßÜß + ÜÜ   Ü Ü +±° Þ² ÛÜÛÛÝ ²Û²±²± +±Ý °Ýݱ±°±°° +° Ý Ý°ÛÝÛÛ°±±°° +ßÜß ²ÛÛ²Üß +ß °Ü Üß +  ۲߲ ÞÝ +Þ±°Ýß °ÝÞÞÛÛ +Û Þ²²²²²±   Þ +Þ²±±±°± + Û  Þ°ÛÞÛ°±±±° + Û   Ûß°°°ßÜ° +ÞÝß°°Ý ÞÞ² +Ý° °Û°°°±Ü + Ü ° ²ÛÛÝ  +ÛÛ²Û²²² Þݲޱ²±²±± + °° °ÛÝÛÛ°°±° + Û   °Ü² ±°°°°±°Ý +޲߰ Û Þ² +±°Ý ÞÞݲß²±°²Ý + ß ßßÜÛÛÛÛÛ²ÛÝ +± ݲ²²²²²² +°°Ý °ÛÞÛ°±°± + Û  °Ü²±Û °±°°±°± +± ß°ßÞþÛ +Ü Üß±° ß°ܲ +±²ßß ܲÜÜÛÛÛÛÛÛÛÛ² +Ý°ß° Û²²Û²²² + °Ý ÞÝÛ°±±° ÛÝ + ܲß°°Þ ±°±°±±±²Ý° + ßßßß Ü ßܱ +²Ü  ßß °²±²ÛÛÛÛÛÛÛÛÛÛÛÛ  + ÞÜÞÛÛ²ÛÛ²Û + ° °°Û°°°ÝÞÛ  +Þ²±°±°° ²±±±±±²±²Ý±² +Þ²Û²Û²Ü ßßß ÜÝ +° ±°±²ÛÛÛÛÛÛÛÛÛÛÝ ² Û² +ÛÛÛÛÛÛ +° Ý Þ°±°Û ÛÝ ²± +°±±°± Þ±±±²±²²²² ±Üß +ÛÛÛ²²ÛÛÛ²° ° ²ÛÛÛÛÛÛÛÛÛß ° +Þ ÞÛÛÛÛÛ²ÛÛ +° °  ±Û° ÛÞÛ +Þ±±±±±±± Þ±²²±²²²Û²ÛÜ + ßÜß²²ÛÛÝÞÛ²° ²ÛÛÛÛÛÛ +ß Ü   ÛÛÛÛÛÛÛÛÛ +±°°°Ý°Þ ÝÞ° + ²±±±²±±± Þ²±²²²²Û²Û +ÛÛÛÛÜ þ ßß²Û²±° ° ²Ûßß + Üܲß ° ÛÛÛÛÛÛÛÛÛÛ +±°±°° °ÛÝ°Ý +Þ±±²±²²±± Þ²²²²Û²Û +ÛÛÛÛÛ² ° Ü °  ²Û²±° + Üܲßßß  ° ÛÛÛÛÛÛÛÛÛÛÛ +²±²±²²° ÛÞÛÝÞ²±²²Û +²±²Ý°²Û²ÛÛÛÛÛÛÛ²± ° ²Ûß + ß  þ °Ü °  ßß + ° °±Ý°ÞÛ²ÛÛÛÛÛÛÛÛÛ +Û²²²²²Ü° ß  ²² +Û²ÛÛ²ÛÝ ²ÛÛÛÛÛÛ +ÛÛ² ° ±²Û°   ÛÛÛ²Û² ° °±° +  Þ²²ÛÛÛÛÛÛÛÛ² +ÛÛÛ²Û²²Ü °Ü + ° ²ÛÛÛÛÛÛÝ +ÛÛÛÛÛ²Û² ° ²Ûß°° ° +  ÛÛÛ²Û²± ° ° ± ±²²ÛÛ²ÛÛ²± +Û²ÛÛÛÛÛÛÞ°Û ²ÜßÛ +²ÛÛÛ² Þ²Û²²±° ° °²Ûß  ° + °±°° °  +ßÛÛÛ²Û±±° ÞÝ ° ±° ° ßß +²±²Û²Û²ÛÞ±° ÜÜ þ +ßßÛÛ±ÝÞ²²± ° °²²ß °  + ° ±²±°±°°  ßÛÛÛ²Û +±²± ²Ý ° °±° °  +± ±²±²±²Ý²°Ý ²Û²ÛÜÜܲ±² + ± ° ±²²ß ° °°°°° +²±²±²±°±° ° ßÛÛ۲۲ݱ Ü° + ° °°  +²° ° ° ß° °Ý Þ²±Û²Û²±° +Þ  °²±²²ß ° °°±°± ° + ° ²²²²±²±±±°±° ßßÛ +ÛÛ °Þ²Ý ܲÛÜÜÜÜܲ +Ûܲ²°±°²±°Üß °°²±²±° ² +°²Û²ß °°°°°°±°±² ±±°°Þ²Û²²²²±²±±± ° +ß Û²± ÞÛÛÛÛÛÛÛÛ +²ÛÛÛܲÛܲ±²²ÜÜ °°±²Ý± ²Ý ß ° ° +°±°±²±²± ±²² ²ÛÛÛ²Û²²²±²±±±°± +   ° ßß²ÛÛÛ²Û +ßß²ÛÛÛ²ÛÛ²ÛÛ²Û²Üܲ۲°ß °  ° °±°± +±²±²²±²²Þ²²Û ÛÛ²ÛÛÛÛ²Û±²²±²±±°°  +Ý °±°  °    + ßßßßßßßßßßß±°±²±²²²²²Û²Û² +²Û²ÞÛÛÛÛÛÛÛÛÛ²Û²²±²²± ± °°°°° °± ܱ +²S™ + °°° ° ±²Û²Û²ÛÛÛÛÛÛÛÝÛÛÛÞÛÛÛ² +ÛÛ²ÛÛÛÛÛÛ²Û²² ±°±° ±±²²±²Û²± +°°° °°° ° ° ° + ° ° Ü  ±²²ÛÛÛÛÛ²Û +²ÛÛÞÛÛ² ÛÛÛ²Û +²ÛÛÛÛÛÛÛÛÛ²ÝÞ ±²±± °²²ÛÛ²ÛÛÛ² +° °°°°°°±°°±°±±° °±±²Ý  Þ²ÛÛÛÛÛ² +ÛÛ²²ÛÞÛÛÛݲÛ²Û²²²ßÛ +²ÛÛÛÛÛÛ ² ²Û²²±²ÛÛÛÛÛÛÛ²Ü +°° °° °°°±° °° ± ß  ß  +ßß ß    ß +ßßßßß +ßß ÞÝßß ßßßßßß ß  +° ° °° ° +   °°°  ±° + °Ü°°Ü°±° ° ° +°Ü°° °°Ü±°° +°Ü°  ° °°Ü°° +±° °Ü Ü° +    Ü ܲÜ±Ü +²±Ü²²ßܲßÛÜ ß߲ܲ߰²ßÛ²Ü߲߲ܲ²ß²±²ÜÜß +Û±²Û° Þ Ü°²ß±² +ܱ°Üß߲ܰ²Ü +  °ß° ß ß° +° °°ß°° ±²°°   + °Ü± °± °°  ° ° + °  ° °± ° +° ° ° °±° +ß°° °° ± +²Ü²ÜÜßÜßÛ²ÜܲßÜß²Ü  ßܲßß²Ü +߲߲ÛÜß²ÛܲÜß߲߲߲ܲß ² +ßܲ°±ßܲ²±ß° +߲ܲ°Ü +°ß°°ß°ß±° °± + °ß °°ß²° °° +° °± °ß °° +°°°°ß° ±°ß ° + ÞÝ ° ß °± + ° °° ±²ß +°ß° + Ü ÜÜÜÜÜÜ ÜÜÜܲÜÜ +  ÜÜÜÜ²Ü ÜÜÜÜÜÜÜÜÜÜÜܲÜÜÜÜ +ÜÜÜ ° ÜÜÜÜ +ÜÜÜÜÜÜÜÜÜ  ß þ + Üß ° ° ° °° °°² +  ܱ° ÜÜÜÜ Ü²Ü±°±° ° ° Ý +Þ  ° Ü °Ü±°° ° ß  +ß +Þ°ÜÜܲ±° °° °°±±²²Û² + ÞÛ°ÞÛÛÛÛ²ÛÛ²Û²² +²±²±±°±° °Û ±Þ°° +ÛÛÛÛÛÛÛÛ²²±±° ° °° +ÛÛÛ²±°° °°°±±²Û²ÛÛÝ +ÞÛ°°ÛÛÛÛÛÛ²Û²²²²²²²±° +°±°  ° ²°ÞÛÛÛÛÛÛÛ² +Û²²±±±±°±° +ÞÛÛ²²±°° °°°±±²±²ÛÛÛÛ  Û +°°ÛÛ²ÛÛÛÛ²Û²²²²±²±±±°Ý +ÜÜ Þ±°ÛÛÛÛÛÛ²ÛÛ² +²±²±²±±°° +ÛÛÛ²±°°°°°°°±±²±²Û²ÛÝ° +ÞÝ°ÞÛÛÛÛ²Û²²²±²²±±±±°± + °Üß °ÛÛÛ +²ÛÛÛÛÛ²Û²²²²±²±±° +ÞÛÛÛ²±°° °°°°°±²Û²ÛÛÛ +° ß°Û²Û²Û²²²²² +²±±°±°ß ܲ +ÜÜÛÛÛÛÛÛÛÛÛ²Û²²²±² +±±±±° +ÛÛÛÛ²±°°°°°°±±±²²ÛÛÛÝß²Ü +±²²²²²²±²±±°±±° Û±Ü +ßþÞÛÛÛÛÛÛÛÛÛÛÛ²²±±²±±±±°° +ÞÛÛÛ²±°±°°°°°±±±²²ÛÛÛÜßܲ +Üß²ßßß±±±±°±° +ßÜÛ²² ÜÜÛÛÛÛÛÛÛÛÛ²Û +²±²²±±±°±°°  +ÞÛÛÛ²²±°°°°°°±°±±²±²ÛÛÛÜßß± +°ß ß ßßß +. ß ±ÛÛÛÛÛÛÛÛÛÛ²²²²²²± +±°±±°°°  +ÛÛÛ²²±°° °°°°±±²²Û²ÛÛÛÛÛÛ +²Üß ß° ²Üþ + ß ° ÜÜ +ÛÛÛÛÛÛÛÛ²ÛÛÛ²²±²±±±±°°°°  +ÛÛÛÛ²±° °°°°±°±±²Û²ÛÛÛÛÛÛÛÛ +²²±Ü ÜÜ +ÜÜÜÜÛÛÛÛÛÛÛÛÛÛÛÛ²²²±²±±±°° +°°°  +ÛÛÛÛ²±°° °°°°±±²±²ÛÛÛÛÛÛÛÛÛÛ²±°° + ±Þ°±°±²ÛÛÛÛÛÛÛÛÛÛÛÛÛ +²²²±±±±±°°°°° °  +ÜÛÛÛÛ²±°°°°°°°°°±±±²ÛÛ²ÛÛÛÛÛÛÛ +Ûß²±ÝÞ² °ß +±²²²ÛÛÛÛÛÛÛÛÛÛÛ²Û²²²±°± +°±°°°  +ÜÛÛÛÛÛ²±°° °°°°°°±±±²²²ÛÛÛÛÛÛÛßß +ßÜÜܲ°±Üß ß +²²²ÛÛÛÛÛÛ²Û²²²²±²±±±±±°°°°° °  + Ü ÛÛÛÛ²²±°°°°°°°°°±°±²Û²ÛÛÛÛÛÛÛÜ +ßßÛ°±²Ü°°Ü +°±° ßß ÛÛÛÛÛÛÛÛÛÛ²²²²²±²± +±±±°±°°°°  +Ü°°° Û²²±°±°°°°°°±°±±±²ÛÛÛÛÛÛÛÛÛÛ +ÛÛÜÜ ß±Üßß°°ß +ÜܲÛÛÛÛÛÛÛÛÛÛÛÛ² +²²²²±±²±±±°±±°°° +°°±±² ±°°±°°°°°±°°±±²±²²Û² +ÛÛÛÛÛÛÛÛÛ²²ßÜ ß +°±ßÝß Ü²ÛÛÛÛÛÛÛÛÛÛÛ +Û²Û²²²²±²±±±±±±°°°  +±±²²²Û °°°°°°±°±±±±²±²²ÛÛÛÛ +ÛÛÛÛÛÛÛ²²Üß ÜÝÞ² +  ܲ²²ÛÛÛÛÛÛÛÛÛÛÛ +ÛÛ²Û²²²±±±±±°°°°  +²²ßÛ²   °°°±°±±±±±±²² +²Û²ÛÛÛÛÛÛÛÛÛ²Û²²²±°Ý +ÞÝ  ²²±±²Û²Û +ÛÛÛÛÛÛÛÛÛÛÛ²²±±±±±±±±°°  +²Û°ß ܱ  +°°°±±±°±²±²²Û²ÛÛÛÛÛÛÛÛÛÛÛ²²²±²±° +Þ ÝÞ°±²±±²ÛÛÛÛÛÛÛÛÛÛÛÛ +²²²²±²²±±°±°°°  +ß ÜÛ°ß ° +°°°±±±±±²²ÛÛÛÛÛÛÛÛÛÛÛÛÛ²²²±°±°  +ÝÞÛÞ±²±²±±²ÛÛÛ² +ÛÛÛÛÛÛÛÛ²²²²²±±±±°°° °  + ÜÛ°ß ÜÜÜ + °°±±²±²²²²ÛÛÛÛÛÛÛÛÛÛÛÛ²²±²±° +° ßß±±²²±²²²ÛÛÛÛÛÛÛÛÛ +²²²²²±±±±±±°°°  +²ßß °Ü²Û²ß  +°±±±²²²ÛÛÛÛ²ÛÛÛÛÛÛÛÛ²²² +±±±° ßÜ°Þ°²²²±±²ÛÛÛÛÛÛÛÛ +²ÛÛ²²²²±²±±±±°°°°  + ÜÜÛ°±ßß Ü² +°±²²²Û²ÛÛÛÛÛÛÛÛÛÛÛÛÛ²²² +±°°° ݲܲ°±²²±±²²²ÛÛÛÛÛÛÛÛÛ +Û²²²²²±±±±°±°°°° +ÛÛÛ°ß Üܲ²²² ±± +²²²Û²ÛÛÛÛÛÛÛÛÛÛÛ²Û² +±±±°°° Ü°°±²²±±²±²ÛÛÛÛÛÛÛÛÛ +Û²²²²²±±±±±±±±°°  ²ßß Ü² +²²²²²²² ±±²Û²ÛÛÛÛÛÛÛÛ +ÛÛÛÛÛ²²²±±±±°°²Ü°°±±°±±²Û +²ÛÛÛÛÛÛÛÛÛ²²²²²²±±±±±°±±°° + ܲ²²²²²²²²²²² ±²²²Û² +ÛÛÛÛÛÛÛÛÛ²ÛÛ²±²±±°±°°°°±°±±±²²²ÛÛ +ÛÛÛÛ²ÛÛ²²²²²²±±±±±±±±±°° +Þ²²²²²²²²²²²²²²² ±²±²²Û² +ÛÛÛÛÛÛ²Û²²²±±±°°°° °°°±±²²²²ÛÛÛÛÛ +ÛÛÛÛ²²²²²²±²±±²±±±±°±° + ²²²²²²²²²²²²²²²² ±±±²²±²Û² +Û²²±²²±±±±°±°° °°±±²²Û²ÛÛßßßßÛ²Û +²²²²²±²²±²±±±±±±±°° + Þ²²²²²ß ÜÜÜÜÜÜÜÜÜÜÜÜÜ ß + ÜÜÜÜÜÜÜÜÜÜÜÜ ß ÜÜÜÜÜÜÜÜÜÜÜÜÜ ß +Ü  ÜÜÜÜÜÜÜÜÜÜÜÜÜ ß± +±±±±° +Ý ²²²²² Ûß°ÛÜ +Û Ü ÛÜÛß°Û Û +ß±ÜÛ Ü ÛÜÛ +°ßÛ ÛÝ °ÜÛ Ü Û +ܱÛßÛ Ü Ûß±ÜÜÛ +Ü ÛÜÛß°Û ±±°±°° +° Þ²²²² ÛÜÛÛÛÛ + ² ßÛÛÛÛß + ßÛÛÛß ° ÛÛÛÛÛÛ ßÛ +ÜÜÛß ± ÛÛÛÛÛ +Û ² ÛܱÛÛÛ +ß ßÛÛÜÛß ±°° +ßß +±Ý ²²²² ÛÞ²ÛÛ +Û ²ß ÜÜÜÜ ß ÜÜÜÜÜÜ +ÛÛÛÛÜÛ ÜÜÜÜÜÜ°± Û +ÛÛÛÝÛ ±Ü + ßßßßßßÜÜÜÜÜÜ ßß Ü + °Ü +²Ý ²²²² Û ±ÛÛ +Û ² ÛÛܲßÛ ÛÛÛÛÛ +Ü ÛÛÛÛÝÛ  ° °°°° + ÛÛÛÛ°Û ± +ÜÛÛÛÛÜ Ü ÛÛÛßÛÛ +°±²ÝÞ² +Û² Þ²²² Û° ÜÛ +Û ² ÛÛÛÛ°Û Û +ÜÛÛÛ ° ÛÛÛß° +Û ° ° °°° ÛÛß °Û +° ÛßÛÛÛÛ ß + ÛÛÛÝ°Û ±²ÛÛ +ß +ÛÛ Þ²²² Û±ßÝßÛ + ² ÛÛÝß±Û Û±ß +ÞÛ   ÛÛÜ°±Û + °°° ° ÛÝÞܱÛ Û + Û±ÞßÛÛ ° ÛÛ +ßÛ±Û ²ÛÛÛÝÞ +ÛÛÝ ²²² Û²±°ßÛ  +ß Ûß°±²Û Û²±ßÛ +ß Ûß°±²Û  ° ° °° + ÛÜ °²Û ² Û² +±°ßÛÜÜÜÛßß°²Û Û²Û²² +ÛÛÝ ²²²Ü ßßßßßßßßßßßßß +Ü ßßßßßßßßßßßß Ü  +°Ü ßßßß  ß +  ßßßßßßßßßßßßß ܲ±²ß°° +ÛÛ² Þ²²²²²²²²²²²²²²²²²²²² +² ° °° ° ²ß +ÜÜ°±²²²Û²²Û²ÛÛÛÛ²Û²±²±Ü +Üß  +Û²ß ÜÜÜÜÜÜÜÜÜÜÜÜÜ ß ÜÜÜÜÜÜÜÜÜ +ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ ß ÜÜÜÜ ß² +ß ÜÜÜÜÜÜÜÜÜÜÜÜÜ ß² +Û² Ûß±ÛÜÛ Ü  +ÛÜÛ°ÞÛ ÛßÛÜ +ÛÛ Ü Û±ßÛÜÛ Ü + ÛÛß°ÜÛ Ü ÛÜ +Û±ßÛ Ûß°ÜÛÛ Û +Ûß°ÛÜÛ   +ÛÜÛß°Û ° +²² ÛßÛÛÛÛ ² +ßÛÜÜÛß ÛÞÛÛÛÛ +² ßÛÜÛÛß ² +ÛÛÝÞÛÛ ß ÛÛÛÛÝ +Û ÛÛÛÛÛÛ ² Û +²ÛÛÛÛ ß ßÛÛÜÛß + ± +²² ÛÛÛÛÛÛ ²ß ÜÜÜÜ +  ÛÛÛÛÛÛ ²ß ÜÜÜÜÜÜÜ +ÛÛÛÛÛÛ ° ÛÛÛÛÛÛ Û² +ÛÛÛÛ   ÛÞÛÛÛÛßß ÜÜÜÜ + ß± +²± ÛÝÛÛÛÛ ² ÛÛÛ +ÜÛÛ ÛßÛÛÛÛ ² +ÛÛÜÛÛÛ Ü ÛÛÝÛÛÛ + ± ÛÛÛÛÝÛ ÛÛÛÛÛÛ +± Û±ßÛÛÛ   Û +ÛÜÛÛÛ ± +±± Û°ßÛÛÛ ² + ÛÛÛÛ°Û Û°ßÛÛÛ +² ÛßÞÛÛÛ ß +ÛÛÝÛÛÛ ² ÛÛÛÝ° +Û ÛÞÛÛÛÛ Û  ßßßß +Ü° ÛÛÛÛÝÛ +² +²± Û±°ßÛÛ ² +ÛÛÛÝßÛ Û±ÞßÛÛ +² Û±ÞÛÛÛ ± + ÛÛÜ°ÛÛ ² Û +ÛÝß±Û Û°ßÞÛÛ Ü + ÜÛÛÛÛÜ ° ÛÛÛß±Û + ± +±± Û²±°ßÛ ß Ûßß°Ü +Û Û²±±ßÛ ² Û²° +ß²Û ß Ûß°±ßÛ ß + ÛÛß°²Û Û²±°ßÛ ß + ÛÛßß±Û ß Ûß°±² +Û ² +±°Ü ßßßßßßßßßßßßß Ü +ßßßß Ü²Ü ßßßßßßßßßßßßßßßßß +ßßßßß   ßßßßßßßßßßßßßß ßßßßßßß +ܲ +±°±°Û ²²²²²²²²²²²²²²²²²²²²²²²² +۲ݰ±²²²²²²²²²Û²Ûß²ÜÛ°ß +Ü °°°±°±±°±±²²±²²²±²²²²²²  +±±°±° Þ²²²²²²²²²²²²²²²²²²²²²²Û²Þ +°±²²²²²²²²Û²Ûß²±±ßß  +Ü °°±°±±±²²±²²²²²²²²²²²²²²² +SAUCE00Cat's Cradle Somms ACiD Productions 19950601°ÀP« \ No newline at end of file diff --git a/mods/art/we-wrong.ans b/mods/art/we-wrong.ans new file mode 100644 index 00000000..30963eb3 --- /dev/null +++ b/mods/art/we-wrong.ans @@ -0,0 +1,22 @@ +[?33l +  ÜÜ ÜÜÜ   Ü ÜÜÜ + Ûß²ÜßÜ Mr.  þ°ÜÛÛß²   ÜÜ ÜÜ    ±Ü   ÜÝÜÛÛÛßßÜ + ÛÜÜÛ²Ü ܲßß  °°°ÛÛÛÛÜ° ß  °°  ßÜÝ ß°±Ü  Üß²²²Ü °° ß²ÜÜÜÜß²ÛÛÛÝ  °   ÜÜÜÛ + ²ÛÛÛÜß ßßÜß  ÜÛ±²ÛÛÛÛÛ²Ü ÞÜ ß    ²²²²²  ÞÛÛÛÛß ±  ß±Ü ±²ÛÛ²ÜÛÛÛÛ²Üß  ÜßßܲÛ + ±²²²²²Ý ß  ÜÛܲ²²ßÛÛÛÛÛÛÜÜ ÜÜ °ܲÛÛßÛÛ²Ü ÛÛÛÛÛÜ ÜßÜ  Üßß²±ß²²²²²²Ü ßÜßßß + °ÛÛÛÛÛ  þßܲ²²²²  ß²²²²²²±Ü ±²²²° ±²²²± ²²²²²²Ü ßÜÛÜ ÜÛÛÛÜ ßÛÛÛÛÛÜÜ   ß± +  ÛÛÛÛß Üþ°  ÛÛÛß ÛÛÜßÛÛÛÛÛÛÜ ²²°° ° ß±±ß°ÜßßÛÛÛÛÜÜ ßÜ  ßÛÛÛ ²²Ü²ÛÛÛÛÛܲÜ + ßßÛܲ ß ܲÜ ÜÛÜÛÛ ßÛÛÜßßÛÛÛÛÝ°°°° ° °°°°°ÛܱßßÛÛÛÛÜÜ  ²ÛÛÛÛ  ßÛÛÛ ßßÛÛÛÛ² + ÜÜÛ²² ÜÜ ß ²²ÛÛÛ ²ÛÛܲ ÛÛ²ß ²²²² ° ±±±± ²²ÛßÜßÛÛÛÛÛÜÜßßÛÛß°²²Û²² Ü ßßßß ÜÛ + ²²²²² ± Üß± ±±²²²  ß²²²²²²ß Ü  ßÛßÜÜܲ²ß  ±±²²± Üßß²²²²²Ü±±± ²²²±± Ü²ß  ÛÜ +  ±±±±± Ü°°°Ü °°±±± ±°±±±±±±°Üß °° ßÜÜþßÜßÜ °°±±±±Üß ßܱ±±±±Üß ²±±°° ÛÜßßß°°°°  +  °°°°Ü²²²²²²²Ü°°°°°²²°°Üß°°°°°Ü°²²²Üßß Ü²Ü °°°°° ÜÜßÜÜßÜ°°°°°Ü°°°°° ß  ܲ²²²± +  ²²Ü²²²²²²²²²²²Üß² °Ü°°° ßß°°°°°Üß±±±ÜßÜß ²²°° ÜÜßß ßÜ°²²²²° °°²² ÜßÜܲ²²ß±° + °ÜÛÛÛÛÛÛ²ßÛÛÛÛÛÛÛ² °°±±± ßÜß±²²²²²²²²ßÜß° ÜÜÛÛÛ² ÜÜ ° ²²²²²°²²Û² ÜÜÛÛÛÛßß Ü + ±²²ÛÛÛ²ß ßÜÛÛÛÛ² ²²²²² °° ßÜßܲ²ÛÛÛÛÛÛÛ  ²ÛÛÛ²²ß ²°ÜÜÜ ÛÛÛÛ²²ÛÛÜÜÜÛÛÛÛÛßÜÜ²Ü + ² ÜÜÛÛÝÜÜÜ þÜßÜßß± ²ÛÛßܲÜþ   ßÜßܲ²ÛÛÛÛÛ ²²²²ß   ²ÛÛ²ÛÛÛ²ß ÜÜÛÛÛÛÛÛßÜÜÜßß + ÛÜÛÛÛÛ²Üß Ü  ßÜßÜ° ²²ßÜ±ß  ÜßÜ ßÜßß²²²Ûß°°²Üß  Ü° ±±ÜßÛÛ²Üß  ÜÛÛÛÛÛÛß ß ß Ü° + ±²ÛÛÛÛÛ²²ßÜ    ß±± ßܱß  ÜßÜÜß° ß²²²Ü  ßß ÜÜßÜÜþ  ßß ÜÝ ÞÛÛÛÛÛÜ ÜÜßNo! + ²²²ÜÛÛ±ßÜß Ü±ÜÜ    ßß ßÜßß ÜÛÛÜÜßÜÜÜß² ²²±  Ü ÜÜ  °°°°°°°ß k1.°° + ²²Ü²Üß Ü²Ü ß   þß ÜþÛÜ f r 3 d Ü ÜÜ ß þ  ß ß ßßßß +ß ßßß ßß diff --git a/mods/art/zO-theGhetto.ans b/mods/art/zO-theGhetto.ans new file mode 100644 index 00000000..8e77925f --- /dev/null +++ b/mods/art/zO-theGhetto.ans @@ -0,0 +1,74 @@ + + +  ÜÜÜÜÜÜÜ   Ü Ü Ü Ü Ü Ü Ü Ü Ü Ü ÜÜ Ü Ü Ü Ü Ü Ü ÜÜ ÜÜ ÜÜ +  ÜÛßß±   ± ÜÛÛProjectÛ  ßß±Û +  ²² ²²²² ÛÛÛ± Û    ÜÜÛTHE GHETTO BBS Menu Theme   Ûß Û + ÛÜß Üß °   ± ÛÛÛ+o: Greg Youngblood   ÛÛ Û + Û Û Û°°°²°   Û ÛÛ Û Û ÛÛÛ Û Û Û Û ÛÛ ÛÛß ± +  ±± ß ßß ß ²   Û±ÛÛÛCreated by enzO of Blocktronics Üßß ° +  ÛBLOCKTRONICSÛ   ß ßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜþ + +  ÜÛÛ ÜßÜ ßÛ ÜÛÛ     E-mail: enzo@blocktronics.org +  ßÜ Û Û ÛÜ ÛÜß + ± + + +°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° + + +ÛÛÛÜ Û Üß Ü ÜÜßÜßÜß±ßÛßÜßÜ Ü ±ßÜßÜ Ü Ü Ü ÜÜþÜßÛÜÜßßÜ ÜÜß ÜÜÜÜÜþÜÛÛÜ ßÛ Üß ÜßÜ ßßÜßÜ ßÛßÜßßÛÜß ßÜß ßþß ß Üß ß ÜÜÜÜÜÜÛÜßßßßßÜßÜÜßßÜÜÛß ßÜ ÜßÜÛÛ±Û Ü Üß ÛßÛßÜßÜßÜßßßÜßÜÛßÜ ß ÜßÜßÛÜ ß ÜßÛÜÜÜÜÛßÜÛÛÛ ÜÜßÜÛ ÜÜÜßßÜþßßßÜßÜßÜÛÛÛÛ Û Û ÜßÜßÜßÛÛ Ü ßßÛÛßÛß ÜßÜßÜßÜÜß ÜßÜßÜ þßÛßÛßÜß ÛÜÜÛÛ ÜÛÜßÜÜß ßßÜßÜßÜÛÛÛÛÛ Ü Ü ß ßÜßÜ ÛÜ ßÛ ßÜßÜßÛßÜß ßÜ ÜßÜß ß ß ß ÜÜ ßÜßÛßÛßÛÜß ÜÜÛÛÜÜ ß +ÛÛÛÛßÜÜßÜ ÜßÜßÜßÜßÛÜ ß ßÜßÜßÜßÜßßßÜßÜßÜß ßÜßÜ Üß ß ² ß ßßß ÜÛÛÜßÜ ÛÜÜÛÛÛÛÛÛÛÜÜßÜß ßÜ ß ß ßÛÜßÜßÜßÜßÜß ßÜßÜßÜß ßÜß ßÜßÜßÜßÜßÜßÜ ßßÜßÛÛÛÜÛÜßÛÛÛÛÛÛÛÛÛÛÜßÜßÜÛßÜÜ ß ßÜßßÜßÜßÜÜ ßß ÜÜ ßÜßÜßÜßÜßÜßÜß ßÜßÛßÛÜßÛÛÛÛÛÛÛÛÛÛßÜÛßÜÜßß ÜÛß Üß± ÜßÜßÜß ßßßÜ ßß Üß ßÜßÜß ßÜßÜßÜÛÛÛÛÛÛÛÛÛÛÛÛÛßþÛßÜ ßßÜ Üß ² ÛÜßÜßÜßÜß ÜßßÜ ßÜßÜßÜßÛßÛß ßÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜ ßÜß ßÜÛ Ü ÛÜÛßÜßÜß ÜÜÜßß Ûßß ÜÜß ÜßÜ ÜßÜßÜ ßÜÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÛÜß ÜßßÛÜ ßÜ ± ßþÜÜÜÛÜßÜ ßÜßßÜÜßß ±ÛÛÛÜÜÜ ß Ü ßÜßÜßÜß ÜßÜÛÛÛÛÛÛÛÛÛÛÛÛÛÛ±Û Û ± Û ß ÛßßÜÛÜÛÜßÜßÜÜÜßÜÛ ß ßß ÜÛßÛÛÜÜÜ þÜ ß ß ßÜßÜÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÛÜ Ü ßÜÜß Ü þßß ÜÜÛÛÛÛÛÜßßßßÜ ÜßÜßÜßß ÜÛÛßß ÜÜßßÜßÜÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ°ÜßÜ þÜÜß ÜÜ ÜßÜÜÛÛÜßÜÛÜßÛÜ Ü Ü Ü Ü ÜßÜ ÜÜ ßßßÜÜ Ü þÜ +ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ ÜÜßÜÜ ßß ÜÜßÜß ÜÜßÛÛßÜÛÛß² ÜßÜßÜ Û ßÜßÜß ßÜÜÜÜßÜ ßÜ ÜßÜ Û ÜÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛßÛÜÜÛßß ÜÛÛß ß ÜÜÛÛÛÛÛÜßÛ±ÜßÜÛÜß ßßÜÜÜßÛÜÛÜÛÜßßÜÜÜ ÜßÜßÜ ÛÜÛÛÛ Û ÛÛÛÛÛÛÜßÜÜÛÛßÜßÜßßßÜÛßÜÛÛß ÜÜßÛÜÛÛÛÜÛÜßßÛÜßþÛÜßÜ ß Ü ±ÛÛÛLogin_ÛblocktronicsÛ ÛÛÛÛÛß ÜßÜßÜßß ÜÜßÛÛÛÛßÛß ÜßÜÛÛÛßÜßÜÛßÜÜßÜÜß ßß ß ÜÜßܲÛÛÛPassw_ *******ÛÛÛÛÛÛÛÛÛÛÛßÛ Û ßÜÜßßÜÜßÛßÛßÜßßßß ßßÛßßÛÛÛÛÛÛÛßßÜÜÛßß ÜÛ ÜÜÜÛÜßÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜ ÜÜß ÜÜßÜßÜßßÜ ÜßÜßÜßÜ ÜßßÜÛÛÛßßÜþßÜÜßÜÛÜÜßßÜßÜÛÛÛÛÛTHEÛÛÛÛÛÛÛÛÛÜÛÛÜÛÛÛÛÛÛÛÛÛÛ ß ÜÜ ßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßßÜÛÛ ß ßßÛÛÛÛÜßßÜÜßÛÜÜÛÛÛÜßßÛÛÛÛÛÛßßßÛßßÛßÜßÛÛÛÛÛÛÜÜÛÛß Ûß ßÜß ÜÜßßßßÜÜßÜß ßÜßÜ ±ßßÜßÜßßßÛÜßÜÜßÛÛÛÛÛÛÛÛÛßÛÛÛßÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜ ±ß ß Üß ÜÜÜß ÜÜ ß Ü   ß ßÜßÜßÜßÜßßÜÜßÜßßÛÛÛÛÛßßÛßÛßÛÛßßÛßÛÛßÛßßÛÛÛÛÛÛÜßzO Üþß Ü ÜßßßÜßÜ Û ßÛßßÜßܱ ß ÛßßÜÜß + + + +ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ²ÛÛÛÛÛÛÛÛÛÛÛÜÜÛÜÜÛÜÛßÜÛ ÛßßÜÜÜßß ±þÜÛÛÜÜ ßßÜßÜßÜßÜßÜÛÛTHEÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛßÛß ÜÛßÜß ßßßß Û Ü ÜßÜßßÜÜ ßßÜ ßÛÜÛÜÛÛÛÛÛÜßÜÛÛÛßßÛÛßßÛÛÛÛÛÛÛÛÛßÛÛÛßÛÛÛÛÛÛÛÛÛÛÛßß ÜÜÜß ÛÜßÜßÜßÜßÛß ÜÜßÜÛ ß ÜÜÛßÜÛÛÛÛÛÛÛÛÜÛÛ ÛÛÜÛÛ ßßÛÛÛßßÛÛ ßßÛ ßßÛÛßßÛÛÛÛÛÛß ß ÜÜÜ ÛÜßÜßÜß ßÜßÜßÜßßÜÜßÜÛ± ßßß ßÛÛÛ²ÛÛÛÛÛÛ ÛÜ ÛÛ ÛÛ Û ßß Û ÛÛÛ ÛÛÛ ÛÛ ÛÛÜÜ ÛßßÜ ßßÛ±ÜÜÛÜÛß²ÜÜßÛÛÛÜ ßßß±ßÛ ±ßßßÜÜÛÛ ÛÛÛÛÛÛÛÜßßÜÛÛ±ÛÛ ÛÜßÛÛÛÜßßÛÜßßÛÜßßÜÛÛÛÛ ßÛÜÜßÜÛ ÜÜÛÛÛÜܲÜÛÛÛÛÛßÜßÜÜßßÜÛ ÜÜÛßÛÜÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ²ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÛÛÛÛÛÛÛÛßÛÜ Üßßß ±ÛÜ ÛßÛÜÛÜÛÛÛßÜ ÜÜÛß ßÜ ßß ÜÛÛÛÛÛÛÛÛßÛÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ ÜÜÜßÜ ßß ²ßÛ ßßÜß ßÜÜÜßÜßÛßÛ ßßÜÜßÛÛÛÛÛÛÛßÛÛ [O]ption 0ÛÛÛÛ[O]ption 9 ÛÛÛÛÜßÛÛßÛÜ Û Ü ßÜÜÛÜÛÜÜßÛßÜßÛßß²Û ÜßÛ ÛÛÛÛÛÛÛÛÛÛÛÛ[O]ption 1ÛÛÛÛ[O]ption 8 ÛÛÛÛ±ÛÛÛßÜßÜÜß ß ßßßßßÛ Üßßßß ßßßÜÜÜÜÜßÜÜßÛÛÛÛÛßÛÛÛÛÛ[O]ption 2ÛÛÛÛ[O]ption 9 ÛÛÛÛÛÛÛÜÛÛÛÜÜß ÜÜ ß ßß ÜÜÛ² ±ÜßÜ Û ÜÜÜÛßÜ ÛÛÛÛÛÛÛ[O]ption 3ÛÛÛÛ[O]ption 10ÛÛÛÛÛÛÛÛÛßÛÛßÜÛÜÜßÛÜÜ ÜÜ  ÜßÛÜÜÜßßÛÜßß ßßÜßßÜÛßÜßÜÛÛÛ[O]ption 4ÛÛÛÛ[O]ption 11ÛÛÛÛÛÛÛÛÛÜÜß ÛÛßÛÛß ß ÜÛßßÛÜÜÜÜßÜÛ ÛßÛ Üß ÜßßÜßßÜßÛßÜÛÛ[O]ption 5ÛÛÛÛ[O]ption 12ÛÛÛÛÛÜßßÛÛÜßÜß² ßß Ü ßßßßÜß ÛÜßß ÜÜßßßßÛßÜßÜÛÜÛÛÛÛÛ ÛÛÛÛÛÛ ÛÛÛÛÛÜßÛÜßÜß ± ßÜßÛÜÜÜþÜßÜßÛÜ Ü Û ÜÜßßÜßßÜÛßÛÛ[O]ption 6ÛÛÛÛ[O]ption 13ÛÛ ± ßÜÛß ÜÜ ± ßßÛ ßßßßßþ ßß Üßß ÜÜ ßÜßÜßßßÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜßÜßß ÜßÜ Ü ÜÜܱ ÜÛßßßßÜÛÜÜßÜÛÛÛÛ±ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÛÛÜÛÛß ÜÜßÜßþÜ Üß ÜÜß ± ÜÜßß Ü ÜÜßßÜÜ +ÛÜÛßÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛßÜÛÛßÜßÛß ÜÛß ßÜÜܲßÜÜÛ ßßßþÜÜßÜ +ÛÛÛÛÛÛÛÛÛÛÛÛÛÜÛßßÜÛÛÛÛÛ ÜÜÛÜÛßßÜ Üܱ±Û ÛÜÜßßÜÜßÛÛÛÜßÛÛÜÜÜßßÜÜ +ÛÛ ±ÛÛÛÛßÛÛÛÜßÜÜÛßÛß ÛÜÜßßÜÛ ² ± ÜÜßÛÜÜÛܲÛßÜßÜÛÛÛÛÛßÛÛ± ÜÛÛÛÛÛÛÛÛÛ ßÜÛÛÛÛÛßÛß ÜßÛßÜ ßß ß±ÜÜÜÜÜßÜ ÜßÜÜß ÜßÜßÜß²ßÜßÜÜßÜßÜÛ zOÜÛÜÜÛßÛÛÛÛÛÛÛÛÛÛÜÜÛßßÜÛÜßÜßßÜßßÛÜßßßÛÜß ÜÜÛÜ ÛÜÜÜ ÜßÜÜßßÛÜßÜßÜÛßÜ +ÛÛÛßÜÛÛÜßÜÛÜÜßÛÜÛÛßÛÛÜßÜßÜßÜ Üß± ÜÜß ÛßßÜÛ²Û Ü ÜßßßßÜÜÜßÜßÜ ßßßßܲÛÛß + + + +ÜßÛßß  Üß ßÜßÜßÜßÜßÜßÜßÜ ÜßÜ ÜßÜ ß ± Ü ±ß±ÛÜÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜßÛÛßß Ü ß ÜßÜß ±ÜßÜßÜÛÜÛÛßÜß ÜÜßÜ ± ÜßÜß± Ü Û Ü Ü ÛÛTHEÛÛÛÛÛÛÛÛÛßÛÛÛÛÛ ÛÜÛ ÜÜ ßß± Üß ß ÜÜßÜÛÛÛßÜÛÛßÛ ÜßÛÜ Û±ÜßÜÛÜ ßÜ ÜÛ Üß ÛÛÛßßÛßßÛßÛßß ß ß ßÛßß ÛßÛßß ÛÜÜÜßÜÜÛÛßßÛÜßÛßÜß± ÛßÛÜ ßÛßÛÜÛÜß Üß Üß ²ÜÛÛ ÛßßÛ ß Û ßÛÛ Û Û ²² ÛÛÜÜ ßÜßÜßÜßÛÜßßÜÜßÜßÜß Ü ÜÜß±ßÛßÜßÛÜÛ ÜÜ Üßß ßÛÛÜßßÜÛ Û ÛÜßÛÛ Û°ÛÜßßÜÛÜ Û ÛÜßþßÜßÛÛÛßÜßÜßÜßÜß ßÜßÜßÜßÜßÜßÛ ßÜ Ü Ü Üß ÛÛÛÛÛ²ÛÛÛÛÛÛÛ±ÛÛÛÛÛÛÛÛ ß Ü ßÜ ÜÜ ßßß ÜÜßÜÛÜßÜßÜßÜßÜß ß ßÜßÜßÜßÜßÜßÜ Üß Ü Ü ±Ü°ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜ Ü ßÜß Ü Ûþ ÜÜ ßÜßÜßÜÛÜßÜßÜ ßÜ ßÜßÜßÜßÜß ÜÜß ÜßÜ ²Ü± ÛÛÛÛÛ[O]ption 1ÛÛÛÛÛÛßÜßÜ ßÜßß ÜÜßÜßÜßÜßÜßÜþßÜ ßÜßÜ Ü ßÜÜ ß±Ü ±ÛÛÛÛÛ[O]ption 2ÛÛÛÛÛÛ ÜÜÜßÜÜ ßÜÜ ß ßÜÛ ÜÜßßÜ ßÜß Üß ÜßÜþ ßßÜ ß ßÜßÛÛÛÛÛ[O]ption 3ÛÛÛÛÛÛß ßÜ Ü ÜÛÜß ÜÜþßßÛß ÜÜÛÛÜ ± Üß ±± ÛÛÛÛÛ[O]ption 4ÛÛÛÛÛÛß ßÜßÜßÜ ÜßÜÜ ß ßÜÜÛßßß ÜÜß ± ±ßÜÜþß ß Ü Üß ß ±ÛÛÛÛ[O]ption 5ÛÛÛÛÛÛ ßÜÜßÜßÜÛÜÜ ß Üß ßÜÜß ÜÜßßÛÜßþÜÜßßÜÜ þß ÜßÜß ÜÜßÛÛÛÛ[O]ption 6ÛÛÛÛÛÛ ßÜßÜ ßß ß ß ÜÜ ßÛßßþÜßÜÜÛÛÜÜßÜ ßß Ü ß Üþß ß ßÜß ßÜß ÛÛÛÛ ÛÛÛÛÛÛÛÛÛÛÛß ß ß Ü ßÜ ßÜßÜßÜßÜÜßßßßÜÛ  ÛÛÛÛÛÛ± ßÜßßß Ü ß Üßß ÜÜ ß ß ÛÛÛÛ ÛÛÛÜÛßÜÛÛÜÜßÛß ß±ß ÜÜß ±ÜÜÜÜÛßÛßÞ Üþ Ü ± ÜÛ ÜÜÛÜ ß Û±± ÛÛÛÛÜÜßßßßßßßßÜßþ ÜÜß ±Üßß Ü ß ÜÜßÜßÜßßßßßÜß ÜÜ Üß ±Ü ÝßÜÛß ± Û  ßÜþ ßßÛÜßß Ü ßßÜßÜ ± ß Û Ü ßÜ ßß ÜÜÜÜßßÜÜßßþ ÜþÜ ß Ü±ß ± Ü ß±ßÜÜß Üß ÛÜÛÛÛÜßßÜß Ü ßßÜßßþ ßÜÜ ß ÛÜ ßÜß Ü ß ÜÜß Ü ÜÜÛÜßÜþßÜßÛÜßÜÜ ÜÛ ßÜÜßß Ü ßÛÜßßßÛ ÜßÜßßÜÜÜßÜßÜÜ Ü Û ÛÜ ß ß ßÜßÜ ß ß ß ßß ÛÜß Ü ßÜÜßÛßß ÜßßÜ ß Û ÜßÜßßßßÜ ÜßÜßÜ ßÜßÜßÛ Ü Û ßÜ ßßßÜÜܱ ± ÜÛÜ ß ßÜÜÛßßßßÜÜ ß Û þß ÜÜßß Ü ßÜßÜßÜÜß þßÛ ßß ÜßÜßßßßßÜÜ ßÜßÜÜ Û Û ± Ü ßßÜÜ ßßßßßßþÜ Û ±ß ÜßÜÜßßßÜÜ ß ßÜÜß ÜÜßßÜß ÜßÜß ßÜ ßÜÜßÛÜÜß ± ± Ü ß Üþß ÜÜ ÜÜÜÜÛÜ ± Ü ÜßÜßßÜþßÜßÜß Üß Û ß Üßß ßÜß ßÜ ß ßÜÜ ß Û ±ÜÜ ß ÜÜþ ß± ßß ßßÜ ßÜßÜß ß ßÜßÜÛ Üß ÛܱzO ÜßÜÜßÜßÜ ß ÜÛßþÜ ß þß Üß ß þ ± Üþ Ü ßÜÜßÜß ßßÜÛßÜ ßÜÜßÜ + + + + +ÜÛßßßßßß ßßßßßßßßßßßßßßßßßßß ßßßßßßßßßßßßßßßßßßßßßßßßßß ß ßßßßßßßßßßßßßßßßßÛþÜÜÛ  ÜÜ ÜÜÜÛÛÛßÜßÜßÛÛÛÛÛÛÜ Ü ÜÛÛÛÛÛßÛßÜßÜÛÛÛÛÜÜ ÛßÛÜßÛÜßÜ ÜÜÜßÜÛÛÛÜßÜÛÛÛÛÛÜßßÜ ÛÛÛ  ßÜßÛÛÛÛßÜÜ ßßßÜßÛÛÛÜßÛßÜßÜÛÛßßÜÜßÛÛÛÛÛß ÛÛÛÛÛßßÛÜ ÜßÜßÜßÜÛÛßÜÛÛ²ÛÛÛßÛÛÛÛÜß Û ² ÛßÛÛÛÛßÜÜß ÜßßÜßÛßÜßßÜß ß ßÜÛÛÜÜÜÛÛÛÜÛÛÛÛÛÛÛ  ÜÜ Û ßßÛßßßßßÜ ÜßÜ ÜßÜßßßÜßÜßÛÛ±ÛÛ±ÜÜßÛÛÛßÜ ± ß ÜßßÜß ß ² ßÜßßßÜßÜßÜÛÛÛÛÛTHEÛÛ Ûßßß ÜÜ Û ÜÜ Ü ÜÜÛ ÜÜÛ ÜÜ ßÛܱÛÛÜÛ ÜÜÜßÛÜßßÜ Ü ß ± ßÜ ± ß Ü ßßÜÜÛßßÛßÛÛÛÛÛÛÛ  ßÜ Ü Üß ß ÜÜÜÜ ßßß ßßß ±± ÜßÜ ÛÜßÛ ßßßÜÜßß Ü ßÜÜ ßÜ ßÜÜ ß ÜÜ ßßßÜÛßÛßÛÜÛÛÜÜÜÜÜÛÛ²ÛÜÜÜÛÛÜÜÜÛÜÜÜÛÜÜÜÜÛÛÛ ÛÛÜß ÜßßÜß Üß Ü ßþÜÜ ßß ÜÜßß ÜÜ ßß Ü ßßÜßÛßßÜßÜßÛÛÛ ßßÜÛÛÛÜÛßÛÛßÛßÛÛßÛßÛß±ÛÛ± Ü Üß ßÜÜ ßßÜÜÜßßßÜÜÜ ßßßÜßÜÜÜßßÜÜ ßÜ ßÜßÛßÛßÜÜßßßÜßÛßÛÛßÜÜßßÜß ßßÜÜßßß Û  ßÜÜ Ü ßßÜÜßßÜÜßßßÜÜ ßßßß ÜÜßßßÜÜÜßßÜ Ü ß²ßßÜßßÜßßßÜÜßßßÜß±ß ßÛß ÜÛ Ü ßþÜÜßßþÜ ßß ÜÜ ßßÜÜþßß ÜÜ ßßßÜÜßÜÜ ß ± ° ß ß ß _menu title ß ßÜ ÜßÜÜ ßßÜÜ Ü ßßÜÜÜÜßÜÜ ßßÜ ßÜÜÜßßÜÜ ßßÜßßÜ ±ßÜ ßÜßßßÜÛ ÜßÜÜßÛÜÜÛÛßÛÜÜßÜÜ ÜßÜÜÛßÜÜßÜ ßÜ ßÜ [O]ption 0 [O]ption 5 ß + ß ÜÜß ßÜ ßßßÛÛßßßßßÜßÜßßß ÜßÛÛÜÜÛ ßÜ ß° ² [O]ption 1 [O]ption 6ßßÜÜ ß ÜßÜÜÛßßÛÛÛÛßÜÜßÛßÛ ÜÜÜÛÛÛßßßßÛÜÜ ß Û ± [O]ption 2 [O]ption 7 +Ü ß ÜßÜß ÛßßßßÜÛßÛÜß±±ÛßÜÛÛßß ßßþÜßÜÜ ² ± [O]ption 3 [O]ption 8 ßÜ ß ÜßÜÜÛßþß ßß ßßßÜÛß ÛßÛßß ÜÜÛÛÛÜÜ ÜßÛ ±[O]ption 4 [O]ption 9 ÛßÜßßÜ ±ÛÛ ÜßÛ± ÜÜßßÛÜÜÛÜÜÛßßÜÜ ßß ßßÛܲÛß± ÜÜÜÜÜÛÜßßÜß ßÛÛßßßÜÜÜÜßßÜßÜßÛßÛßÜßþß ÜÜÜÜ ß ßÛß ÜÛßßßÜßÜÜÜßÜ ßß ß ÜÛÜ ±ÛÛßÛÛ ßß ÜÜÜÜßß ÛÛßÛÛ± ÜÜÜÜß ßß±ÛßÜßܱ ÜÜÛÜß ÜÜßÜ Ûß ß ÛÜßßÜßßßß ÜßÜ ÜÜÜÜÜ ÛÜÜ ßßßßÜÜÜÜÜÜßÛÜÜ ßßÜÜÜ ßßÜß ÜßßÜßß ÜÜÛÜÜß +Ü ±ßÛÛßÛßÛÜÛßÜßÜßÜßÜßÜßÛÜÛÜÜÜßÜÜÜÛÛÛÛÛÜß ß ßßÜßßÜþÜÜ ÛÜÜÜß ÜÜßÜßÜßÜÜßÜ ßÜÜÜÜÜÜ ßßßÜÜßßÜßÛßÜßÜßÜÜÜÜÜÜÜßßÜßÜßÜßÜÜßßß ÜÜßßÜ ÜÜÛßßÜ ÛßßÜß ßÜÜÜßß Üßßßþ Ü +ßÜßÜß ßÜÜþÜßÜÜ ßßß ßßÜÛßßßßß ßßß ÜÜÜßßÜßßÜÛÛÜÜßßßÜÜß ßßß ßÛÜßß ÜßÜÜß ßßÝzOÜßÜÜÜßÛßÛßßßÛÜÜþÜÜÜÜßþÜ ßß ÜÜÜÜ ÜÜßßÜßÜÛßÜÛßÜßßÜÜßÜþÜÛßßÜÜþÜ ßßß Ü ßÛßß ßÜßÜßÜ + + + +±ßÜßÜ Ü Üßß + ÜÛ Û Û ÛßÛßßßÜÜßÜÜÜÜ ±ÜÜÜÜÜÜ Ü±±ÛÛÛ± ±ÛÛ Ü Ûß ß ß ß ÛßÜÜÜÛ ß ßÜÜß  ÜÜÜßÜ Û ÛÛÜßܱ ÜÜ   Û ± ÜßÜß ÜÜ ß ßÛÛÛ ±ÜßÜß ÜÜ ± ÛÛÛ ± ± ÛÛÛ ß ß ßÜß ÜÜÜß± Û Û Û Üß ÜßÜÜÜÜÜß ÜÜ ÛÛß ÜÜÜßßÜ ÜßßÜÜÜÜßÜ ÛÜÜÜÜÜÜ ÜÜÜÜÜÜܱ ÜßÛÜÜÛßÜ ßÜ Û Û Û ÜßÜÜÛÛÛÛÛÜÜßßÜ ßÛßÛ±ÛÛÛÜܱ ßÜÜÛÛÛÛÛÜÜÛ ÛÛßßÛÛÛ ÛßßßßÛÛ ßÜÜÛÛÛÛÜÜßÜ ± + Û ± Ü°ÜßÜ ± ÛÛÛ Üß ÛÜÜ ± ÜÛÛ ÜßÜ ± ßÛÜÜ ÛÜÜ Û Ûßß ± ÛÜßÜ ÛÛßÜß Û ± ± ÛÜÛ Û ßßß ± ÛÛÛ ° ÛÛÛ ÛÜÛÜß ÛÛÛ ÛÛÜ ² ÛÛÜ Û ° ÛÜÛ ±ÛÜÛÛÜ Û ± Û± ÛÛÛÛÛÛ ° ÛÛÛ ±ÛÛÛÛÜßÛÛÛÛ± ÛÛÛ Û ± ÛÛÛ     ÛÛÛ ² ßÛß ± ± Û ÜÜÛÛÛ ±ÛÛÛ ± ÛÜÛ ° ÜÛß ±ÛÜÜ ÜÜÜÛ ßÛÛ ± °ÛÛß ± ± ßÛß±ßß ÜÛ Ü ± ÛÛ ß ÜÜÛÛÛß ßß ÜÜ ± ÜÜß ± ßÛÜÜ ßßÛÜÜÜÜ ÜÜþßÛÛ ÜÜÜ ßÛÜÜÜ Ü ßßÛÛÛÛÛßßÛß ± Û ßÜÜß ÜÜ ßßÛÛÛÛÛÛß ÜÜÛÛÛÛ ÜÜÛÛ ßÜßßþÜ Üßß ÜÜßßÛßÜÛ ßÜßßÛÜÛ Ü ßÜÜßßßßÜß Û ±ßÜÜßßßßÜÜ ßßÜÜ ßß ßÜÜþß ßß ÜßÜÜ ßßÜÜßß ß Ü°°ßÜßß Û Û + ± °ßÜÛß Ü ß Ü Ü ß ßßÜÜ ßÜÜß Ü Ü ß Ü ÜßÜÜ Ü ± Üß Û ± ± Û ± ÛÛÛ ÜÛ ÛÛÜ Û ßÜßÜßÜßÛÜÛÛÜßÛßÜßÜ ßÜßÜßßÛÜÜÜßÜ ± ÜßÜßÜÛÛÜÛßß Ü Üß ± ß Û ÛÜ ÜßÛßÜÜÜÜÛßßÛ ÜÜÜÜ ÜßÜÛÛßÜ ß Ü ÜÜ ß ß ÜÛÜßß Ü Ü ßÜßÜÛÜÛ ÜÜÜ Ü ÜÜß   ßÜßÜÜ ßÜßßÛÛÛßßÜß ÜÜÜ Ü ÜÜßÜÛßÛÜÛÜÛÛÜÛÛÜÛÛßÜÛÜÛÛÛÛÜÛÛÛÜÛÛÛÛÜßÜßßÜßÜ ÜÜÜßÜßß ÜÜÛßÜÜÜÛßÛÜßÜßÜ Ü ßÛÛÛÜÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛßÛÛßÜ ßzO + Ü ÜßÜ ÜßÜßÜßÜßÛÜ Ü ßÛßÛÛÛÛÛÛÛÛÜÛßÜÛ_MENU TITLEÛÛÛÜÛÜÛÛßÜÜÜßßÜÜßÜßÜÜÛÜÜÛÜßÛÜÛßÜßÜßÜÜßÛÜßÛÛÛÛÛÛÛÛ ÛÛÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÛÛÛÜ ÜÜÜÜÛÜ ßÜ ÜßÜ ÜÛÜÛÛßÛ[O]ption 1ÛÛÛ[O]ption 1ÛÛÛ[O]ption 1ÛÛÛ[O]ption 1ÛÛÛÛÛÛßÜßÛßÜÜßÜÛßßÜÜßÛ ß ÛÛÛÛÛ[O]ption 1ÛÛÛ[O]ption 1ÛÛÛ[O]ption 1ÛÛÛ[O]ption 1ÛÛÛÛÛÛÛÜÛÛÛÜÜßÜÜßÜÜÜ ÛßÛ ÛÛÛ[O]ption 1ÛÛÛ[O]ption 1ÛÛÛ[O]ption 1ÛÛÛ[O]ption 1ÛÛÛÛÛÛÜßÜßßÛßßÜÛßÜÛßÜßÛÜ þ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÛÛÜßÜßÜÜÛßÜßÜßÜßÜßÛßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÛßÛßÜßßÜÜßÜßÜßÜ + + + +±ßÜßÜ Ü Üßß + ÜÛ Û Û ÛßÛßßßÜÜßÜÜÜÜ ±ÜÜÜÜÜÜ Ü±±ÛÛÛ± ±ÛÛ Ü Ûß ß ß ß ÛßÜÜÜÛ ß ßÜÜß  ÜÜÜßÜ Û ÛÛÜßܱ ÜÜ   Û ± ÜßÜß ÜÜ ß ßÛÛÛ ±ÜßÜß ÜÜ ± ÛÛÛ ± ± ÛÛÛ ß ß ßÜß ÜÜÜß± Û Û Û Üß ÜßÜÜÜÜÜß ÜÜ ÛÛß ÜÜÜßßÜ ÜßßÜÜÜÜßÜ ÛÜÜÜÜÜÜ ÜÜÜÜÜÜܱ ÜßÛÜÜÛßÜ ßÜ Û Û Û ÜßÜÜÛÛÛÛÛÜÜßßÜ ßÛßÛ±ÛÛÛÜܱ ßÜÜÛÛÛÛÛÜÜÛ ÛÛßßÛÛÛ ÛßßßßÛÛ ßÜÜÛÛÛÛÜÜßÜ ± + Û ± Ü°ÜßÜ ± ÛÛÛ Üß ÛÜÜ ± ÜÛÛ ÜßÜ ± ßÛÜÜ ÛÜÜ Û Ûßß ± ÛÜßÜ ÛÛßÜß Û ± ± ÛÜÛ Û ßßß ± ÛÛÛ ° ÛÛÛ ÛÜÛÜß ÛÛÛ ÛÛÜ ² ÛÛÜ Û ° ÛÜÛ ±ÛÜÛÛÜ Û ± Û± ÛÛÛÛÛÛ ° ÛÛÛ ±ÛÛÛÛÜßÛÛÛÛ± ÛÛÛ Û ± ÛÛÛ     ÛÛÛ ² ßÛß ± ± Û ÜÜÛÛÛ ±ÛÛÛ ± ÛÜÛ ° ÜÛß ±ÛÜÜ ÜÜÜÛ ßÛÛ ± °ÛÛß ± ± ßÛß±ßß ÜÛ Ü ± ÛÛ ß ÜÜÛÛÛß ßß ÜÜ ± ÜÜß ± ßÛÜÜ ßßÛÜÜÜÜ ÜÜþßÛÛ ÜÜÜ ßÛÜÜÜ Ü ßßÛÛÛÛÛßßÛß ± Û ßÜÜß ÜÜ ßßÛÛÛÛÛÛß ÜÜÛÛÛÛ ÜÜÛÛ ßÜßßþÜ Üßß ÜÜßßÛßÜÛ ßÜßßÛÜÛ Ü ßÜÜßßßßÜß Û ±ßÜÜßßßßÜÜ ßßÜÜ ßß ßÜÜþß ßß ÜßÜÜ ßßÜÜßß ß Ü°°ßÜßß Û Û + ± °ßÜÛß Ü ß Ü Ü ß ßßÜÜ ßÜÜß Ü Ü ß Ü ÜßÜÜ Ü ± Üß Û ± ± Û ± ÛÛÛ ÜÛ ÛÛÜ Û ßÜßÜßÜßÛÜÛÛÜßÛßÜßÜ ßÜßÜßßÛÜÜÜßÜ ± ÜßÜßÜÛÛÜÛßß Ü Üß ± ß Û ÛÜ ÜßÛßÜÜÜÜÛßßÛ ÜÜÜÜ ÜßÜÛÛßÜ ß Ü ÜÜ ß ß ÜÛÜßß Ü Ü ßÜßÜÛÜÛÛÜÜÜ Ü ÜÜß   ßÜßÜÜ ßÜßßÛÛÛßßÜß ÜÜÜ Ü ÜÜßÜÛßÛÜÛÜÛÛÜÛÛÜÛÛßÜÛÜÛÛÛÛÜÛÛÛÜÛÛÛÛÜßÜßßÜßÜ ÜÜÜßÜßß ÜÜÛßÜÜÜÛßÛÜßÜßÜÛÜÛßÛÛÛÜÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛßÛÛßÜ ßzO + Ü ÜßÜ ÜßÜßÜßÜßÛÜÛÛÜÛÛÛßÛßÛÛÛÛÛÛÛÛÜÛßÜÛ_MENU TITLEÛÛÛÜÛÜÛÛßÜÜÜßßÜÜßÜßÜÜÛÜÜÛÜßÛÜÛßÜßÜßÜÜßÛÜßÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÛÛÛÜÛÜÜÜÜÛÜÛßÜÛÜßÜÛÜÛÜÛÛÜÛ[O]ption 1ÛÛÛ[O]ptionÛ4ÛÛÛ[O]ption 7ÛÛÛ[O]ptionÛ10ÛÛÛÛÛßÜßÛßÜÜßÜÛßßÜÜßÛÛßÛÛÛÛÛÛÛ[O]ptionÛ2ÛÛÛ[O]ptionÛ5ÛÛÛ[O]ption 8ÛÛÛ[O]ptionÛ11ÛÛÛÛÛÛÜÛÛÛÜÜßÜÜßÜÜÜÛÛÛßÛÛÛÛÛÛÛ[O]ptionÛ3ÛÛÛ[O]ptionÛ6ÛÛÛ[O]ption 9ÛÛÛ[O]ptionÛ12ÛÛÛÛÛÜßÜßßÛßßÜÛßÜÛßÜßÛÜÛþÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÛÛÜßÜßÜÜÛßÜßÜßÜßÜßÛßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÛßÛßÜßßÜÜßÜßÜßÜ + + + +±ßÜßÜ Ü Üßß + ÜÛ Û Û ÛßÛßßßÜÜßÜÜÜÜ ±ÜÜÜÜÜÜ Ü±±ÛÛÛ± ±ÛÛ Ü Ûß ß ß ß ÛßÜÜÜÛ ß ßÜÜß  ÜÜÜßÜ Û ÛÛÜßܱ ÜÜ   Û ± ÜßÜß ÜÜ ß ßÛÛÛ ±ÜßÜß ÜÜ ± ÛÛÛ ± ± ÛÛÛ ß ß ßÜß ÜÜÜß± Û Û Û Üß ÜßÜÜÜÜÜß ÜÜ ÛÛß ÜÜÜßßÜ ÜßßÜÜÜÜßÜ ÛÜÜÜÜÜÜ ÜÜÜÜÜÜܱ ÜßÛÜÜÛßÜ ßÜ Û Û Û ÜßÜÜÛÛÛÛÛÜÜßßÜ ßÛßÛ±ÛÛÛÜܱ ßÜÜÛÛÛÛÛÜÜÛ ÛÛßßÛÛÛ ÛßßßßÛÛ ßÜÜÛÛÛÛÜÜßÜ ± + Û ± Ü°ÜßÜ ± ÛÛÛ Üß ÛÜÜ ± ÜÛÛ ÜßÜ ± ßÛÜÜ ÛÜÜ Û Ûßß ± ÛÜßÜ ÛÛßÜß Û ± ± ÛÜÛ Û ßßß ± ÛÛÛ ° ÛÛÛ ÛÜÛÜß ÛÛÛ ÛÛÜ ² ÛÛÜ Û ° ÛÜÛ ±ÛÜÛÛÜ Û ± Û± ÛÛÛÛÛÛ ° ÛÛÛ ±ÛÛÛÛÜßÛÛÛÛ± ÛÛÛ Û ± ÛÛÛ     ÛÛÛ ² ßÛß ± ± Û ÜÜÛÛÛ ±ÛÛÛ ± ÛÜÛ ° ÜÛß ±ÛÜÜ ÜÜÜÛ ßÛÛ ± °ÛÛß ± ± ßÛß±ßß ÜÛ Ü ± ÛÛ ß ÜÜÛÛÛß ßß ÜÜ ± ÜÜß ± ßÛÜÜ ßßÛÜÜÜÜ ÜÜþßÛÛ ÜÜÜ ßÛÜÜÜ Ü ßßÛÛÛÛÛßßÛß ± Û ßÜÜß ÜÜ ßßÛÛÛÛÛÛß ÜÜÛÛÛÛ ÜÜÛÛ ßÜßßþÜ Üßß ÜÜßßÛßÜÛ ßÜßßÛÜÛ Ü ßÜÜßßßßÜß Û ±ßÜÜßßßßÜÜ ßßÜÜ ßß ßÜÜþß ßß ÜßÜÜ ßßÜÜßß ß Ü°°ßÜßß Û Û + ± °ßÜÛß Ü ß Ü Ü ß ßßÜÜ ßÜÜß Ü Ü ß Ü ÜßÜÜ Ü ± Üß Û ± ± Û ± ÛÛÛ ÜÛ ÛÛÜ Û ßÜßÜßÜßÛÜÛÛÜßÛßÜßÜ ßÜßÜßßÛÜÜÜßÜ ± ÜßÜßÜÛÛÜÛßß Ü Üß ± ß Û ÛÜ ÜßÛßÜÜÜÜÛßßÛ ÜÜÜÜ ÜßÜÛÛßÜ ß Ü ÜÜ ß ß ÜÛÜßß Ü Ü ßÜßÜÛÜÛÛÜÜÜ Ü ÜÜß   ßÜßÜÜ ßÜßßÛÛÛßßÜß ÜÜÜ Ü ÜÜßÜÛßÛÜÛÜÛÛÜÛÛÜÛÛßÜÛÜÛÛÛÛÜÛÛÛÜÛÛÛÛÜßÜßßÜßÜ ÜÜÜßÜßß ÜÜÛßÜÜÜÛßÛÜßÜßÜÛÜÛßÛÛÛÜÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛßÛÛßÜ ßzO + Ü ÜßÜ ÜßÜßÜßÜßÛÜÛÛÜÛÛÛßÛßÛÛÛÛÛÛÛÛÜÛßÜÛ_MENUÛTITLEÛÛÛÜÛÜÛÛßÜÜÜßßÜÜßÜßÜÜÛÜÜÛÜßÛÜÛßÜßÜßÜÜßÛÜßÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÛÛÛÜÛÜÜÜÜÛÜÛßÜÛÜßÜÛÜÛÜÛÛÜÛ[O]ption 1 [O]ptionÛ4ÛÛÛ[O]ptionÛ7ÛÛÛ[O]ption 10ÛÛÛÛÛßÜßÛßÜÜßÜÛßßÜÜßÛÛßÛÛÛÛÛÛÛ[O]ption 2 [O]ptionÛ5ÛÛÛ[O]ptionÛ8ÛÛÛ[O]ption 11ÛÛÛÛÛÛÜÛÛ ÜÜßÜÜßÜÜÜÛÛÛßÛÛÛÛÛÛÛ[O]ption 3 [O]ptionÛ6ÛÛÛ[O]ptionÛ9ÛÛÛ[O]ption 12ÛÛÛÛÛÜßÜßßÛßßÜÛßÜÛßÜßÛÜÛþÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÛÛÜßÜßÜÜÛßÜßÜßÜßÜßÛßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÜßÛßÛßÜßßÜÜßÜßÜßÜ + + +°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +Created by enzO of Blocktronics +enzo@blocktronics.org + +April 22 2014 +SAUCE00 20140422¶©PâIBM VGA \ No newline at end of file diff --git a/mods/connect.js b/mods/connect.js new file mode 100644 index 00000000..82f13f2e --- /dev/null +++ b/mods/connect.js @@ -0,0 +1,55 @@ +/* jslint node: true */ +'use strict'; + +var ansi = require('../core/ansi_term.js'); +var artwork = require('../core/art.js'); +var modules = require('../core/modules.js'); +var packageJson = require('../package.json'); +var util = require('util'); + +exports.moduleInfo = { + name : 'Connect', + desc : 'First module upon connection', + author : 'NuSkooler', +}; + +exports.entryPoint = entryPoint; + +function entryPoint(client) { + /*var self = this; + this.client = client; + var term = this.client.term;*/ + + var term = client.term; + + term.write(ansi.normal()); + + term.write(ansi.disableVT100LineWrapping()); + + // :TODO: set xterm stuff -- see x84/others + + // :TODO: add URL to banner + term.write(ansi.fromPipeCode(util.format('' + + '|33Conected to |32EN|33|01i|32|22GMA|32|01½|00 |33BBS version|31|01 %s\n' + + '|00|33Copyright (c) 2014 Bryan Ashby\n' + + '|00', packageJson.version))); + + setTimeout(function onTimeout() { + term.write(ansi.clearScreen()); + + artwork.getArt('CONNECT', { random : true, readSauce : true }, function onArt(err, art) { + var timeout = 0; + + if(!err) { + term.write(art.data); + timeout = 1000; + } + + setTimeout(function onTimeout() { + term.write(ansi.clearScreen()); + modules.goto('matrix', client); + }, timeout); + }); + }, 500); +} + diff --git a/mods/matrix.js b/mods/matrix.js new file mode 100644 index 00000000..8f2b7908 --- /dev/null +++ b/mods/matrix.js @@ -0,0 +1,36 @@ +/* jslint node: true */ +'use strict'; + +//var art = require('../core/art.js'); +var ansi = require('../core/ansi_term.js'); +var lineEditor = require('../core/line_editor.js'); +var art = require('../core/art.js'); + +exports.moduleInfo = { + name : 'Matrix', + desc : 'Standardish Matrix', + author : 'NuSkooler', +}; + +exports.entryPoint = entryPoint; + +function entryPoint(client) { + var term = client.term; + + term.write(ansi.clearScreenGoHome()); + + // :TODO: types, random, and others? could come from conf.mods.matrix or such + + //art.getArt('SO-CC1.ANS'/* 'MATRIX'*/, { types: ['.ans'], random: true}, function onArt(err, theArt) { + art.getArt('DM-ENIG2-MATRIX.ANS', {}, function onArt(err, theArt) { + if(!err) { + art.display(theArt.data, { client : client, mciReplaceChar : ' ' }, function onArtDisplayed(err, mci) { + if(err) { + return; + } + + console.log(mci); + }); + } + }); +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 00000000..c52441cd --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "name" : "enigma-bbs", + "version" : "0.0.1-beta", + "description" : "ENiGMA½ Bulletin Board System" +} \ No newline at end of file