diff --git a/exec/chksetup.js b/exec/chksetup.js
index 4f4c28924d7f0ce436aee00a80540305cdf86783..45684d11f3efb4efce56ee9745f70cd2e022072b 100644
--- a/exec/chksetup.js
+++ b/exec/chksetup.js
@@ -219,7 +219,7 @@ var tests = {
 		try {
 			finger_obj = JSON.parse(finger_result.join(''));
 		} catch(e) {
-			return 'finger ' + finger_query + '@' + finger_host + ' result: ' + e;
+			return finger_result.join('');
 		}
 		var bbs = list[lib.system_index(list, system.name)];
 		bbs.entry = undefined;
diff --git a/exec/load/http.js b/exec/load/http.js
index 071ca35a4243929543dbf8c657fc9f48fda9a2bd..ca31a61394e3ffeb362251cb432f8944a3ce7e72 100644
--- a/exec/load/http.js
+++ b/exec/load/http.js
@@ -220,10 +220,7 @@ HTTPRequest.prototype.Get=function(url, referer, base) {
 		&& this.response_headers_parsed.Location.length
 	) {
 		this.follow_redirects--;
-		const re = /([a-z]+:\/\/.+:{0,1}\d{0,5})\//i;
-		var loc = this.response_headers_parsed.Location[0];
-		if (loc.search(re) < 0) loc = url.match(re)[1] + loc; // Assumes 'url' will match 're', but if it doesn't you'll have other problems anyway
-		return this.Get(loc, url); // To-do: be less tired and think about referer,base
+		return this.Get(this.response_headers_parsed.Location[0], this.url.url, this.url.url);
 	}
 	return(this.body);
 };
diff --git a/xtrn/3rdp-install/exitilus.ini b/xtrn/3rdp-install/exitilus.ini
new file mode 100644
index 0000000000000000000000000000000000000000..2661ab124473d5a31e0fbdf0bc0973b391ce12e5
--- /dev/null
+++ b/xtrn/3rdp-install/exitilus.ini
@@ -0,0 +1,38 @@
+Name: Exitilus
+Desc: Turn-based, RPG set in medieval times
+By:   Tao Ge,Ricky Link,ECI Software
+Cats: Games
+Subs: RPG
+exe: EXITILUS.EXE
+
+; "player file in use" during maintenance - do "touch player.dat"
+; "news not found" on game reset - create dir pnews
+
+[md5:5c8efef0badeeb892f501c09b7356f95]
+ver = 3.20
+url = http://www.breakintochat.com/files/doors/ECI/exitv320.zip
+
+[md5:42ca4136942667eaba789fafbf9874e4]
+ver = 3.17
+url = http://www.breakintochat.com/files/doors/ECI/exitv317.zip
+
+[md5:d34e367ed483e5fc4753830127e780fb]
+ver = 3.15
+url = http://www.breakintochat.com/files/doors/ECI/exitv315.zip
+
+[md5:10e5b07f035bf2dac9a1356cee290c33]
+ver = 2.05
+url = http://www.breakintochat.com/files/doors/Ge/EXS205-1.ZIP
+; ALSO url = http://www.breakintochat.com/files/doors/Ge/EXS205-2.ZIP
+
+[prog:EXITILUS]
+name = Exitilus
+cmd = exitilus %#
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/exitilus.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/exitilus.js b/xtrn/3rdp-install/exitilus.js
new file mode 100644
index 0000000000000000000000000000000000000000..8c18a6e8fb4b0069687c3885b9062735e9564f92
--- /dev/null
+++ b/xtrn/3rdp-install/exitilus.js
@@ -0,0 +1,28 @@
+"use strict";
+
+var gamedir = fullpath(js.startup_dir);
+
+var lines = [];
+lines[1] = gamedir;
+lines[2] = gamedir;
+lines[3] = "   0";
+
+writeln("Beginning node config generation...");
+for(i = 0; i < system.nodes; i++) {
+	var nodenum = i + 1;
+	
+	file_backup(gamedir + 'exnod' + nodenum + '.cfg');
+
+	lines[0] = system.node_list[i].dir + 'door.sys';
+	var file = new File(gamedir + 'exnod' + nodenum + '.cfg');
+	if (!file.open("w")) {
+		writeln("Error " + file.error + " opening " + file.name + " for writing");
+		exit(1)
+	}
+	file.writeAll(lines);
+	file.close();
+}
+
+writeln("Config file generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/jibben-boxcars.ini b/xtrn/3rdp-install/jibben-boxcars.ini
new file mode 100644
index 0000000000000000000000000000000000000000..af56bca6c7d2998bc0abcbe77e17649916dbf7e8
--- /dev/null
+++ b/xtrn/3rdp-install/jibben-boxcars.ini
@@ -0,0 +1,23 @@
+Name: Boxcars
+Desc: Dice BBS door game
+By:   Scott Jibben
+Cats: Games
+Subs: Chance,Blackjack.Dice
+exe: BOXCARS.EXE
+
+[md5:da57581a1cca804881d3b0d2a7c01de6]
+ver = 1.01
+url = https://jibbensoftware.com/download/BOX101.ZIP
+;keyfile at https://jibbensoftware.com/download/RBOX777.ZIP
+
+[prog:BOXCARS]
+name = Boxcars
+cmd = boxcars %#
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jibben.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jibben-galacticoverlord.ini b/xtrn/3rdp-install/jibben-galacticoverlord.ini
new file mode 100644
index 0000000000000000000000000000000000000000..aba5f4fe236fb60b6082f46525cf27c61636881d
--- /dev/null
+++ b/xtrn/3rdp-install/jibben-galacticoverlord.ini
@@ -0,0 +1,38 @@
+Name: Galactic Overlord
+Desc: Up to 6 plays play against each other to control a universe of planets.
+By:   Scott Jibben
+Cats: Games
+Subs: War,Space
+exe: GO.EXE
+
+[md5:3b6e4c118b08f7604701fa713c839396]
+ver = 1.36
+url = https://jibbensoftware.com/download/GO136.ZIP
+;key at https://jibbensoftware.com/download/RGO777.ZIP
+
+[md5:2c55001ab710901db3fff4f4ca799c2e]
+ver = 1.37b
+url = https://jibbensoftware.com/download/GO137B.ZIP
+
+[md5:40741e0be3911a7aa75849cc3a0e0bbd]
+ver = 1.35
+
+[prog:GOVERLD]
+name = Galactic Overlord
+cmd = go %#
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jibben.js]
+prompt = false
+required = true
+
+[event:GOMAINT]
+prompt = false
+cmd = gomaint.exe
+name = Galactic Overlord Maintenance
+; all days
+days = 127
+time = 0:00
\ No newline at end of file
diff --git a/xtrn/3rdp-install/jibben-minesofgorr.ini b/xtrn/3rdp-install/jibben-minesofgorr.ini
new file mode 100644
index 0000000000000000000000000000000000000000..82f1b326af0ed3bf06faa90ded3e517ed4b0882a
--- /dev/null
+++ b/xtrn/3rdp-install/jibben-minesofgorr.ini
@@ -0,0 +1,28 @@
+Name: Mines of Gorr
+Desc: 3D Medieval Role Playing Game
+By:   Scott Jibben
+Cats: Games
+Subs: RPG
+exe: GORR.EXE
+
+; Run gengorr prior to first game
+; Key at https://jibbensoftware.com/download/RMOG777.ZIP
+
+[md5:a4875312b7372e43bea86d4234257179]
+ver = 1.00
+url = https://jibbensoftware.com/download/MOG100.ZIP
+
+[md5:7ed23d2bdae4942f8bdbad030cbca066]
+ver = .30
+
+[prog:GORR]
+name = Mines of Gorr
+cmd = gorr %#
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jibben.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jibben-rollthebones.ini b/xtrn/3rdp-install/jibben-rollthebones.ini
new file mode 100644
index 0000000000000000000000000000000000000000..a2fc9e5ce36001a29415668649d1b65b5fc5e1ce
--- /dev/null
+++ b/xtrn/3rdp-install/jibben-rollthebones.ini
@@ -0,0 +1,22 @@
+Name: Roll the Bones
+Desc: Dice game
+By:   Scott Jibben
+Cats: Games
+Subs: Chance,Dice
+exe: RTB.EXE
+
+[md5:ffc4b66aad960f271048834b757b70fb]
+ver = 1.02
+url = https://jibbensoftware.com/download/RTB102.ZIP
+
+[prog:RTB]
+name = Roll the Bones
+cmd = rtb %#
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jibben-rollthebones.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jibben-rollthebones.js b/xtrn/3rdp-install/jibben-rollthebones.js
new file mode 100644
index 0000000000000000000000000000000000000000..8f6bdf29733c691318583d79a6521ed88d360125
--- /dev/null
+++ b/xtrn/3rdp-install/jibben-rollthebones.js
@@ -0,0 +1,37 @@
+"use strict";
+
+writeln("Generating config files...");
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "RTB.CFG";
+var nodefile = gamedir + "NODES.CFG";
+
+file_backup(configfile);
+file_backup(nodefile);
+
+var conf = new File(configfile);
+if (conf.open(file_exists(configfile) ? 'r+' : 'w+')) {
+	conf.iniSetValue("DOOR SETTINGS", "BBSNAME", system.name);
+	conf.close();
+} else {
+	writeln("Could not open " + configfile + " for writing");
+	exit(1);
+}
+
+var nodeconf = new File(nodefile);
+if (nodeconf.open(file_exists(nodefile) ? 'r+' : 'w+')) {
+	for(i = 0; i < system.nodes; i++) {
+		var nodenum = i + 1;
+		nodeconf.iniSetValue("NODE" + nodenum, "DROPFILE", system.node_list[i].dir + 'door.sys');
+		nodeconf.iniSetValue("NODE" + nodenum, "FOSSIL", "Y");
+	}
+	nodeconf.close();
+} else {
+	writeln("Could not open " + nodefile + " for writing");
+	exit(1);
+}
+
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/jibben.js b/xtrn/3rdp-install/jibben.js
new file mode 100644
index 0000000000000000000000000000000000000000..7e33455d2a1202fddacca0a129c251234e80186a
--- /dev/null
+++ b/xtrn/3rdp-install/jibben.js
@@ -0,0 +1,27 @@
+"use strict";
+
+writeln("Generating config files...");
+
+var gamedir = fullpath(js.startup_dir);
+
+var nodefile = gamedir + "NODES.CFG";
+
+file_backup(nodefile);
+
+var nodeconf = new File(nodefile);
+if (nodeconf.open(file_exists(nodefile) ? 'r+' : 'w+')) {
+	for(i = 0; i < system.nodes; i++) {
+		var nodenum = i + 1;
+		nodeconf.iniSetValue("NODE" + nodenum, "DROPFILE", system.node_list[i].dir + 'door.sys');
+		nodeconf.iniSetValue("NODE" + nodenum, "FOSSIL", "Y");
+	}
+	nodeconf.close();
+} else {
+	writeln("Could not open " + nodefile + " for writing");
+	exit(1);
+}
+
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/jns-amazingmemory.ini b/xtrn/3rdp-install/jns-amazingmemory.ini
new file mode 100644
index 0000000000000000000000000000000000000000..243b3d8295dc299bac5ae720009e4ca006425322
--- /dev/null
+++ b/xtrn/3rdp-install/jns-amazingmemory.ini
@@ -0,0 +1,22 @@
+Name: Amazing Memory
+Desc: Simon clone where the game displays a sequence and the user repeats it.
+By:   Rusty Johnson,JNS Software
+Cats: Games
+Subs: Board
+exe: AMAZING.EXE
+
+[md5:80939a979b6ce816f8178a011775a695]
+ver = 4.25
+url = http://bbsfiles.com/ftp/JNS/jnsam425.zip
+
+[prog:jnsam]
+name = Amazing Memory
+cmd = amazing.exe FOSSIL ALIAS
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jns-amazingmemory.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jns-amazingmemory.js b/xtrn/3rdp-install/jns-amazingmemory.js
new file mode 100644
index 0000000000000000000000000000000000000000..c033d108c7d732b8a263fc87b05244a06acd2ed0
--- /dev/null
+++ b/xtrn/3rdp-install/jns-amazingmemory.js
@@ -0,0 +1,91 @@
+"use strict";
+
+writeln("Generating config files...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "AMAZING.CFG";
+var nodedat = gamedir + "DOORINFO.DAT";
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();
+} else {
+	// new config
+	lines[2] = 'Y';
+	lines[3] = 'NOLOG';
+	lines[6] = gamedir + 'amazing.asc';
+	lines[7] = gamedir + 'amazing.ans';
+	lines[8] = "";
+	lines[11] = "2";
+	lines[12] = "27";
+}
+
+lines[0] = system.name;
+lines[1] = system.operator;
+
+var mfile = new File(gamedir + "AMAZING.KEY");
+if(mfile.open("rb")) {
+	var md5 = mfile.md5_hex;
+	writeln("Check key " + md5);
+	if (md5 == "14989a3a738bf58f651f6e5a31d4a48b") {
+		writeln("Restoring free key sysop info");
+		lines[0] = "BBSFILES.COM";
+		lines[1] = "FREE COPY";
+	}
+	mfile.close();
+}
+
+lines[4] = "Synchronet";
+lines[5] = "DOOR.SYS";
+lines[9] = "PCBNODE";
+lines[10] = gamedir;
+
+if (file.open(configfile, file_exists(configfile) ? 'r+' : 'w+')) {
+	file.writeAll(lines);
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+writeln("Beginning node config generation...");
+
+file_backup(nodedat);
+
+var nodedata = "";
+
+for(i = 0; i < 250; i++) {
+	var nodenum = i + 1;
+	
+	// you can use COM1 for every node
+	var nodeentry = (typeof system.node_list[i] !== "undefined") ?
+		"03F8" : "0000"
+	nodeentry = nodeentry + "0" + " ";
+	if (typeof system.node_list[i] !== "undefined") {
+		nodeentry = nodeentry + system.node_list[i].dir;
+	}
+	while (nodeentry.length < 46) {
+		nodeentry = nodeentry + " ";
+	}
+	
+	nodedata = nodedata + nodeentry;
+}
+
+var file = new File(nodedat);
+if (!file.open("w+")) {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+file.write(nodedata);
+file.close();
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/jns-bokedpoker.js b/xtrn/3rdp-install/jns-bokedpoker.js
new file mode 100644
index 0000000000000000000000000000000000000000..5e01d443ab6bdb43984fa013c07dffd0c2f1d829
--- /dev/null
+++ b/xtrn/3rdp-install/jns-bokedpoker.js
@@ -0,0 +1,92 @@
+"use strict";
+
+writeln("Generating config files...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "BOXPOKER.CFG";
+var nodedat = gamedir + "DOORINFO.DAT";
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();
+} else {
+	// new config
+	lines[2] = 'Y';
+	lines[3] = 'NOLOG';
+	lines[6] = gamedir + 'boxpoker.asc';
+	lines[7] = gamedir + 'boxpoker.ans';
+	lines[8] = "";
+	lines[11] = "3";
+	lines[12] = "2";
+	lines[13] = "27";
+}
+
+lines[0] = system.name;
+lines[1] = system.operator;
+
+var mfile = new File(gamedir + "BOXPOKER.KEY");
+if(mfile.open("rb")) {
+	var md5 = mfile.md5_hex;
+	writeln("Check key " + md5);
+	if (md5 == "5a2069529d7a981f2dc1adca3c51c7c1") {
+		writeln("Restoring free key sysop info");
+		lines[0] = "BBSFILES.COM";
+		lines[1] = "FREE COPY";
+	}
+	mfile.close();
+}
+
+lines[4] = "Synchronet";
+lines[5] = "DOOR.SYS";
+lines[9] = "PCBNODE";
+lines[10] = gamedir;
+
+if (file.open(configfile, file_exists(configfile) ? 'r+' : 'w+')) {
+	file.writeAll(lines);
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+writeln("Beginning node config generation...");
+
+file_backup(nodedat);
+
+var nodedata = "";
+
+for(i = 0; i < 250; i++) {
+	var nodenum = i + 1;
+	
+	// you can use COM1 for every node
+	var nodeentry = (typeof system.node_list[i] !== "undefined") ?
+		"03F8" : "0000"
+	nodeentry = nodeentry + "0" + " ";
+	if (typeof system.node_list[i] !== "undefined") {
+		nodeentry = nodeentry + system.node_list[i].dir;
+	}
+	while (nodeentry.length < 46) {
+		nodeentry = nodeentry + " ";
+	}
+	
+	nodedata = nodedata + nodeentry;
+}
+
+var file = new File(nodedat);
+if (!file.open("w+")) {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+file.write(nodedata);
+file.close();
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/jns-bombsquad.ini b/xtrn/3rdp-install/jns-bombsquad.ini
new file mode 100644
index 0000000000000000000000000000000000000000..13df85b09c795cfa13c453393e8b7f96f3bd5075
--- /dev/null
+++ b/xtrn/3rdp-install/jns-bombsquad.ini
@@ -0,0 +1,22 @@
+Name: Bomb Squad
+Desc: A power of deduction game where you try to deactivate bombs before they explode
+By:   Rusty Johnson,JNS Software
+Cats: Games
+Subs: Puzzle
+exe: BOMBSQAD.EXE
+
+[md5:53b97215ab0218a744f54dae8d46f968]
+ver = 1.0
+url = http://bbsfiles.com/ftp/JNS/jns_bs10.zip
+
+[prog:BOMBSQAD]
+name = Bomb Squad
+cmd = bombsqad.exe /fossil
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jns-bombsquad.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jns-bombsquad.js b/xtrn/3rdp-install/jns-bombsquad.js
new file mode 100644
index 0000000000000000000000000000000000000000..0cd92645a6998db90e4a0715cd6ce378b76c50c6
--- /dev/null
+++ b/xtrn/3rdp-install/jns-bombsquad.js
@@ -0,0 +1,92 @@
+"use strict";
+
+writeln("Generating config files...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "BOMBSQAD.CFG";
+var nodedat = gamedir + "NODEINFO.DAT";
+
+file_backup(configfile);
+
+var lines = [];
+	
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();	
+} else {
+	// new config
+	lines[2] = 'Y';
+	lines[3] = 'NOLOG';
+	lines[6] = gamedir + 'bombsqad.asc';
+	lines[7] = gamedir + 'bombsqad.ans';
+	lines[11] = "5";
+	lines[12] = "2";
+	lines[13] = "2";
+	lines[14] = "300";
+}
+
+lines[0] = system.name;
+lines[1] = system.operator;
+
+var mfile = new File(gamedir + "BOMBSQAD.KEY");
+if(mfile.open("rb")) {
+	var md5 = mfile.md5_hex;
+	writeln("Check key " + md5);
+	if (md5 == "70356999d5473d3fb05693dc7aa36407") {
+		writeln("Restoring free key sysop info");
+		lines[0] = "BBSFILES.COM";
+		lines[1] = "FREE COPY";
+	}
+	mfile.close();
+}
+
+lines[4] = "Synchronet";
+lines[5] = "DOOR.SYS";
+lines[9] = "PCBNODE";
+lines[10] = gamedir;
+
+if (file.open(configfile, file_exists(configfile) ? 'r+' : 'w+')) {
+	file.writeAll(lines);
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+writeln("Beginning node config generation...");
+
+file_backup(nodedat);
+
+var nodedata = "";
+
+for(i = 0; i < 250; i++) {
+	var nodenum = i + 1;
+	
+	// you can use COM1 for every node
+	var nodeentry = (typeof system.node_list[i] !== "undefined") ?
+		"03F8" : "0000"
+	nodeentry = nodeentry + "0" + " ";
+	if (typeof system.node_list[i] !== "undefined") {
+		nodeentry = nodeentry + system.node_list[i].dir;
+	}
+	while (nodeentry.length < 46) {
+		nodeentry = nodeentry + " ";
+	}
+	
+	nodedata = nodedata + nodeentry;
+}
+
+var file = new File(nodedat);
+if (!file.open("w+")) {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+file.write(nodedata);
+file.close();
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/jns-boxedpoker.ini b/xtrn/3rdp-install/jns-boxedpoker.ini
new file mode 100644
index 0000000000000000000000000000000000000000..3ad57809403b8e5b62ca6d47b96d389cec0418f5
--- /dev/null
+++ b/xtrn/3rdp-install/jns-boxedpoker.ini
@@ -0,0 +1,22 @@
+Name: Boxed Poker
+Desc: Place Cards in a 5x5 grid to come up with the best ten poker hands
+By:   Rusty Johnson,JNS Software
+Cats: Games
+Subs: Chance,Cards,Poker
+exe: BOXPOKER.EXE
+
+[md5:94ffec12a9142c904b77855482015f76]
+ver = 4.15
+url = http://bbsfiles.com/ftp/JNS/jnsbp415.zip
+
+[prog:boxedpkr]
+name = Boxed Poker
+cmd = boxpoker.exe FOSSIL ALIAS
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jns-bokedpoker.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jns-doublesolitree.ini b/xtrn/3rdp-install/jns-doublesolitree.ini
new file mode 100644
index 0000000000000000000000000000000000000000..af1fe0135a89c24376be0df1a4520fb1c97db5d3
--- /dev/null
+++ b/xtrn/3rdp-install/jns-doublesolitree.ini
@@ -0,0 +1,22 @@
+Name: Double Solitree
+Desc: Like Soli-Tree With 2 Decks
+By:   Rusty Johnson,JNS Software
+Cats: Games
+Subs: Cards,Solitaire
+exe: DOUBLEST.EXE
+
+[md5:4ec138e079a2d9579be43623905955bb]
+ver = 2.2
+url = http://bbsfiles.com/ftp/JNS/jns_ds22.zip
+
+[prog:SOLITREE]
+name = Double Solitree
+cmd = doublest.exe /fossil
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jns-doublesolitree.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jns-doublesolitree.js b/xtrn/3rdp-install/jns-doublesolitree.js
new file mode 100644
index 0000000000000000000000000000000000000000..2cb799cbbb4e3c4baf6fb59d76f66d09b978b7b5
--- /dev/null
+++ b/xtrn/3rdp-install/jns-doublesolitree.js
@@ -0,0 +1,93 @@
+"use strict";
+
+writeln("Generating config files...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "DOUBLEST.CFG";
+var nodedat = gamedir + "NODEINFO.DAT";
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();
+} else {
+	// new config
+	lines[2] = 'Y';
+	lines[3] = 'NOLOG';
+	lines[6] = gamedir + 'solitree.asc';
+	lines[7] = gamedir + 'solitree.ans';
+	lines[8] = "";
+	lines[11] = "2";
+	lines[12] = "2";
+	lines[13] = "2";
+	lines[14] = "JOKERS";
+}
+
+lines[0] = system.name;
+lines[1] = system.operator;
+
+var mfile = new File(gamedir + "DOUBLEST.KEY");
+if(mfile.open("rb")) {
+	var md5 = mfile.md5_hex;
+	writeln("Check key " + md5);
+	if (md5 == "3ffb73eb3ca49349eaec562eeb82ec6e") {
+		writeln("Restoring free key sysop info");
+		lines[0] = "BBSFILES.COM";
+		lines[1] = "FREE COPY";
+	}
+	mfile.close();
+}
+
+lines[4] = "Synchronet";
+lines[5] = "DOOR.SYS";
+lines[9] = "PCBNODE";
+lines[10] = gamedir;
+
+if (file.open(configfile, file_exists(configfile) ? 'r+' : 'w+')) {
+	file.writeAll(lines);
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+writeln("Beginning node config generation...");
+
+file_backup(nodedat);
+
+var nodedata = "";
+
+for(i = 0; i < 250; i++) {
+	var nodenum = i + 1;
+	
+	// you can use COM1 for every node
+	var nodeentry = (typeof system.node_list[i] !== "undefined") ?
+		"03F8" : "0000"
+	nodeentry = nodeentry + "0" + " ";
+	if (typeof system.node_list[i] !== "undefined") {
+		nodeentry = nodeentry + system.node_list[i].dir;
+	}
+	while (nodeentry.length < 46) {
+		nodeentry = nodeentry + " ";
+	}
+	
+	nodedata = nodedata + nodeentry;
+}
+
+var file = new File(nodedat);
+if (!file.open("w+")) {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+file.write(nodedata);
+file.close();
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/jns-fivecarddrawpoker.js b/xtrn/3rdp-install/jns-fivecarddrawpoker.js
new file mode 100644
index 0000000000000000000000000000000000000000..809b2f9ca28ba37b609efdcf279f70b09fed4b76
--- /dev/null
+++ b/xtrn/3rdp-install/jns-fivecarddrawpoker.js
@@ -0,0 +1,92 @@
+"use strict";
+
+writeln("Generating config files...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "DRAWPOKE.CFG";
+var nodedat = gamedir + "DOORINFO.DAT";
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();
+} else {
+	// new config
+	lines[2] = 'Y';
+	lines[3] = 'NOLOG';
+	lines[6] = gamedir + 'drawpoke.asc';
+	lines[7] = gamedir + 'drawpoke.ans';
+	lines[8] = "";
+	lines[11] = "15";
+	lines[12] = "2";
+	lines[13] = "27";
+}
+
+lines[0] = system.name;
+lines[1] = system.operator;
+
+var mfile = new File(gamedir + "DRAWPOKE.KEY");
+if(mfile.open("rb")) {
+	var md5 = mfile.md5_hex;
+	writeln("Check key " + md5);
+	if (md5 == "cee24fb1e8d4035f7f63097e262091b1") {
+		writeln("Restoring free key sysop info");
+		lines[0] = "BBSFILES.COM";
+		lines[1] = "FREE COPY";
+	}
+	mfile.close();
+}
+
+lines[4] = "Synchronet";
+lines[5] = "DOOR.SYS";
+lines[9] = "PCBNODE";
+lines[10] = gamedir;
+
+if (file.open(configfile, file_exists(configfile) ? 'r+' : 'w+')) {
+	file.writeAll(lines);
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+writeln("Beginning node config generation...");
+
+file_backup(nodedat);
+
+var nodedata = "";
+
+for(i = 0; i < 250; i++) {
+	var nodenum = i + 1;
+	
+	// you can use COM1 for every node
+	var nodeentry = (typeof system.node_list[i] !== "undefined") ?
+		"03F8" : "0000"
+	nodeentry = nodeentry + "0" + " ";
+	if (typeof system.node_list[i] !== "undefined") {
+		nodeentry = nodeentry + system.node_list[i].dir;
+	}
+	while (nodeentry.length < 46) {
+		nodeentry = nodeentry + " ";
+	}
+	
+	nodedata = nodedata + nodeentry;
+}
+
+var file = new File(nodedat);
+if (!file.open("w+")) {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+file.write(nodedata);
+file.close();
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/jns-fivecarddrawpoker.js.ini b/xtrn/3rdp-install/jns-fivecarddrawpoker.js.ini
new file mode 100644
index 0000000000000000000000000000000000000000..f622657e6f73dc32c7f1f6dda7d3a2f9f7ec3382
--- /dev/null
+++ b/xtrn/3rdp-install/jns-fivecarddrawpoker.js.ini
@@ -0,0 +1,22 @@
+Name: Five Card Draw Poker
+Desc: A nice poker game where the user plays against the dealing computer
+By:   Rusty Johnson,JNS Software
+Cats: Games
+Subs: Chance,Cards,Poker
+exe: DRAWPOKE.EXE
+
+[md5:3afb3a9e4cfc78b59e65658edce5a06d]
+ver = 2.15
+url = http://bbsfiles.com/ftp/JNS/jnsdp215.zip
+
+[prog:drawpoke]
+name = Five Card Draw Poker
+cmd = drawpoke.exe FOSSIL ALIAS
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jns-fivecarddrawpoker.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jns-fortythievessolitaire.ini b/xtrn/3rdp-install/jns-fortythievessolitaire.ini
new file mode 100644
index 0000000000000000000000000000000000000000..dfa9431e8a15dc3bbb15e970faa5fb47fa921a15
--- /dev/null
+++ b/xtrn/3rdp-install/jns-fortythievessolitaire.ini
@@ -0,0 +1,22 @@
+Name: Forty Thieves Solitaire
+Desc: Nice Solitaire Card game where the user must win to play. Similar To Golf-St.
+By:   Rusty Johnson,JNS Software
+Cats: Games
+Subs: Cards,Solitaire
+exe: FTHIEVES.EXE
+
+[md5:e8d44a1b003a7f1720bfcda5e76b4259]
+ver = 1.2
+url = http://bbsfiles.com/ftp/JNS/jns_ft12.zip
+
+[prog:forty]
+name = Forty Thieves Solitaire
+cmd = fthieves.exe /fossil
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jns-fortythievessolitaire.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jns-fortythievessolitaire.js b/xtrn/3rdp-install/jns-fortythievessolitaire.js
new file mode 100644
index 0000000000000000000000000000000000000000..f3d2e95a33db4f96d1821cb1f4ffdecae9f28950
--- /dev/null
+++ b/xtrn/3rdp-install/jns-fortythievessolitaire.js
@@ -0,0 +1,92 @@
+"use strict";
+
+writeln("Generating config files...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "FTHIEVES.CFG";
+var nodedat = gamedir + "NODEINFO.DAT";
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();
+} else {
+	// new config
+	lines[2] = 'Y';
+	lines[3] = 'NOLOG';
+	lines[6] = gamedir + 'forty.asc';
+	lines[7] = gamedir + 'forty.ans';
+	lines[8] = "";
+	lines[11] = "Y";
+	lines[12] = "2";
+	lines[13] = "25";
+}
+
+lines[0] = system.name;
+lines[1] = system.operator;
+
+var mfile = new File(gamedir + "FTHIEVES.KEY");
+if(mfile.open("rb")) {
+	var md5 = mfile.md5_hex;
+	writeln("Check key " + md5);
+	if (md5 == "f7730187c0ddfa54eaa1a4abcea18ece") {
+		writeln("Restoring free key sysop info");
+		lines[0] = "BBSFILES.COM";
+		lines[1] = "FREE COPY";
+	}
+	mfile.close();
+}
+
+lines[4] = "Synchronet";
+lines[5] = "DOOR.SYS";
+lines[9] = "PCBNODE";
+lines[10] = nodedat;
+
+if (file.open(configfile, file_exists(configfile) ? 'r+' : 'w+')) {
+	file.writeAll(lines);
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+writeln("Beginning node config generation...");
+
+file_backup(nodedat);
+
+var nodedata = "";
+
+for(i = 0; i < 250; i++) {
+	var nodenum = i + 1;
+	
+	// you can use COM1 for every node
+	var nodeentry = (typeof system.node_list[i] !== "undefined") ?
+		"03F8" : "0000"
+	nodeentry = nodeentry + "0" + " ";
+	if (typeof system.node_list[i] !== "undefined") {
+		nodeentry = nodeentry + system.node_list[i].dir;
+	}
+	while (nodeentry.length < 46) {
+		nodeentry = nodeentry + " ";
+	}
+	
+	nodedata = nodedata + nodeentry;
+}
+
+var file = new File(nodedat);
+if (!file.open("w+")) {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+file.write(nodedata);
+file.close();
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/jns-freecellsolitaire.ini b/xtrn/3rdp-install/jns-freecellsolitaire.ini
new file mode 100644
index 0000000000000000000000000000000000000000..1b758b0956b1ebc16add5dc7a8034c2f884e33d5
--- /dev/null
+++ b/xtrn/3rdp-install/jns-freecellsolitaire.ini
@@ -0,0 +1,22 @@
+Name: Free Cell Solitree
+Desc: Solitaire Card game that takes a lot of skill to remove cards from table
+By:   Rusty Johnson,JNS Software
+Cats: Games
+Subs: Cards,Solitaire
+exe: FREECELL.EXE
+
+[md5:c5d98d5b76cb53f415a626c4c8a2443a]
+ver = 1.0
+url = http://bbsfiles.com/ftp/JNS/jns_fc10.zip
+
+[prog:freecell]
+name = Free Cell Solitree
+cmd = freecell.exe /fossil
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jns-freecellsolitaire.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jns-freecellsolitaire.js b/xtrn/3rdp-install/jns-freecellsolitaire.js
new file mode 100644
index 0000000000000000000000000000000000000000..f1179776e56d1ad54f2f83c62b2efe1987db2777
--- /dev/null
+++ b/xtrn/3rdp-install/jns-freecellsolitaire.js
@@ -0,0 +1,92 @@
+"use strict";
+
+writeln("Generating config files...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "FREECELL.CFG";
+var nodedat = gamedir + "NODEINFO.DAT";
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();
+} else {
+	// new config
+	lines[2] = 'Y';
+	lines[3] = 'NOLOG';
+	lines[6] = gamedir + 'freecell.asc';
+	lines[7] = gamedir + 'freecell.ans';
+	lines[8] = "";
+	lines[11] = "2";
+	lines[12] = "2";
+	lines[13] = "2";
+}
+
+lines[0] = system.name;
+lines[1] = system.operator;
+
+var mfile = new File(gamedir + "FREECELL.KEY");
+if(mfile.open("rb")) {
+	var md5 = mfile.md5_hex;
+	writeln("Check key " + md5);
+	if (md5 == "93b6cbd1e08b4a5a451b6584f4fe114b") {
+		writeln("Restoring free key sysop info");
+		lines[0] = "BBSFILES.COM";
+		lines[1] = "FREE COPY";
+	}
+	mfile.close();
+}
+
+lines[4] = "Synchronet";
+lines[5] = "DOOR.SYS";
+lines[9] = "PCBNODE";
+lines[10] = gamedir;
+
+if (file.open(configfile, file_exists(configfile) ? 'r+' : 'w+')) {
+	file.writeAll(lines);
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+writeln("Beginning node config generation...");
+
+file_backup(nodedat);
+
+var nodedata = "";
+
+for(i = 0; i < 250; i++) {
+	var nodenum = i + 1;
+	
+	// you can use COM1 for every node
+	var nodeentry = (typeof system.node_list[i] !== "undefined") ?
+		"03F8" : "0000"
+	nodeentry = nodeentry + "0" + " ";
+	if (typeof system.node_list[i] !== "undefined") {
+		nodeentry = nodeentry + system.node_list[i].dir;
+	}
+	while (nodeentry.length < 46) {
+		nodeentry = nodeentry + " ";
+	}
+	
+	nodedata = nodedata + nodeentry;
+}
+
+var file = new File(nodedat);
+if (!file.open("w+")) {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+file.write(nodedata);
+file.close();
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/jns-golfsolitaire.ini b/xtrn/3rdp-install/jns-golfsolitaire.ini
new file mode 100644
index 0000000000000000000000000000000000000000..8abf1c1fd3b54dcd35e66a7445f50702fe157f60
--- /dev/null
+++ b/xtrn/3rdp-install/jns-golfsolitaire.ini
@@ -0,0 +1,22 @@
+Name: Golf Solitaire
+Desc: This is a solitaire card game which features Tournament Play for a very competitive game.
+By:   Rusty Johnson,JNS Software
+Cats: Games
+Subs: Cards,Board
+exe: GOLF-ST.EXE
+
+[md5:7f6ee77b01d518064315beefd78e677b]
+ver = 4.5
+url = http://bbsfiles.com/ftp/JNS/jns_gs45.zip
+
+[prog:golfso]
+name = Golf Solitaire
+cmd = golf-st /fossil
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jns-golfsolitaire.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jns-golfsolitaire.js b/xtrn/3rdp-install/jns-golfsolitaire.js
new file mode 100644
index 0000000000000000000000000000000000000000..8b1c162c96e183f5c20fab4a1ce6ff28882728ae
--- /dev/null
+++ b/xtrn/3rdp-install/jns-golfsolitaire.js
@@ -0,0 +1,93 @@
+"use strict";
+
+writeln("Generating config files...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "GOLF-ST.CFG";
+var nodedat = gamedir + "NODEINFO.DAT";
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();
+} else {
+	// new config
+	lines[2] = 'Y';
+	lines[3] = 'NOLOG';
+	lines[6] = gamedir + 'golfso.asc';
+	lines[7] = gamedir + 'golfso.ans';
+	lines[8] = "";
+	lines[11] = "3";
+	lines[12] = "2";
+	lines[13] = "25";
+	lines[14] = "y";
+}
+
+lines[0] = system.name;
+lines[1] = system.operator;
+
+var mfile = new File(gamedir + "GOLF-ST.KEY");
+if(mfile.open("rb")) {
+	var md5 = mfile.md5_hex;
+	writeln("Check key " + md5);
+	if (md5 == "a48fa3fa696ca1027022a28e0f4d5302") {
+		writeln("Restoring free key sysop info");
+		lines[0] = "BBSFILES.COM";
+		lines[1] = "FREE COPY";
+	}
+	mfile.close();
+}
+
+lines[4] = "Synchronet";
+lines[5] = "DOOR.SYS";
+lines[9] = "PCBNODE";
+lines[10] = nodedat;
+
+if (file.open(configfile, file_exists(configfile) ? 'r+' : 'w+')) {
+	file.writeAll(lines);
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+writeln("Beginning node config generation...");
+
+file_backup(nodedat);
+
+var nodedata = "";
+
+for(i = 0; i < 250; i++) {
+	var nodenum = i + 1;
+	
+	// you can use COM1 for every node
+	var nodeentry = (typeof system.node_list[i] !== "undefined") ?
+		"03F8" : "0000"
+	nodeentry = nodeentry + "0" + " ";
+	if (typeof system.node_list[i] !== "undefined") {
+		nodeentry = nodeentry + system.node_list[i].dir;
+	}
+	while (nodeentry.length < 46) {
+		nodeentry = nodeentry + " ";
+	}
+	
+	nodedata = nodedata + nodeentry;
+}
+
+var file = new File(nodedat);
+if (!file.open("w+")) {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+file.write(nodedata);
+file.close();
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/jns-greatwhitecard.ini b/xtrn/3rdp-install/jns-greatwhitecard.ini
new file mode 100644
index 0000000000000000000000000000000000000000..78dbfff28cfb1fc9f181d73be72bc29b8e1c95e3
--- /dev/null
+++ b/xtrn/3rdp-install/jns-greatwhitecard.ini
@@ -0,0 +1,22 @@
+Name: Great White Card
+Desc: A nice poker game where the user plays against the dealing computer
+By:   Rusty Johnson,JNS Software
+Cats: Games
+Subs: Cards,Poker,Guppy
+exe: GREATWHT.EXE
+
+[md5:4c82404c52b53e5fd674c07b5ace83a6]
+ver = 4.15
+url = http://bbsfiles.com/ftp/JNS/jnsgw415.zip
+
+[prog:greatwht]
+name = Great White Card
+cmd = greatwht.exe FOSSIL ALIAS
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jns-greatwhitecard.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jns-greatwhitecard.js b/xtrn/3rdp-install/jns-greatwhitecard.js
new file mode 100644
index 0000000000000000000000000000000000000000..93d720260631a0bc48d5156d1a65014ad873ca41
--- /dev/null
+++ b/xtrn/3rdp-install/jns-greatwhitecard.js
@@ -0,0 +1,91 @@
+"use strict";
+
+writeln("Generating config files...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "GREATWHT.CFG";
+var nodedat = gamedir + "DOORINFO.DAT";
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();
+} else {
+	// new config
+	lines[2] = 'Y';
+	lines[3] = 'NOLOG';
+	lines[6] = gamedir + 'greatwht.asc';
+	lines[7] = gamedir + 'greatwht.ans';
+	lines[8] = "";
+	lines[11] = "2";
+	lines[12] = "27";
+}
+
+lines[0] = system.name;
+lines[1] = system.operator;
+
+var mfile = new File(gamedir + "GREATWHT.KEY");
+if(mfile.open("rb")) {
+	var md5 = mfile.md5_hex;
+	writeln("Check key " + md5);
+	if (md5 == "018309ccab819725ee32023864c04b19") {
+		writeln("Restoring free key sysop info");
+		lines[0] = "BBSFILES.COM";
+		lines[1] = "FREE COPY";
+	}
+	mfile.close();
+}
+
+lines[4] = "Synchronet";
+lines[5] = "DOOR.SYS";
+lines[9] = "PCBNODE";
+lines[10] = gamedir;
+
+if (file.open(configfile, file_exists(configfile) ? 'r+' : 'w+')) {
+	file.writeAll(lines);
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+writeln("Beginning node config generation...");
+
+file_backup(nodedat);
+
+var nodedata = "";
+
+for(i = 0; i < 250; i++) {
+	var nodenum = i + 1;
+	
+	// you can use COM1 for every node
+	var nodeentry = (typeof system.node_list[i] !== "undefined") ?
+		"03F8" : "0000"
+	nodeentry = nodeentry + "0" + " ";
+	if (typeof system.node_list[i] !== "undefined") {
+		nodeentry = nodeentry + system.node_list[i].dir;
+	}
+	while (nodeentry.length < 46) {
+		nodeentry = nodeentry + " ";
+	}
+	
+	nodedata = nodedata + nodeentry;
+}
+
+var file = new File(nodedat);
+if (!file.open("w+")) {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+file.write(nodedata);
+file.close();
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/jns-idiotsdelight.ini b/xtrn/3rdp-install/jns-idiotsdelight.ini
new file mode 100644
index 0000000000000000000000000000000000000000..a5769a821fe490d47136d3ea8fd6a0b1a66f4972
--- /dev/null
+++ b/xtrn/3rdp-install/jns-idiotsdelight.ini
@@ -0,0 +1,22 @@
+Name: Idiot's Delight
+Desc: You can't be an idiot if you want to win this solitaire card game.
+By:   Rusty Johnson,JNS Software
+Cats: Games
+Subs: Cards,Solitaire
+exe: IDELIGHT.EXE
+
+[md5:102b33f0e238d2ecd7a212dc4b7e07f2]
+ver = 1.5
+url = http://bbsfiles.com/ftp/JNS/jns_id15.zip
+
+[prog:idiot]
+name = Idiot's Delight
+cmd = idelight /fossil
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jns-idiotsdelight.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jns-idiotsdelight.js b/xtrn/3rdp-install/jns-idiotsdelight.js
new file mode 100644
index 0000000000000000000000000000000000000000..82d2a02d54d337b24a2256086fa773af3327fbc1
--- /dev/null
+++ b/xtrn/3rdp-install/jns-idiotsdelight.js
@@ -0,0 +1,92 @@
+"use strict";
+
+writeln("Generating config files...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "IDELIGHT.CFG";
+var nodedat = gamedir + "NODEINFO.DAT";
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();
+} else {
+	// new config
+	lines[2] = 'Y';
+	lines[3] = 'NOLOG';
+	lines[6] = gamedir + 'idiot.asc';
+	lines[7] = gamedir + 'idiot.ans';
+	lines[8] = "";
+	lines[11] = "3";
+	lines[12] = "5";
+	lines[13] = "25";
+}
+
+lines[0] = system.name;
+lines[1] = system.operator;
+
+var mfile = new File(gamedir + "IDELIGHT.KEY");
+if(mfile.open("rb")) {
+	var md5 = mfile.md5_hex;
+	writeln("Check key " + md5);
+	if (md5 == "28fc004f23a6af5ff9463dd3ee496943") {
+		writeln("Restoring free key sysop info");
+		lines[0] = "BBSFILES.COM";
+		lines[1] = "FREE COPY";
+	}
+	mfile.close();
+}
+
+lines[4] = "Synchronet";
+lines[5] = "DOOR.SYS";
+lines[9] = "PCBNODE";
+lines[10] = nodedat;
+
+if (file.open(configfile, file_exists(configfile) ? 'r+' : 'w+')) {
+	file.writeAll(lines);
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+writeln("Beginning node config generation...");
+
+file_backup(nodedat);
+
+var nodedata = "";
+
+for(i = 0; i < 250; i++) {
+	var nodenum = i + 1;
+	
+	// you can use COM1 for every node
+	var nodeentry = (typeof system.node_list[i] !== "undefined") ?
+		"03F8" : "0000"
+	nodeentry = nodeentry + "0" + " ";
+	if (typeof system.node_list[i] !== "undefined") {
+		nodeentry = nodeentry + system.node_list[i].dir;
+	}
+	while (nodeentry.length < 46) {
+		nodeentry = nodeentry + " ";
+	}
+	
+	nodedata = nodedata + nodeentry;
+}
+
+var file = new File(nodedat);
+if (!file.open("w+")) {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+file.write(nodedata);
+file.close();
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/jns-inbetween.ini b/xtrn/3rdp-install/jns-inbetween.ini
new file mode 100644
index 0000000000000000000000000000000000000000..d9ad001c19a578aaba2b482d97d89c1f4c90e8da
--- /dev/null
+++ b/xtrn/3rdp-install/jns-inbetween.ini
@@ -0,0 +1,22 @@
+Name: In Between
+Desc: This is a simple but yet a very aggravating card high/low card game.
+By:   Rusty Johnson,JNS Software
+Cats: Games
+Subs: Cards,Chance
+exe: NBETWEEN.EXE
+
+[md5:1109bcd4ff4f7da651e5bbca4e6c1d25]
+ver = 5.0
+url = http://bbsfiles.com/ftp/JNS/jns_gs45.zip
+
+[prog:jnsib]
+name = In Between
+cmd = nbetween /fossil
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jns-inbetween.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jns-inbetween.js b/xtrn/3rdp-install/jns-inbetween.js
new file mode 100644
index 0000000000000000000000000000000000000000..b8cd79c0e3319fdea433924fca2dad6f614e71c5
--- /dev/null
+++ b/xtrn/3rdp-install/jns-inbetween.js
@@ -0,0 +1,92 @@
+"use strict";
+
+writeln("Generating config files...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "NBETWEEN.CFG";
+var nodedat = gamedir + "NODEINFO.DAT";
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();
+} else {
+	// new config
+	lines[2] = 'Y';
+	lines[3] = 'NOLOG';
+	lines[6] = gamedir + 'ib.asc';
+	lines[7] = gamedir + 'ib.ans';
+	lines[8] = "";
+	lines[11] = "30";
+	lines[12] = "2";
+	lines[13] = "25";
+}
+
+lines[0] = system.name;
+lines[1] = system.operator;
+
+var mfile = new File(gamedir + "NBETWEEN.KEY");
+if(mfile.open("rb")) {
+	var md5 = mfile.md5_hex;
+	writeln("Check key " + md5);
+	if (md5 == "6f3c52afb709ba2dcc38b1e7ac205bdf") {
+		writeln("Restoring free key sysop info");
+		lines[0] = "BBSFILES.COM";
+		lines[1] = "FREE COPY";
+	}
+	mfile.close();
+}
+
+lines[4] = "Synchronet";
+lines[5] = "DOOR.SYS";
+lines[9] = "PCBNODE";
+lines[10] = nodedat;
+
+if (file.open(configfile, file_exists(configfile) ? 'r+' : 'w+')) {
+	file.writeAll(lines);
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+writeln("Beginning node config generation...");
+
+file_backup(nodedat);
+
+var nodedata = "";
+
+for(i = 0; i < 250; i++) {
+	var nodenum = i + 1;
+	
+	// you can use COM1 for every node
+	var nodeentry = (typeof system.node_list[i] !== "undefined") ?
+		"03F8" : "0000"
+	nodeentry = nodeentry + "0" + " ";
+	if (typeof system.node_list[i] !== "undefined") {
+		nodeentry = nodeentry + system.node_list[i].dir;
+	}
+	while (nodeentry.length < 46) {
+		nodeentry = nodeentry + " ";
+	}
+	
+	nodedata = nodedata + nodeentry;
+}
+
+var file = new File(nodedat);
+if (!file.open("w+")) {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+file.write(nodedata);
+file.close();
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/jns-kingslast.ini b/xtrn/3rdp-install/jns-kingslast.ini
new file mode 100644
index 0000000000000000000000000000000000000000..f354e4be743201477cfd3f885835f19f84ca73b7
--- /dev/null
+++ b/xtrn/3rdp-install/jns-kingslast.ini
@@ -0,0 +1,22 @@
+Name: Kings Last
+Desc: A pure luck Solitaire game
+By:   Rusty Johnson,JNS Software
+Cats: Games
+Subs: Cards,Solitaire
+exe: KINGLAST.EXE
+
+[md5:0ef36c63f3076125b16e245a710e53e8]
+ver = 2.15
+url = http://bbsfiles.com/ftp/JNS/jnskl215.zip
+
+[prog:kinglast]
+name = Kings Last
+cmd = kinglast.exe FOSSIL ALIAS
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jns-kingslast.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jns-kingslast.js b/xtrn/3rdp-install/jns-kingslast.js
new file mode 100644
index 0000000000000000000000000000000000000000..877c9d53f801175abf910f79b182d698c7a5ad93
--- /dev/null
+++ b/xtrn/3rdp-install/jns-kingslast.js
@@ -0,0 +1,92 @@
+"use strict";
+
+writeln("Generating config files...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "KINGLAST.CFG";
+var nodedat = gamedir + "DOORINFO.DAT";
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();
+} else {
+	// new config
+	lines[2] = 'Y';
+	lines[3] = 'NOLOG';
+	lines[6] = gamedir + 'kinglast.asc';
+	lines[7] = gamedir + 'kinglast.ans';
+	lines[8] = "";
+	lines[11] = "3";
+	lines[12] = "2";
+	lines[13] = "27";
+}
+
+lines[0] = system.name;
+lines[1] = system.operator;
+
+var mfile = new File(gamedir + "KINGLAST.KEY");
+if(mfile.open("rb")) {
+	var md5 = mfile.md5_hex;
+	writeln("Check key " + md5);
+	if (md5 == "04c79e9d62bfcb5611e3b096f8301825") {
+		writeln("Restoring free key sysop info");
+		lines[0] = "BBSFILES.COM";
+		lines[1] = "FREE COPY";
+	}
+	mfile.close();
+}
+
+lines[4] = "Synchronet";
+lines[5] = "DOOR.SYS";
+lines[9] = "PCBNODE";
+lines[10] = gamedir;
+
+if (file.open(configfile, file_exists(configfile) ? 'r+' : 'w+')) {
+	file.writeAll(lines);
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+writeln("Beginning node config generation...");
+
+file_backup(nodedat);
+
+var nodedata = "";
+
+for(i = 0; i < 250; i++) {
+	var nodenum = i + 1;
+	
+	// you can use COM1 for every node
+	var nodeentry = (typeof system.node_list[i] !== "undefined") ?
+		"03F8" : "0000"
+	nodeentry = nodeentry + "0" + " ";
+	if (typeof system.node_list[i] !== "undefined") {
+		nodeentry = nodeentry + system.node_list[i].dir;
+	}
+	while (nodeentry.length < 46) {
+		nodeentry = nodeentry + " ";
+	}
+	
+	nodedata = nodedata + nodeentry;
+}
+
+var file = new File(nodedat);
+if (!file.open("w+")) {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+file.write(nodedata);
+file.close();
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/jns-matchtwo.ini b/xtrn/3rdp-install/jns-matchtwo.ini
new file mode 100644
index 0000000000000000000000000000000000000000..f6b3e7d98d49be9de453d4071851da65f94234e5
--- /dev/null
+++ b/xtrn/3rdp-install/jns-matchtwo.ini
@@ -0,0 +1,22 @@
+Name: Match Two
+Desc: A poker style card game in which you bet on matching cards
+By:   Rusty Johnson,JNS Software
+Cats: Games
+Subs: Chance,Cards,Poker
+exe: MATCHTWO.EXE
+
+[md5:7e85ffd3376ebdd80de861c2ff796b04]
+ver = 1.25
+url = http://bbsfiles.com/ftp/JNS/jnsmt125.zip
+
+[prog:jnsmatch]
+name = Match Two
+cmd = matchtwo.exe FOSSIL ALIAS
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jns-matchtwo.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jns-matchtwo.js b/xtrn/3rdp-install/jns-matchtwo.js
new file mode 100644
index 0000000000000000000000000000000000000000..37f121586559d927920778384467dcd10d84a8f4
--- /dev/null
+++ b/xtrn/3rdp-install/jns-matchtwo.js
@@ -0,0 +1,92 @@
+"use strict";
+
+writeln("Generating config files...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "MATCHTWO.CFG";
+var nodedat = gamedir + "DOORINFO.DAT";
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();
+} else {
+	// new config
+	lines[2] = 'Y';
+	lines[3] = 'NOLOG';
+	lines[6] = gamedir + 'matchtwo.asc';
+	lines[7] = gamedir + 'matchtwo.ans';
+	lines[8] = "";
+	lines[11] = "10";
+	lines[12] = "2";
+	lines[13] = "27";
+}
+
+lines[0] = system.name;
+lines[1] = system.operator;
+
+var mfile = new File(gamedir + "MATCHTWO.KEY");
+if(mfile.open("rb")) {
+	var md5 = mfile.md5_hex;
+	writeln("Check key " + md5);
+	if (md5 == "884da3f991e83f411d36f66117bdb990") {
+		writeln("Restoring free key sysop info");
+		lines[0] = "BBSFILES.COM";
+		lines[1] = "FREE COPY";
+	}
+	mfile.close();
+}
+
+lines[4] = "Synchronet";
+lines[5] = "DOOR.SYS";
+lines[9] = "PCBNODE";
+lines[10] = gamedir;
+
+if (file.open(configfile, file_exists(configfile) ? 'r+' : 'w+')) {
+	file.writeAll(lines);
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+writeln("Beginning node config generation...");
+
+file_backup(nodedat);
+
+var nodedata = "";
+
+for(i = 0; i < 250; i++) {
+	var nodenum = i + 1;
+	
+	// you can use COM1 for every node
+	var nodeentry = (typeof system.node_list[i] !== "undefined") ?
+		"03F8" : "0000"
+	nodeentry = nodeentry + "0" + " ";
+	if (typeof system.node_list[i] !== "undefined") {
+		nodeentry = nodeentry + system.node_list[i].dir;
+	}
+	while (nodeentry.length < 46) {
+		nodeentry = nodeentry + " ";
+	}
+	
+	nodedata = nodedata + nodeentry;
+}
+
+var file = new File(nodedat);
+if (!file.open("w+")) {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+file.write(nodedata);
+file.close();
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/jns-montanasolitaire.ini b/xtrn/3rdp-install/jns-montanasolitaire.ini
new file mode 100644
index 0000000000000000000000000000000000000000..6ff80ba5590017bdd3428edec80d8f423f8ee02c
--- /dev/null
+++ b/xtrn/3rdp-install/jns-montanasolitaire.ini
@@ -0,0 +1,22 @@
+Name: Montana Solitaire
+Desc: Solitaire Card game thats easy to play but hard to beat.
+By:   Rusty Johnson,JNS Software
+Cats: Games
+Subs: Cards,Solitaire
+exe: MONTANA.EXE
+
+[md5:ca3c73b21830e1f05d5e190ef84c641f]
+ver = 1.5
+url = http://bbsfiles.com/ftp/JNS/jns_ms11.zip
+
+[prog:montana]
+name = Montana Solitaire
+cmd = montana /fossil
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jns-montanasolitaire.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jns-montanasolitaire.js b/xtrn/3rdp-install/jns-montanasolitaire.js
new file mode 100644
index 0000000000000000000000000000000000000000..c865b0bfc614045cdcb37c04a4e70a816a7dd628
--- /dev/null
+++ b/xtrn/3rdp-install/jns-montanasolitaire.js
@@ -0,0 +1,92 @@
+"use strict";
+
+writeln("Generating config files...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "MONTANA.CFG";
+var nodedat = gamedir + "NODEINFO.DAT";
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();
+} else {
+	// new config
+	lines[2] = 'Y';
+	lines[3] = 'NOLOG';
+	lines[6] = gamedir + 'montana.asc';
+	lines[7] = gamedir + 'montana.ans';
+	lines[8] = "";
+	lines[11] = "3";
+	lines[12] = "5";
+	lines[13] = "25";
+}
+
+lines[0] = system.name;
+lines[1] = system.operator;
+
+var mfile = new File(gamedir + "MONTANA.KEY");
+if(mfile.open("rb")) {
+	var md5 = mfile.md5_hex;
+	writeln("Check key " + md5);
+	if (md5 == "7dc69db0330c6396fdcf18b67bb83f65") {
+		writeln("Restoring free key sysop info");
+		lines[0] = "BBSFILES.COM";
+		lines[1] = "FREE COPY";
+	}
+	mfile.close();
+}
+
+lines[4] = "Synchronet";
+lines[5] = "DOOR.SYS";
+lines[9] = "PCBNODE";
+lines[10] = nodedat;
+
+if (file.open(configfile, file_exists(configfile) ? 'r+' : 'w+')) {
+	file.writeAll(lines);
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+writeln("Beginning node config generation...");
+
+file_backup(nodedat);
+
+var nodedata = "";
+
+for(i = 0; i < 250; i++) {
+	var nodenum = i + 1;
+	
+	// you can use COM1 for every node
+	var nodeentry = (typeof system.node_list[i] !== "undefined") ?
+		"03F8" : "0000"
+	nodeentry = nodeentry + "0" + " ";
+	if (typeof system.node_list[i] !== "undefined") {
+		nodeentry = nodeentry + system.node_list[i].dir;
+	}
+	while (nodeentry.length < 46) {
+		nodeentry = nodeentry + " ";
+	}
+	
+	nodedata = nodedata + nodeentry;
+}
+
+var file = new File(nodedat);
+if (!file.open("w+")) {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+file.write(nodedata);
+file.close();
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/jns-ontheedge.ini b/xtrn/3rdp-install/jns-ontheedge.ini
new file mode 100644
index 0000000000000000000000000000000000000000..ded1b2a2dfd490d9bb4dc3c83b495791420cff25
--- /dev/null
+++ b/xtrn/3rdp-install/jns-ontheedge.ini
@@ -0,0 +1,22 @@
+Name: On the Edge
+Desc: This is a solitaire style card game which features Tournament play
+By:   Rusty Johnson,JNS Software
+Cats: Games
+Subs: Cards,Solitaire
+exe: ONTHEDGE.EXE
+
+[md5:9d0b37bd668af8e3650176c9deb14736]
+ver = 1.45
+url = http://bbsfiles.com/ftp/JNS/jnsoe145.zip
+
+[prog:onedge]
+name = On the Edge
+cmd = onthedge.exe FOSSIL ALIAS
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jns-ontheedge.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jns-ontheedge.js b/xtrn/3rdp-install/jns-ontheedge.js
new file mode 100644
index 0000000000000000000000000000000000000000..2c421757458172aefc86d2c589e91c620665ed21
--- /dev/null
+++ b/xtrn/3rdp-install/jns-ontheedge.js
@@ -0,0 +1,92 @@
+"use strict";
+
+writeln("Generating config files...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "ONTHEDGE.CFG";
+var nodedat = gamedir + "DOORINFO.DAT";
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();
+} else {
+	// new config
+	lines[2] = 'Y';
+	lines[3] = 'NOLOG';
+	lines[6] = gamedir + 'onthedge.asc';
+	lines[7] = gamedir + 'onthedge.ans';
+	lines[8] = "";
+	lines[11] = "2";
+	lines[12] = "2";
+	lines[13] = "27";
+}
+
+lines[0] = system.name;
+lines[1] = system.operator;
+
+var mfile = new File(gamedir + "ONTHEDGE.KEY");
+if(mfile.open("rb")) {
+	var md5 = mfile.md5_hex;
+	writeln("Check key " + md5);
+	if (md5 == "ef394ffea735925aee06a6f03ea72cfd") {
+		writeln("Restoring free key sysop info");
+		lines[0] = "BBSFILES.COM";
+		lines[1] = "FREE COPY";
+	}
+	mfile.close();
+}
+
+lines[4] = "Synchronet";
+lines[5] = "DOOR.SYS";
+lines[9] = "PCBNODE";
+lines[10] = gamedir;
+
+if (file.open(configfile, file_exists(configfile) ? 'r+' : 'w+')) {
+	file.writeAll(lines);
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+writeln("Beginning node config generation...");
+
+file_backup(nodedat);
+
+var nodedata = "";
+
+for(i = 0; i < 250; i++) {
+	var nodenum = i + 1;
+	
+	// you can use COM1 for every node
+	var nodeentry = (typeof system.node_list[i] !== "undefined") ?
+		"03F8" : "0000"
+	nodeentry = nodeentry + "0" + " ";
+	if (typeof system.node_list[i] !== "undefined") {
+		nodeentry = nodeentry + system.node_list[i].dir;
+	}
+	while (nodeentry.length < 46) {
+		nodeentry = nodeentry + " ";
+	}
+	
+	nodedata = nodedata + nodeentry;
+}
+
+var file = new File(nodedat);
+if (!file.open("w+")) {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+file.write(nodedata);
+file.close();
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/jns-pressyourluck.ini b/xtrn/3rdp-install/jns-pressyourluck.ini
new file mode 100644
index 0000000000000000000000000000000000000000..03891c6c69be1a41ff61ae0bd680539c352ae8b9
--- /dev/null
+++ b/xtrn/3rdp-install/jns-pressyourluck.ini
@@ -0,0 +1,22 @@
+Name: Press Your Luck
+Desc: Pressing your luck on rolls of dice is the theme but you must know when to stop
+By:   Rusty Johnson,JNS Software
+Cats: Games
+Subs: Chance,Dice
+exe: PRESLUCK.EXE
+
+[md5:ac2470712f7cfc1772106c3860c11b1b]
+ver = 1.45
+url = http://bbsfiles.com/ftp/JNS/jnspl145.zip
+
+[prog:presluck]
+name = Press Your Luck
+cmd = presluck.exe FOSSIL ALIAS
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jns-pressyourluck.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jns-pressyourluck.js b/xtrn/3rdp-install/jns-pressyourluck.js
new file mode 100644
index 0000000000000000000000000000000000000000..97a0b1e8eeaedc8d30da0a81782066b0bd30938f
--- /dev/null
+++ b/xtrn/3rdp-install/jns-pressyourluck.js
@@ -0,0 +1,92 @@
+"use strict";
+
+writeln("Generating config files...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "PRESLUCK.CFG";
+var nodedat = gamedir + "DOORINFO.DAT";
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();
+} else {
+	// new config
+	lines[2] = 'Y';
+	lines[3] = 'NOLOG';
+	lines[6] = gamedir + 'presluck.asc';
+	lines[7] = gamedir + 'presluck.ans';
+	lines[8] = "";
+	lines[11] = "10";
+	lines[12] = "2";
+	lines[13] = "27";
+}
+
+lines[0] = system.name;
+lines[1] = system.operator;
+
+var mfile = new File(gamedir + "PRESLUCK.KEY");
+if(mfile.open("rb")) {
+	var md5 = mfile.md5_hex;
+	writeln("Check key " + md5);
+	if (md5 == "9e1d5776eaf008c545fc7e85c6129c35") {
+		writeln("Restoring free key sysop info");
+		lines[0] = "BBSFILES.COM";
+		lines[1] = "FREE COPY";
+	}
+	mfile.close();
+}
+
+lines[4] = "Synchronet";
+lines[5] = "DOOR.SYS";
+lines[9] = "PCBNODE";
+lines[10] = gamedir;
+
+if (file.open(configfile, file_exists(configfile) ? 'r+' : 'w+')) {
+	file.writeAll(lines);
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+writeln("Beginning node config generation...");
+
+file_backup(nodedat);
+
+var nodedata = "";
+
+for(i = 0; i < 250; i++) {
+	var nodenum = i + 1;
+	
+	// you can use COM1 for every node
+	var nodeentry = (typeof system.node_list[i] !== "undefined") ?
+		"03F8" : "0000"
+	nodeentry = nodeentry + "0" + " ";
+	if (typeof system.node_list[i] !== "undefined") {
+		nodeentry = nodeentry + system.node_list[i].dir;
+	}
+	while (nodeentry.length < 46) {
+		nodeentry = nodeentry + " ";
+	}
+	
+	nodedata = nodedata + nodeentry;
+}
+
+var file = new File(nodedat);
+if (!file.open("w+")) {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+file.write(nodedata);
+file.close();
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/jns-quotefall.ini b/xtrn/3rdp-install/jns-quotefall.ini
new file mode 100644
index 0000000000000000000000000000000000000000..97a17635bad51ed5004159ecb3a480cd1145c726
--- /dev/null
+++ b/xtrn/3rdp-install/jns-quotefall.ini
@@ -0,0 +1,22 @@
+Name: Quote Fall
+Desc: The player must unscramble mixed quotes by droping letters into boxes
+By:   Rusty Johnson,JNS Software
+Cats: Games
+Subs: Word
+exe: QUOTFALL.EXE
+
+[md5:bc6241d62303583491b42ad3a0787b18]
+ver = 4.25
+url = http://bbsfiles.com/ftp/JNS/jnsqf425.zip
+
+[prog:quotfall]
+name = Quote Fall
+cmd = quotfall.exe FOSSIL ALIAS
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jns-quotefall.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jns-quotefall.js b/xtrn/3rdp-install/jns-quotefall.js
new file mode 100644
index 0000000000000000000000000000000000000000..f6cf1e8fbf9365b5232e0d3cf1e47c537681d102
--- /dev/null
+++ b/xtrn/3rdp-install/jns-quotefall.js
@@ -0,0 +1,91 @@
+"use strict";
+
+writeln("Generating config files...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "QUOTFALL.CFG";
+var nodedat = gamedir + "DOORINFO.DAT";
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();
+} else {
+	// new config
+	lines[2] = 'Y';
+	lines[3] = 'NOLOG';
+	lines[6] = gamedir + 'quotfall.asc';
+	lines[7] = gamedir + 'quotfall.ans';
+	lines[8] = "";
+	lines[11] = "2";
+	lines[12] = "27";
+}
+
+lines[0] = system.name;
+lines[1] = system.operator;
+
+var mfile = new File(gamedir + "QUOTFALL.KEY");
+if(mfile.open("rb")) {
+	var md5 = mfile.md5_hex;
+	writeln("Check key " + md5);
+	if (md5 == "121ff59b1ff03a8d86db4ea96b05bb47") {
+		writeln("Restoring free key sysop info");
+		lines[0] = "BBSFILES.COM";
+		lines[1] = "FREE COPY";
+	}
+	mfile.close();
+}
+
+lines[4] = "Synchronet";
+lines[5] = "DOOR.SYS";
+lines[9] = "PCBNODE";
+lines[10] = gamedir;
+
+if (file.open(configfile, file_exists(configfile) ? 'r+' : 'w+')) {
+	file.writeAll(lines);
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+writeln("Beginning node config generation...");
+
+file_backup(nodedat);
+
+var nodedata = "";
+
+for(i = 0; i < 250; i++) {
+	var nodenum = i + 1;
+	
+	// you can use COM1 for every node
+	var nodeentry = (typeof system.node_list[i] !== "undefined") ?
+		"03F8" : "0000"
+	nodeentry = nodeentry + "0" + " ";
+	if (typeof system.node_list[i] !== "undefined") {
+		nodeentry = nodeentry + system.node_list[i].dir;
+	}
+	while (nodeentry.length < 46) {
+		nodeentry = nodeentry + " ";
+	}
+	
+	nodedata = nodedata + nodeentry;
+}
+
+var file = new File(nodedat);
+if (!file.open("w+")) {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+file.write(nodedata);
+file.close();
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/jns-slidersolitaire.ini b/xtrn/3rdp-install/jns-slidersolitaire.ini
new file mode 100644
index 0000000000000000000000000000000000000000..2ffcceabfd6faf5e1190c34fc42849bca9419e91
--- /dev/null
+++ b/xtrn/3rdp-install/jns-slidersolitaire.ini
@@ -0,0 +1,22 @@
+Name: Slider Solitaire
+Desc: Great Solitaire Card game where you try to make alignments of three of a kind.
+By:   Rusty Johnson,JNS Software
+Cats: Games
+Subs: Cards,Solitaire
+exe: SLIDER.EXE
+
+[md5:773de76ddfe29950a15d774d9c47eea0]
+ver = 1.0
+url = http://bbsfiles.com/ftp/JNS/jns_sl10.zip
+
+[prog:slider]
+name = Slider Solitaire
+cmd = slider.exe /fossil
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jns-slidersolitaire.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jns-slidersolitaire.js b/xtrn/3rdp-install/jns-slidersolitaire.js
new file mode 100644
index 0000000000000000000000000000000000000000..c73e4cd4df8ba037d48d2f1b01c12a7e81f37d63
--- /dev/null
+++ b/xtrn/3rdp-install/jns-slidersolitaire.js
@@ -0,0 +1,92 @@
+"use strict";
+
+writeln("Generating config files...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "SLIDER.CFG";
+var nodedat = gamedir + "NODEINFO.DAT";
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();
+} else {
+	// new config
+	lines[2] = 'Y';
+	lines[3] = 'NOLOG';
+	lines[6] = gamedir + 'slider.asc';
+	lines[7] = gamedir + 'slider.ans';
+	lines[8] = "";
+	lines[11] = "3";
+	lines[12] = "2";
+	lines[13] = "27";
+}
+
+lines[0] = system.name;
+lines[1] = system.operator;
+
+var mfile = new File(gamedir + "SLIDER.KEY");
+if(mfile.open("rb")) {
+	var md5 = mfile.md5_hex;
+	writeln("Check key " + md5);
+	if (md5 == "17013b199de01e4e7f3794404983e668") {
+		writeln("Restoring free key sysop info");
+		lines[0] = "BBSFILES.COM";
+		lines[1] = "FREE COPY";
+	}
+	mfile.close();
+}
+
+lines[4] = "Synchronet";
+lines[5] = "DOOR.SYS";
+lines[9] = "PCBNODE";
+lines[10] = gamedir;
+
+if (file.open(configfile, file_exists(configfile) ? 'r+' : 'w+')) {
+	file.writeAll(lines);
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+writeln("Beginning node config generation...");
+
+file_backup(nodedat);
+
+var nodedata = "";
+
+for(i = 0; i < 250; i++) {
+	var nodenum = i + 1;
+	
+	// you can use COM1 for every node
+	var nodeentry = (typeof system.node_list[i] !== "undefined") ?
+		"03F8" : "0000"
+	nodeentry = nodeentry + "0" + " ";
+	if (typeof system.node_list[i] !== "undefined") {
+		nodeentry = nodeentry + system.node_list[i].dir;
+	}
+	while (nodeentry.length < 46) {
+		nodeentry = nodeentry + " ";
+	}
+	
+	nodedata = nodedata + nodeentry;
+}
+
+var file = new File(nodedat);
+if (!file.open("w+")) {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+file.write(nodedata);
+file.close();
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/jns-solitree.ini b/xtrn/3rdp-install/jns-solitree.ini
new file mode 100644
index 0000000000000000000000000000000000000000..98818ee9f8784a3e4a75110f802c957401bb3063
--- /dev/null
+++ b/xtrn/3rdp-install/jns-solitree.ini
@@ -0,0 +1,22 @@
+Name: Solitree
+Desc: This is a solitaire card game which features Tournament & Standard Modes
+By:   Rusty Johnson,JNS Software
+Cats: Games
+Subs: Cards,Solitaire
+exe: SOLITREE.EXE
+
+[md5:1793f3abc4a4171330bab03c4d77e662]
+ver = 5.4
+url = http://bbsfiles.com/ftp/JNS/jns_st54.zip
+
+[prog:jnssolit]
+name = Solitree
+cmd = solitree.exe /fossil
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jns-solitree.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jns-solitree.js b/xtrn/3rdp-install/jns-solitree.js
new file mode 100644
index 0000000000000000000000000000000000000000..ea90cfb9feaf1fea1c8fa63a248bc16a98be8772
--- /dev/null
+++ b/xtrn/3rdp-install/jns-solitree.js
@@ -0,0 +1,93 @@
+"use strict";
+
+writeln("Generating config files...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "SOLITREE.CFG";
+var nodedat = gamedir + "NODEINFO.DAT";
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();
+} else {
+	// new config
+	lines[2] = 'Y';
+	lines[3] = 'NOLOG';
+	lines[6] = gamedir + 'solitree.asc';
+	lines[7] = gamedir + 'solitree.ans';
+	lines[8] = "";
+	lines[11] = "3";
+	lines[12] = "5";
+	lines[13] = "25";
+	lines[14] = "STANDARD";
+}
+
+lines[0] = system.name;
+lines[1] = system.operator;
+
+var mfile = new File(gamedir + "SOLITREE.KEY");
+if(mfile.open("rb")) {
+	var md5 = mfile.md5_hex;
+	writeln("Check key " + md5);
+	if (md5 == "9d6be13347ac6e48d4dfb2e1fb739e17") {
+		writeln("Restoring free key sysop info");
+		lines[0] = "BBSFILES.COM";
+		lines[1] = "FREE COPY";
+	}
+	mfile.close();
+}
+
+lines[4] = "Synchronet";
+lines[5] = "DOOR.SYS";
+lines[9] = "PCBNODE";
+lines[10] = nodedat;
+
+if (file.open(configfile, file_exists(configfile) ? 'r+' : 'w+')) {
+	file.writeAll(lines);
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+writeln("Beginning node config generation...");
+
+file_backup(nodedat);
+
+var nodedata = "";
+
+for(i = 0; i < 250; i++) {
+	var nodenum = i + 1;
+	
+	// you can use COM1 for every node
+	var nodeentry = (typeof system.node_list[i] !== "undefined") ?
+		"03F8" : "0000"
+	nodeentry = nodeentry + "0" + " ";
+	if (typeof system.node_list[i] !== "undefined") {
+		nodeentry = nodeentry + system.node_list[i].dir;
+	}
+	while (nodeentry.length < 46) {
+		nodeentry = nodeentry + " ";
+	}
+	
+	nodedata = nodedata + nodeentry;
+}
+
+var file = new File(nodedat);
+if (!file.open("w+")) {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+file.write(nodedata);
+file.close();
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/jns-strikesandspares.ini b/xtrn/3rdp-install/jns-strikesandspares.ini
new file mode 100644
index 0000000000000000000000000000000000000000..14352cda8fcacd545477eaae9b8a4586c080cb25
--- /dev/null
+++ b/xtrn/3rdp-install/jns-strikesandspares.ini
@@ -0,0 +1,22 @@
+Name: Strikes and Spares
+Desc: A bowling solitaire card game where users try to get the high series
+By:   Rusty Johnson,JNS Software
+Cats: Games
+Subs: Cards,Solitaire
+exe: STRIKES.EXE
+
+[md5:d8ad6bec65a08c1fdae0e7fde9649050]
+ver = 1.0
+url = http://bbsfiles.com/ftp/JNS/jns_ss10.zip
+
+[prog:strikes]
+name = Strikes and Spares
+cmd = strikes.exe /fossil
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jns-strikesandspares.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jns-strikesandspares.js b/xtrn/3rdp-install/jns-strikesandspares.js
new file mode 100644
index 0000000000000000000000000000000000000000..5b5eb3c9429d9d982069d0da40acfcbfed28e9fe
--- /dev/null
+++ b/xtrn/3rdp-install/jns-strikesandspares.js
@@ -0,0 +1,92 @@
+"use strict";
+
+writeln("Generating config files...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "STRIKES.CFG";
+var nodedat = gamedir + "NODEINFO.DAT";
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();
+} else {
+	// new config
+	lines[2] = 'Y';
+	lines[3] = 'NOLOG';
+	lines[6] = gamedir + 'strikes.asc';
+	lines[7] = gamedir + 'strikes.ans';
+	lines[8] = "";
+	lines[11] = "3";
+	lines[12] = "2";
+	lines[13] = "27";
+}
+
+lines[0] = system.name;
+lines[1] = system.operator;
+
+var mfile = new File(gamedir + "STRIKES.KEY");
+if(mfile.open("rb")) {
+	var md5 = mfile.md5_hex;
+	writeln("Check key " + md5);
+	if (md5 == "8381c1e5c682fa42c3d353416d419d5e") {
+		writeln("Restoring free key sysop info");
+		lines[0] = "BBSFILES.COM";
+		lines[1] = "FREE COPY";
+	}
+	mfile.close();
+}
+
+lines[4] = "Synchronet";
+lines[5] = "DOOR.SYS";
+lines[9] = "PCBNODE";
+lines[10] = gamedir;
+
+if (file.open(configfile, file_exists(configfile) ? 'r+' : 'w+')) {
+	file.writeAll(lines);
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+writeln("Beginning node config generation...");
+
+file_backup(nodedat);
+
+var nodedata = "";
+
+for(i = 0; i < 250; i++) {
+	var nodenum = i + 1;
+	
+	// you can use COM1 for every node
+	var nodeentry = (typeof system.node_list[i] !== "undefined") ?
+		"03F8" : "0000"
+	nodeentry = nodeentry + "0" + " ";
+	if (typeof system.node_list[i] !== "undefined") {
+		nodeentry = nodeentry + system.node_list[i].dir;
+	}
+	while (nodeentry.length < 46) {
+		nodeentry = nodeentry + " ";
+	}
+	
+	nodedata = nodedata + nodeentry;
+}
+
+var file = new File(nodedat);
+if (!file.open("w+")) {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+file.write(nodedata);
+file.close();
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/jns-theeliminator.js b/xtrn/3rdp-install/jns-theeliminator.js
new file mode 100644
index 0000000000000000000000000000000000000000..473ceeb2fb3d5ae6000ccfdd8c78c56267066c23
--- /dev/null
+++ b/xtrn/3rdp-install/jns-theeliminator.js
@@ -0,0 +1,92 @@
+"use strict";
+
+writeln("Generating config files...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "THELIMIN.CFG";
+var nodedat = gamedir + "NODEINFO.DAT";
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();
+} else {
+	// new config
+	lines[2] = 'Y';
+	lines[3] = 'NOLOG';
+	lines[6] = gamedir + 'thelimin.asc';
+	lines[7] = gamedir + 'thelimin.ans';
+	lines[8] = "";
+	lines[11] = "2";
+	lines[12] = "2";
+	lines[13] = "27";
+}
+
+lines[0] = system.name;
+lines[1] = system.operator;
+
+var mfile = new File(gamedir + "THELIMIN.KEY");
+if(mfile.open("rb")) {
+	var md5 = mfile.md5_hex;
+	writeln("Check key " + md5);
+	if (md5 == "e1dcfe2a94612cbec8afbd092b4f8b7c") {
+		writeln("Restoring free key sysop info");
+		lines[0] = "BBSFILES.COM";
+		lines[1] = "FREE COPY";
+	}
+	mfile.close();
+}
+
+lines[4] = "Synchronet";
+lines[5] = "DOOR.SYS";
+lines[9] = "PCBNODE";
+lines[10] = gamedir;
+
+if (file.open(configfile, file_exists(configfile) ? 'r+' : 'w+')) {
+	file.writeAll(lines);
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+writeln("Beginning node config generation...");
+
+file_backup(nodedat);
+
+var nodedata = "";
+
+for(i = 0; i < 250; i++) {
+	var nodenum = i + 1;
+	
+	// you can use COM1 for every node
+	var nodeentry = (typeof system.node_list[i] !== "undefined") ?
+		"03F8" : "0000"
+	nodeentry = nodeentry + "0" + " ";
+	if (typeof system.node_list[i] !== "undefined") {
+		nodeentry = nodeentry + system.node_list[i].dir;
+	}
+	while (nodeentry.length < 46) {
+		nodeentry = nodeentry + " ";
+	}
+	
+	nodedata = nodedata + nodeentry;
+}
+
+var file = new File(nodedat);
+if (!file.open("w+")) {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+file.write(nodedata);
+file.close();
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/jns-theelminator.ini b/xtrn/3rdp-install/jns-theelminator.ini
new file mode 100644
index 0000000000000000000000000000000000000000..23675881c6abca448035383d0969cd02ed25da7e
--- /dev/null
+++ b/xtrn/3rdp-install/jns-theelminator.ini
@@ -0,0 +1,22 @@
+Name: The Eliminator
+Desc: Freat Solitaire Card game where you try to remove cards using the least piles
+By:   Rusty Johnson,JNS Software
+Cats: Games
+Subs: Cards,Solitaire
+exe: THELIMIN.EXE
+
+[md5:f8736aad971cf425ad9be1ce1db38f62]
+ver = 1.0
+url = http://bbsfiles.com/ftp/JNS/jns_te10.zip
+
+[prog:elim]
+name = The Eliminator
+cmd = thelimin.exe /fossil
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jns-theeliminator.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jns-thepuzzlemachine.ini b/xtrn/3rdp-install/jns-thepuzzlemachine.ini
new file mode 100644
index 0000000000000000000000000000000000000000..7009841b5dd13217fde5504eb71e96c62cb1c438
--- /dev/null
+++ b/xtrn/3rdp-install/jns-thepuzzlemachine.ini
@@ -0,0 +1,22 @@
+Name: The Puzzle Machine
+Desc: Number puzzle that must be returned to it's 1 to 15 numbered sequence
+By:   Rusty Johnson,JNS Software
+Cats: Games
+Subs: Chance,Dice
+exe: PUZZLE.EXE
+
+[md5:968b125e14b79d690be51fd698c9100d]
+ver = 4.25
+url = http://bbsfiles.com/ftp/JNS/jnspm425.zip
+
+[prog:jnspuzz]
+name = The Puzzle Machine
+cmd = puzzle.exe FOSSIL ALIAS
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jns-thepuzzlemachine.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jns-thepuzzlemachine.js b/xtrn/3rdp-install/jns-thepuzzlemachine.js
new file mode 100644
index 0000000000000000000000000000000000000000..55688d99abce96e71a7ec61e8af99a4cf325fc99
--- /dev/null
+++ b/xtrn/3rdp-install/jns-thepuzzlemachine.js
@@ -0,0 +1,91 @@
+"use strict";
+
+writeln("Generating config files...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "PUZZLE.CFG";
+var nodedat = gamedir + "DOORINFO.DAT";
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();
+} else {
+	// new config
+	lines[2] = 'Y';
+	lines[3] = 'NOLOG';
+	lines[6] = gamedir + 'puzzle.asc';
+	lines[7] = gamedir + 'puzzle.ans';
+	lines[8] = "";
+	lines[11] = "2";
+	lines[12] = "27";
+}
+
+lines[0] = system.name;
+lines[1] = system.operator;
+
+var mfile = new File(gamedir + "PUZZLE.KEY");
+if(mfile.open("rb")) {
+	var md5 = mfile.md5_hex;
+	writeln("Check key " + md5);
+	if (md5 == "3ce6f831dea7f3f919a11d9d2b7e5dab") {
+		writeln("Restoring free key sysop info");
+		lines[0] = "BBSFILES.COM";
+		lines[1] = "FREE COPY";
+	}
+	mfile.close();
+}
+
+lines[4] = "Synchronet";
+lines[5] = "DOOR.SYS";
+lines[9] = "PCBNODE";
+lines[10] = gamedir;
+
+if (file.open(configfile, file_exists(configfile) ? 'r+' : 'w+')) {
+	file.writeAll(lines);
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+writeln("Beginning node config generation...");
+
+file_backup(nodedat);
+
+var nodedata = "";
+
+for(i = 0; i < 250; i++) {
+	var nodenum = i + 1;
+	
+	// you can use COM1 for every node
+	var nodeentry = (typeof system.node_list[i] !== "undefined") ?
+		"03F8" : "0000"
+	nodeentry = nodeentry + "0" + " ";
+	if (typeof system.node_list[i] !== "undefined") {
+		nodeentry = nodeentry + system.node_list[i].dir;
+	}
+	while (nodeentry.length < 46) {
+		nodeentry = nodeentry + " ";
+	}
+	
+	nodedata = nodedata + nodeentry;
+}
+
+var file = new File(nodedat);
+if (!file.open("w+")) {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+file.write(nodedata);
+file.close();
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/jns-threeshuffles.ini b/xtrn/3rdp-install/jns-threeshuffles.ini
new file mode 100644
index 0000000000000000000000000000000000000000..e39d7bf7cd2af0d7c58686e0d9332b0a72f8259c
--- /dev/null
+++ b/xtrn/3rdp-install/jns-threeshuffles.ini
@@ -0,0 +1,22 @@
+Name: Three Shuffles and a Draw
+Desc: Super Solitaire Card game that takes a lot of skill to remove cards from table
+By:   Rusty Johnson,JNS Software
+Cats: Games
+Subs: Cards,Solitaire
+exe: THREE.EXE
+
+[md5:9c11696a2d2d029782d9ed72ee7d3f51]
+ver = 1.2
+url = http://bbsfiles.com/ftp/JNS/jns_ts12.zip
+
+[prog:threesh]
+name = Three Shuffles and a Draw
+cmd = three.exe /fossil
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jns-threeshuffles.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jns-threeshuffles.js b/xtrn/3rdp-install/jns-threeshuffles.js
new file mode 100644
index 0000000000000000000000000000000000000000..50a474b54236a15c0a4c09d6cdc4d9f650368f85
--- /dev/null
+++ b/xtrn/3rdp-install/jns-threeshuffles.js
@@ -0,0 +1,93 @@
+"use strict";
+
+writeln("Generating config files...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "THREE.CFG";
+var nodedat = gamedir + "NODEINFO.DAT";
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();
+} else {
+	// new config
+	lines[2] = 'Y';
+	lines[3] = 'NOLOG';
+	lines[6] = gamedir + 'threesh.asc';
+	lines[7] = gamedir + 'threesh.ans';
+	lines[8] = "";
+	lines[11] = "3";
+	lines[12] = "5";
+	lines[13] = "25";
+	lines[14] = "7";
+}
+
+lines[0] = system.name;
+lines[1] = system.operator;
+
+var mfile = new File(gamedir + "THREE.KEY");
+if(mfile.open("rb")) {
+	var md5 = mfile.md5_hex;
+	writeln("Check key " + md5);
+	if (md5 == "d2fdf6ed54fd27de589ddfb6a6493735") {
+		writeln("Restoring free key sysop info");
+		lines[0] = "BBSFILES.COM";
+		lines[1] = "FREE COPY";
+	}
+	mfile.close();
+}
+
+lines[4] = "Synchronet";
+lines[5] = "DOOR.SYS";
+lines[9] = "PCBNODE";
+lines[10] = nodedat;
+
+if (file.open(configfile, file_exists(configfile) ? 'r+' : 'w+')) {
+	file.writeAll(lines);
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+writeln("Beginning node config generation...");
+
+file_backup(nodedat);
+
+var nodedata = "";
+
+for(i = 0; i < 250; i++) {
+	var nodenum = i + 1;
+	
+	// you can use COM1 for every node
+	var nodeentry = (typeof system.node_list[i] !== "undefined") ?
+		"03F8" : "0000"
+	nodeentry = nodeentry + "0" + " ";
+	if (typeof system.node_list[i] !== "undefined") {
+		nodeentry = nodeentry + system.node_list[i].dir;
+	}
+	while (nodeentry.length < 46) {
+		nodeentry = nodeentry + " ";
+	}
+	
+	nodedata = nodedata + nodeentry;
+}
+
+var file = new File(nodedat);
+if (!file.open("w+")) {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+file.write(nodedata);
+file.close();
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/jns-trapper.ini b/xtrn/3rdp-install/jns-trapper.ini
new file mode 100644
index 0000000000000000000000000000000000000000..8351b05588e441ab9199e6f0e6c85c2454f60a85
--- /dev/null
+++ b/xtrn/3rdp-install/jns-trapper.ini
@@ -0,0 +1,22 @@
+Name: Trapper
+Desc: A fun and challenging game where you try to trap the varment on a block
+By:   Rusty Johnson,JNS Software
+Cats: Games
+Subs: Arcade
+exe: TRAPPER.EXE
+
+[md5:093af2b439676b306f94bd22d84bdae7]
+ver = 2.15
+url = http://bbsfiles.com/ftp/JNS/jnstp215.zip
+
+[prog:trapper]
+name = Trapper
+cmd = trapper.exe FOSSIL ALIAS
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jns-trapper.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jns-trapper.js b/xtrn/3rdp-install/jns-trapper.js
new file mode 100644
index 0000000000000000000000000000000000000000..453e64f7536b8d438f97a5294c217bee26750fed
--- /dev/null
+++ b/xtrn/3rdp-install/jns-trapper.js
@@ -0,0 +1,91 @@
+"use strict";
+
+writeln("Generating config files...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "TRAPPER.CFG";
+var nodedat = gamedir + "DOORINFO.DAT";
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();
+} else {
+	// new config
+	lines[2] = 'Y';
+	lines[3] = 'NOLOG';
+	lines[6] = gamedir + 'trapper.asc';
+	lines[7] = gamedir + 'trapper.ans';
+	lines[8] = "";
+	lines[11] = "2";
+	lines[12] = "27";
+}
+
+lines[0] = system.name;
+lines[1] = system.operator;
+
+var mfile = new File(gamedir + "TRAPPER.KEY");
+if(mfile.open("rb")) {
+	var md5 = mfile.md5_hex;
+	writeln("Check key " + md5);
+	if (md5 == "f9bf0fde1a9e5f7d638941b5954fa8e3") {
+		writeln("Restoring free key sysop info");
+		lines[0] = "BBSFILES.COM";
+		lines[1] = "FREE COPY";
+	}
+	mfile.close();
+}
+
+lines[4] = "Synchronet";
+lines[5] = "DOOR.SYS";
+lines[9] = "PCBNODE";
+lines[10] = gamedir;
+
+if (file.open(configfile, file_exists(configfile) ? 'r+' : 'w+')) {
+	file.writeAll(lines);
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+writeln("Beginning node config generation...");
+
+file_backup(nodedat);
+
+var nodedata = "";
+
+for(i = 0; i < 250; i++) {
+	var nodenum = i + 1;
+	
+	// you can use COM1 for every node
+	var nodeentry = (typeof system.node_list[i] !== "undefined") ?
+		"03F8" : "0000"
+	nodeentry = nodeentry + "0" + " ";
+	if (typeof system.node_list[i] !== "undefined") {
+		nodeentry = nodeentry + system.node_list[i].dir;
+	}
+	while (nodeentry.length < 46) {
+		nodeentry = nodeentry + " ";
+	}
+	
+	nodedata = nodedata + nodeentry;
+}
+
+var file = new File(nodedat);
+if (!file.open("w+")) {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+file.write(nodedata);
+file.close();
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/jns-treasuretrove.ini b/xtrn/3rdp-install/jns-treasuretrove.ini
new file mode 100644
index 0000000000000000000000000000000000000000..80e248ece4c6cacff8259910bc0ca132cb9a2d1d
--- /dev/null
+++ b/xtrn/3rdp-install/jns-treasuretrove.ini
@@ -0,0 +1,22 @@
+Name: Treasure Trove
+Desc: This is a solitaire card game which features Tournament play
+By:   Rusty Johnson,JNS Software
+Cats: Games
+Subs: Cards,Solitaire
+exe: TREASURE.EXE
+
+[md5:7c5be2ef0e88516aa1254b58d49dcc1a]
+ver = 1.45
+url = http://bbsfiles.com/ftp/JNS/jnstt145.zip
+
+[prog:ttrove]
+name = Treasure Trove
+cmd = treasure.exe FOSSIL ALIAS
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jns-treasuretrove.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jns-treasuretrove.js b/xtrn/3rdp-install/jns-treasuretrove.js
new file mode 100644
index 0000000000000000000000000000000000000000..f3247cb0f91d75aa50b940d10a805cc224e714b5
--- /dev/null
+++ b/xtrn/3rdp-install/jns-treasuretrove.js
@@ -0,0 +1,92 @@
+"use strict";
+
+writeln("Generating config files...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "TREASURE.CFG";
+var nodedat = gamedir + "DOORINFO.DAT";
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();
+} else {
+	// new config
+	lines[2] = 'Y';
+	lines[3] = 'NOLOG';
+	lines[6] = gamedir + 'treasure.asc';
+	lines[7] = gamedir + 'treasure.ans';
+	lines[8] = "";
+	lines[11] = "2";
+	lines[12] = "2";
+	lines[13] = "27";
+}
+
+lines[0] = system.name;
+lines[1] = system.operator;
+
+var mfile = new File(gamedir + "TREASURE.KEY");
+if(mfile.open("rb")) {
+	var md5 = mfile.md5_hex;
+	writeln("Check key " + md5);
+	if (md5 == "e206d2074d1b91b9851d7a92e04d06f4") {
+		writeln("Restoring free key sysop info");
+		lines[0] = "BBSFILES.COM";
+		lines[1] = "FREE COPY";
+	}
+	mfile.close();
+}
+
+lines[4] = "Synchronet";
+lines[5] = "DOOR.SYS";
+lines[9] = "PCBNODE";
+lines[10] = gamedir;
+
+if (file.open(configfile, file_exists(configfile) ? 'r+' : 'w+')) {
+	file.writeAll(lines);
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+writeln("Beginning node config generation...");
+
+file_backup(nodedat);
+
+var nodedata = "";
+
+for(i = 0; i < 250; i++) {
+	var nodenum = i + 1;
+	
+	// you can use COM1 for every node
+	var nodeentry = (typeof system.node_list[i] !== "undefined") ?
+		"03F8" : "0000"
+	nodeentry = nodeentry + "0" + " ";
+	if (typeof system.node_list[i] !== "undefined") {
+		nodeentry = nodeentry + system.node_list[i].dir;
+	}
+	while (nodeentry.length < 46) {
+		nodeentry = nodeentry + " ";
+	}
+	
+	nodedata = nodedata + nodeentry;
+}
+
+var file = new File(nodedat);
+if (!file.open("w+")) {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+file.write(nodedata);
+file.close();
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/jns-tripeakssolitaire.ini b/xtrn/3rdp-install/jns-tripeakssolitaire.ini
new file mode 100644
index 0000000000000000000000000000000000000000..c5915cf883814e9b2c361eccefa5921535068cfd
--- /dev/null
+++ b/xtrn/3rdp-install/jns-tripeakssolitaire.ini
@@ -0,0 +1,22 @@
+Name: Tri Peaks Solitaire
+Desc: Solitaire Card game where its easy to lose points instead of winning them.
+By:   Rusty Johnson,JNS Software
+Cats: Games
+Subs: Cards,Solitaire
+exe: TRIPEAKS.EXE
+
+[md5:da06474583b983faeebdcd57113eaa90]
+ver = 1.1
+url = http://bbsfiles.com/ftp/JNS/jns_ps11.zip
+
+[prog:tripeaks]
+name = Tri Peaks Solitaire
+cmd = tripeaks /fossil
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jns-tripeakssolitaire.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jns-tripeakssolitaire.js b/xtrn/3rdp-install/jns-tripeakssolitaire.js
new file mode 100644
index 0000000000000000000000000000000000000000..436314269682188c14b201f7cee231c875307634
--- /dev/null
+++ b/xtrn/3rdp-install/jns-tripeakssolitaire.js
@@ -0,0 +1,92 @@
+"use strict";
+
+writeln("Generating config files...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "TRIPEAKS.CFG";
+var nodedat = gamedir + "NODEINFO.DAT";
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();
+} else {
+	// new config
+	lines[2] = 'Y';
+	lines[3] = 'NOLOG';
+	lines[6] = gamedir + 'tripeaks.asc';
+	lines[7] = gamedir + 'tripeaks.ans';
+	lines[8] = "";
+	lines[11] = "3";
+	lines[12] = "5";
+	lines[13] = "25";
+}
+
+lines[0] = system.name;
+lines[1] = system.operator;
+
+var mfile = new File(gamedir + "TRIPEAKS.KEY");
+if(mfile.open("rb")) {
+	var md5 = mfile.md5_hex;
+	writeln("Check key " + md5);
+	if (md5 == "6acd292bd87b40ca5bc9adb7f92b250e") {
+		writeln("Restoring free key sysop info");
+		lines[0] = "BBSFILES.COM";
+		lines[1] = "FREE COPY";
+	}
+	mfile.close();
+}
+
+lines[4] = "Synchronet";
+lines[5] = "DOOR.SYS";
+lines[9] = "PCBNODE";
+lines[10] = nodedat;
+
+if (file.open(configfile, file_exists(configfile) ? 'r+' : 'w+')) {
+	file.writeAll(lines);
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+writeln("Beginning node config generation...");
+
+file_backup(nodedat);
+
+var nodedata = "";
+
+for(i = 0; i < 250; i++) {
+	var nodenum = i + 1;
+	
+	// you can use COM1 for every node
+	var nodeentry = (typeof system.node_list[i] !== "undefined") ?
+		"03F8" : "0000"
+	nodeentry = nodeentry + "0" + " ";
+	if (typeof system.node_list[i] !== "undefined") {
+		nodeentry = nodeentry + system.node_list[i].dir;
+	}
+	while (nodeentry.length < 46) {
+		nodeentry = nodeentry + " ";
+	}
+	
+	nodedata = nodedata + nodeentry;
+}
+
+var file = new File(nodedat);
+if (!file.open("w+")) {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+file.write(nodedata);
+file.close();
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/jns-twocardpoker.ini b/xtrn/3rdp-install/jns-twocardpoker.ini
new file mode 100644
index 0000000000000000000000000000000000000000..bb246f033b358c0b2b4874a5ef93fe65d8fff45d
--- /dev/null
+++ b/xtrn/3rdp-install/jns-twocardpoker.ini
@@ -0,0 +1,22 @@
+Name: Two Card Poker
+Desc: Two card poker game where the player plays against 3 computer hands
+By:   Rusty Johnson,JNS Software
+Cats: Games
+Subs: Chance,Cards,Poker
+exe: TWO-CARD.EXE
+
+[md5:e864ed3ccb71bd730deb5ed37d0dec95]
+ver = 5.0
+url = http://bbsfiles.com/ftp/JNS/jns_tc50.zip
+
+[prog:twocard]
+name = Two Card Poker
+cmd = two-card.exe /fossil
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jns-twocardpoker.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jns-twocardpoker.js b/xtrn/3rdp-install/jns-twocardpoker.js
new file mode 100644
index 0000000000000000000000000000000000000000..c6555ad90be737e75086f12eb1d2719556bfe765
--- /dev/null
+++ b/xtrn/3rdp-install/jns-twocardpoker.js
@@ -0,0 +1,92 @@
+"use strict";
+
+writeln("Generating config files...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "TWO-CARD.CFG";
+var nodedat = gamedir + "NODEINFO.DAT";
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();
+} else {
+	// new config
+	lines[2] = 'Y';
+	lines[3] = 'NOLOG';
+	lines[6] = gamedir + 'twocard.asc';
+	lines[7] = gamedir + 'twocard.ans';
+	lines[8] = "";
+	lines[11] = "30";
+	lines[12] = "5";
+	lines[13] = "25";
+}
+
+lines[0] = system.name;
+lines[1] = system.operator;
+
+var mfile = new File(gamedir + "TWO-CARD.KEY");
+if(mfile.open("rb")) {
+	var md5 = mfile.md5_hex;
+	writeln("Check key " + md5);
+	if (md5 == "eb4cdfb3115ead18dba51d1d5428e73c") {
+		writeln("Restoring free key sysop info");
+		lines[0] = "BBSFILES.COM";
+		lines[1] = "FREE COPY";
+	}
+	mfile.close();
+}
+
+lines[4] = "Synchronet";
+lines[5] = "DOOR.SYS";
+lines[9] = "PCBNODE";
+lines[10] = nodedat;
+
+if (file.open(configfile, file_exists(configfile) ? 'r+' : 'w+')) {
+	file.writeAll(lines);
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+writeln("Beginning node config generation...");
+
+file_backup(nodedat);
+
+var nodedata = "";
+
+for(i = 0; i < 250; i++) {
+	var nodenum = i + 1;
+	
+	// you can use COM1 for every node
+	var nodeentry = (typeof system.node_list[i] !== "undefined") ?
+		"03F8" : "0000"
+	nodeentry = nodeentry + "0" + " ";
+	if (typeof system.node_list[i] !== "undefined") {
+		nodeentry = nodeentry + system.node_list[i].dir;
+	}
+	while (nodeentry.length < 46) {
+		nodeentry = nodeentry + " ";
+	}
+	
+	nodedata = nodedata + nodeentry;
+}
+
+var file = new File(nodedat);
+if (!file.open("w+")) {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+file.write(nodedata);
+file.close();
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/jns-yacht.ini b/xtrn/3rdp-install/jns-yacht.ini
new file mode 100644
index 0000000000000000000000000000000000000000..4a3484d9684807e4ec4ad8f3010e34d898784e5d
--- /dev/null
+++ b/xtrn/3rdp-install/jns-yacht.ini
@@ -0,0 +1,22 @@
+Name: Yacht (JNS)
+Desc: Nice Yacht Dice game that awards points for the high score of the day
+By:   Rusty Johnson,JNS Software
+Cats: Games
+Subs: Chance,Dice
+exe: YACHT.EXE
+
+[md5:62b55e386a16f214735d7606f405abe2]
+ver = 1.15
+url = http://bbsfiles.com/ftp/JNS/jnsyt115.zip
+
+[prog:jnsyacht]
+name = Yacht (JNS)
+cmd = yacht.exe FOSSIL ALIAS
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/jns-yacht.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/jns-yacht.js b/xtrn/3rdp-install/jns-yacht.js
new file mode 100644
index 0000000000000000000000000000000000000000..b6883145963200b004aaa640c29b7b6b30d0f553
--- /dev/null
+++ b/xtrn/3rdp-install/jns-yacht.js
@@ -0,0 +1,92 @@
+"use strict";
+
+writeln("Generating config files...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "YACHT.CFG";
+var nodedat = gamedir + "DOORINFO.DAT";
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();
+} else {
+	// new config
+	lines[2] = 'Y';
+	lines[3] = 'NOLOG';
+	lines[6] = gamedir + 'yacht.asc';
+	lines[7] = gamedir + 'yacht.ans';
+	lines[8] = "";
+	lines[11] = "2";
+	lines[12] = "2";
+	lines[13] = "27";
+}
+
+lines[0] = system.name;
+lines[1] = system.operator;
+
+var mfile = new File(gamedir + "YACHT.KEY");
+if(mfile.open("rb")) {
+	var md5 = mfile.md5_hex;
+	writeln("Check key " + md5);
+	if (md5 == "9188018c75b521bd9af8d8042de6da40") {
+		writeln("Restoring free key sysop info");
+		lines[0] = "BBSFILES.COM";
+		lines[1] = "FREE COPY";
+	}
+	mfile.close();
+}
+
+lines[4] = "Synchronet";
+lines[5] = "DOOR.SYS";
+lines[9] = "PCBNODE";
+lines[10] = gamedir;
+
+if (file.open(configfile, file_exists(configfile) ? 'r+' : 'w+')) {
+	file.writeAll(lines);
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+writeln("Beginning node config generation...");
+
+file_backup(nodedat);
+
+var nodedata = "";
+
+for(i = 0; i < 250; i++) {
+	var nodenum = i + 1;
+	
+	// you can use COM1 for every node
+	var nodeentry = (typeof system.node_list[i] !== "undefined") ?
+		"03F8" : "0000"
+	nodeentry = nodeentry + "0" + " ";
+	if (typeof system.node_list[i] !== "undefined") {
+		nodeentry = nodeentry + system.node_list[i].dir;
+	}
+	while (nodeentry.length < 46) {
+		nodeentry = nodeentry + " ";
+	}
+	
+	nodedata = nodedata + nodeentry;
+}
+
+var file = new File(nodedat);
+if (!file.open("w+")) {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+file.write(nodedata);
+file.close();
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/latenight-civilwar.ini b/xtrn/3rdp-install/latenight-civilwar.ini
new file mode 100644
index 0000000000000000000000000000000000000000..fc56ca7173c474a82d22f617d8255f08565a6f60
--- /dev/null
+++ b/xtrn/3rdp-install/latenight-civilwar.ini
@@ -0,0 +1,22 @@
+Name: Civil War
+Desc: See how well you can do with your troops in the continuing battle between North & South!
+By:   Marvin Davis,Late Nite Doors
+Cats: Games
+Subs: War
+exe: CIVILWAR.EXE
+
+[md5: ]
+ver = 5.a
+url = http://bbsfiles.com/ftp/LateNite/cvlwar5a.zip
+
+[prog:civilwar]
+name = Civil War
+cmd = civilwar.exe NODE%#.CFG
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/latenight-civilwar.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/latenight-civilwar.js b/xtrn/3rdp-install/latenight-civilwar.js
new file mode 100644
index 0000000000000000000000000000000000000000..d0215532f090f827a6c77a806cc2a8782c2fce2c
--- /dev/null
+++ b/xtrn/3rdp-install/latenight-civilwar.js
@@ -0,0 +1,60 @@
+"use strict";
+
+writeln("Generating config file...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "CIVILWAR.CFG";
+if (!file_exists(configfile)) {
+	if (file_exists(gamedir + "CVLWAR1.CFG")) {
+		configfile = gamedir + "CVLWAR1.CFG";
+	}
+}
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+if ((lines[0] != "BBSFiles.com") && (lines[0] != "GameMaster's Realm")) {
+	var op = system.operator.split(" ", 2);
+	lines[0] = system.name;
+	lines[1] = op[0];
+	lines[2] = op[1];
+}
+
+lines[4] = gamedir + "civilwar.asc";
+lines[5] = gamedir + "civilwar.ans";
+
+file.writeAll(lines);
+file.close();
+
+writeln("Beginning node config generation...");
+for(i = 0; i < system.nodes; i++) {
+	var nodenum = i + 1;
+	lines[3] = system.node_list[i].dir + "DOOR.SYS";
+	lines[11] = nodenum;
+	lines[12] = system.nodes;			
+	writeln("Creating " + gamedir + 'NODE' + nodenum + '.CFG');
+	
+	var file = new File(gamedir + 'NODE' + nodenum + '.CFG');
+	if (!file.open("w")) {
+		writeln("Error " + file.error + " opening " + file.name + " for writing");
+		exit(1)
+	}
+	file.writeAll(lines);
+	file.close();
+}
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/latenight-civilwar48.ini b/xtrn/3rdp-install/latenight-civilwar48.ini
new file mode 100644
index 0000000000000000000000000000000000000000..6d05f172e0e53dbf12a0aa2586ea128a9d7dba4b
--- /dev/null
+++ b/xtrn/3rdp-install/latenight-civilwar48.ini
@@ -0,0 +1,21 @@
+Name: Civil War
+Desc: See how well you can do with your troops in the continuing battle between North & South!
+By:   Marvin Davis,Late Nite Doors
+Cats: Games
+Subs: War
+exe: CIVILWAR.EXE
+
+[md5:3b6f286960e98e99a6098abd0604a0ad]
+ver = 4.8
+
+[prog:civilwar]
+name = Civil War
+cmd = civilwar.exe /GAP /NODE%#
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/latenight-civilwar48.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/latenight-civilwar48.js b/xtrn/3rdp-install/latenight-civilwar48.js
new file mode 100644
index 0000000000000000000000000000000000000000..dec9547e43c0ba1d32c3732271ddaab1ca27b83b
--- /dev/null
+++ b/xtrn/3rdp-install/latenight-civilwar48.js
@@ -0,0 +1,58 @@
+"use strict";
+
+writeln("Generating config file...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "CIVILWAR.CFG";
+if (!file_exists(configfile)) {
+	if (file_exists(gamedir + "CVLWAR1.CFG")) {
+		configfile = gamedir + "CVLWAR1.CFG";
+	}
+}
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r+')) {
+	lines = file.readAll();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+if ((lines[0] != "BBSFiles.com") && (lines[0] != "GameMaster's Realm")) {
+	var op = system.operator.split(" ", 2);
+	lines[0] = system.name;
+	lines[1] = op[0];
+	lines[2] = op[1];
+}
+
+lines[4] = gamedir + "civilwar.asc";
+lines[5] = gamedir + "civilwar.ans";
+
+file.writeAll(lines);
+file.close();
+
+writeln("Beginning node config generation...");
+for(i = 0; i < system.nodes; i++) {
+	var nodenum = i + 1;
+	lines[3] = system.node_list[i].dir + "DOOR.SYS";
+	
+	writeln("Creating " + gamedir + 'NODE' + nodenum + '.CFG');
+	
+	var file = new File(gamedir + 'NODE' + nodenum + '.CFG');
+	if (!file.open("w")) {
+		writeln("Error " + file.error + " opening " + file.name + " for writing");
+		exit(1)
+	}
+	file.writeAll(lines);
+	file.close();
+}
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/latenight-darkcastle.ini b/xtrn/3rdp-install/latenight-darkcastle.ini
new file mode 100644
index 0000000000000000000000000000000000000000..88f7fc4a6a7b2b1a6e8ef101909026d5c8330f49
--- /dev/null
+++ b/xtrn/3rdp-install/latenight-darkcastle.ini
@@ -0,0 +1,25 @@
+Name: Dark Castle
+Desc: Venture into the Dark Castle and retrieve the awesome prize inside
+By:   Marvin Davis,Late Nite Doors,Marty Blankenship
+Cats: Games
+Subs: Adventure
+exe: DCASTLE.EXE
+
+[md5:ecc8c652cce434d474c61a7d63c3ac16]
+ver = 8.5
+url = http://bbsfiles.com/ftp/LateNite/castle85.zip
+
+[md5:eeadc8a93e71e5b9152b9a90b760c765]
+ver = 7.7
+
+[prog:dcastle]
+name = Dark Castle
+cmd = dcastle.exe %f
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/latenight-darkcastle.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/latenight-darkcastle.js b/xtrn/3rdp-install/latenight-darkcastle.js
new file mode 100644
index 0000000000000000000000000000000000000000..0b04b24dd25f02a45dc026e78365958dea1678fa
--- /dev/null
+++ b/xtrn/3rdp-install/latenight-darkcastle.js
@@ -0,0 +1,36 @@
+"use strict";
+
+writeln("Generating config file...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "CASTLE.CFG";
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r+')) {
+	lines = file.readAll();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+if (lines[0] != "BBSFiles.com") {
+	var op = system.operator.split(" ", 2);
+	lines[0] = system.name;
+	lines[1] = op[0];
+	lines[2] = op[1];
+}
+
+lines[4] = gamedir + "castle.asc";
+lines[5] = gamedir + "castle.ans";
+
+file.writeAll(lines);
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/latenight-superrig.ini b/xtrn/3rdp-install/latenight-superrig.ini
new file mode 100644
index 0000000000000000000000000000000000000000..9f04a310c6fd069dcd2a4907a5d7734721a99ec5
--- /dev/null
+++ b/xtrn/3rdp-install/latenight-superrig.ini
@@ -0,0 +1,22 @@
+Name: Super Rig
+Desc: Includes a CB.Radio simulator and realistic highway actio
+By:   Marvin Davis,Late Nite Doors
+Cats: Games
+Subs: Simulation
+exe: SUPERIG.EXE
+
+[md5:d2cdbdb0349e99f985360a02677b8736]
+ver = 6.3
+url = http://bbsfiles.com/ftp/LateNite/suprig63.zip
+
+[prog:superrig]
+name = Super Rig
+cmd = superig.exe NODE%#.CFG
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/latenight-superrig.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/latenight-superrig.js b/xtrn/3rdp-install/latenight-superrig.js
new file mode 100644
index 0000000000000000000000000000000000000000..f8dbab151c88aa51c107471cdf1a94a95cbc3f56
--- /dev/null
+++ b/xtrn/3rdp-install/latenight-superrig.js
@@ -0,0 +1,55 @@
+"use strict";
+
+writeln("Generating config file...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "SUPERIG1.CFG";
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+if ((lines[0] != "BBSFiles.com") && (lines[0] != "GameMaster's Realm")) {
+	var op = system.operator.split(" ", 2);
+	lines[0] = system.name;
+	lines[1] = op[0];
+	lines[2] = op[1];
+}
+
+lines[4] = gamedir + "superig.asc";
+lines[5] = gamedir + "superig.ans";
+
+file.writeAll(lines);
+file.close();
+
+writeln("Beginning node config generation...");
+for(i = 0; i < system.nodes; i++) {
+	var nodenum = i + 1;
+	lines[3] = system.node_list[i].dir + "DOOR.SYS";
+	lines[11] = nodenum;
+	lines[12] = system.nodes;
+	writeln("Creating " + gamedir + 'NODE' + nodenum + '.CFG');
+	
+	var file = new File(gamedir + 'NODE' + nodenum + '.CFG');
+	if (!file.open("w")) {
+		writeln("Error " + file.error + " opening " + file.name + " for writing");
+		exit(1)
+	}
+	file.writeAll(lines);
+	file.close();
+}
+
+writeln("Config generation complete");
+
+exit(0);
diff --git a/xtrn/3rdp-install/latenight-triviamaster.ini b/xtrn/3rdp-install/latenight-triviamaster.ini
new file mode 100644
index 0000000000000000000000000000000000000000..9c13c24c5ad313a0a287cd74ee515cccb0a25685
--- /dev/null
+++ b/xtrn/3rdp-install/latenight-triviamaster.ini
@@ -0,0 +1,22 @@
+Name: Trivia Master
+Desc: Includes a free Rock'n Roll database with over 40 years of trivia for all ages
+By:   Marvin Davis,Late Nite Doors
+Cats: Games
+Subs: Trivia
+exe: TRIVIA.EXE
+
+[md5:a6c7ac71c739e4c5e1a049143c7b595f]
+ver = 5.2
+url = http://bbsfiles.com/ftp/LateNite/tmastr52.zip
+
+[prog:triviam]
+name = Trivia Master
+cmd = trivia.exe NODE%#.CFG
+ars = DOS
+execution_ars = NOT GUEST
+settings = XTRN_ANSI | XTRN_MULTIUSER | XTRN_SH
+type = XTRN_GAP
+
+[exec:../3rdp-install/latenight-triviamaster.js]
+prompt = false
+required = true
diff --git a/xtrn/3rdp-install/latenight-triviamaster.js b/xtrn/3rdp-install/latenight-triviamaster.js
new file mode 100644
index 0000000000000000000000000000000000000000..7fd6f49cc68fd266b91cb400c38d8ae12d25f510
--- /dev/null
+++ b/xtrn/3rdp-install/latenight-triviamaster.js
@@ -0,0 +1,53 @@
+"use strict";
+
+writeln("Generating config file...");
+
+var i;
+
+var gamedir = fullpath(js.startup_dir);
+var configfile = gamedir + "TRIVIA1.CFG";
+
+file_backup(configfile);
+
+var lines = [];
+
+var file = new File(configfile);
+if (file.open(configfile, 'r')) {
+	lines = file.readAll();
+	file.close();
+} else {
+	writeln("Error " + file.error + " opening " + file.name + " for writing");
+	exit(1)
+}
+
+var op = system.operator.split(" ", 2);
+lines[0] = system.name;
+lines[1] = op[0];
+lines[2] = op[1];
+
+lines[4] = gamedir + "triviam.asc";
+lines[5] = gamedir + "triviam.ans";
+
+file.writeAll(lines);
+file.close();
+
+writeln("Beginning node config generation...");
+for(i = 0; i < system.nodes; i++) {
+	var nodenum = i + 1;
+	lines[3] = system.node_list[i].dir + "DOOR.SYS";
+	lines[11] = nodenum;
+
+	writeln("Creating " + gamedir + 'NODE' + nodenum + '.CFG');
+	
+	var file = new File(gamedir + 'NODE' + nodenum + '.CFG');
+	if (!file.open("w")) {
+		writeln("Error " + file.error + " opening " + file.name + " for writing");
+		exit(1)
+	}
+	file.writeAll(lines);
+	file.close();
+}
+
+writeln("Config generation complete");
+
+exit(0);