diff --git a/src/sbbs3/load_cfg.c b/src/sbbs3/load_cfg.c
index edd41f1546899ba40ecc398ad62939b1915e3ae4..ba24ff91a87ba1bb1101643c26b6627c4e697618 100644
--- a/src/sbbs3/load_cfg.c
+++ b/src/sbbs3/load_cfg.c
@@ -1,5 +1,3 @@
-/* load_cfg.c */
-
 /* Synchronet configuration load routines (exported) */
 
 /* $Id$ */
@@ -139,13 +137,6 @@ void prep_cfg(scfg_t* cfg)
 {
 	int i;
 
-#if 0 /* def __unix__ */
-	strlwr(cfg->text_dir);	/* temporary Unix-compatibility hack */
-	strlwr(cfg->temp_dir);	/* temporary Unix-compatibility hack */
-	strlwr(cfg->data_dir);	/* temporary Unix-compatibility hack */
-	strlwr(cfg->exec_dir);	/* temporary Unix-compatibility hack */
-#endif
-
 	/* Fix-up paths */
 	prep_dir(cfg->ctrl_dir, cfg->data_dir, sizeof(cfg->data_dir));
 	prep_dir(cfg->ctrl_dir, cfg->logs_dir, sizeof(cfg->logs_dir));
@@ -161,17 +152,6 @@ void prep_cfg(scfg_t* cfg)
 	prep_path(cfg->echomail_sem);
 	prep_path(cfg->inetmail_sem);
 
-#if 0 /* def __unix__ */
-	/* temporary hack for Unix compatibility */
-	strlwr(cfg->logon_mod);
-	strlwr(cfg->logoff_mod);
-	strlwr(cfg->newuser_mod);
-	strlwr(cfg->login_mod);
-	strlwr(cfg->logout_mod);
-	strlwr(cfg->sync_mod);
-	strlwr(cfg->expire_mod);
-#endif
-
 	for(i=0;i<cfg->total_subs;i++) {
 
 		if(!cfg->sub[i]->data_dir[0])	/* no data storage path specified */
@@ -224,6 +204,48 @@ void prep_cfg(scfg_t* cfg)
 		prep_path(cfg->dir[i]->upload_sem);
 	}
 
+	for(i=0;i<cfg->total_libs;i++) {
+		if((cfg->lib[i]->misc&LIB_DIRS) == 0 || cfg->lib[i]->parent_path[0] == 0)
+			continue;
+		char path[MAX_PATH+1];
+		SAFECOPY(path, cfg->lib[i]->parent_path);
+		backslash(path);
+		strcat(path, ALLFILES);
+		glob_t g;
+		if(glob(path, GLOB_MARK, NULL, &g))
+			continue;
+   		for(uint gi=0;gi<g.gl_pathc;gi++) {
+			char* p = g.gl_pathv[gi];
+			char* tp = lastchar(p);
+			if(*tp != '/')
+				continue;
+			*tp = 0;
+			dir_t dir;
+			memset(&dir, 0, sizeof(dir));
+			dir.lib = i;
+			dir.misc = DIR_FILES;
+			SAFECOPY(dir.path, p);
+			backslash(dir.path);
+			SAFECOPY(dir.lname, getfname(p));
+			SAFECOPY(dir.sname, dir.lname);
+			char code_suffix[LEN_EXTCODE+1];
+			SAFECOPY(code_suffix, dir.lname);
+			prep_code(code_suffix, cfg->lib[i]->code_prefix);
+			SAFECOPY(dir.code_suffix, code_suffix);
+			SAFEPRINTF2(dir.code,"%s%s"
+				,cfg->lib[i]->code_prefix
+				,dir.code_suffix);
+
+			dir_t** new_dirs;
+			if((new_dirs=(dir_t **)realloc(cfg->dir, sizeof(dir_t *)*(cfg->total_dirs+2)))==NULL)
+				continue;
+			cfg->dir  = new_dirs;
+			if((cfg->dir[cfg->total_dirs] = malloc(sizeof(dir_t))) == NULL)
+				continue;
+			*cfg->dir[cfg->total_dirs++] = dir;
+		}
+	}
+
 
 	/* make data filenames are all lowercase */
 	for(i=0;i<cfg->total_shells;i++)
@@ -404,6 +426,35 @@ char* prep_path(char* path)
 	return(path);
 }
 
+/* Prepare a string to be used as an internal code */
+/* Return the usable code */
+char* prep_code(char *str, const char* prefix)
+{
+	char tmp[1024];
+	int i,j;
+
+	if(prefix!=NULL) {	/* skip the grp/lib prefix, if specified */
+		i=strlen(prefix);
+		if(i && strnicmp(str,prefix,i)==0 && strlen(str)!=i)
+			str+=i;
+	}
+	for(i=j=0;str[i] && i<sizeof(tmp);i++)
+		if(str[i]>' ' && !(str[i]&0x80) && str[i]!='*' && str[i]!='?'
+			&& strchr(ILLEGAL_FILENAME_CHARS,str[i])==NULL)
+			tmp[j++]=toupper(str[i]);
+	tmp[j]=0;
+	strcpy(str,tmp);
+	if(j>LEN_CODE) {	/* Extra chars? Strip symbolic chars */
+		for(i=j=0;str[i];i++)
+			if(isalnum(str[i]))
+				tmp[j++]=str[i];
+		tmp[j]=0;
+		strcpy(str,tmp);
+	}
+	str[LEN_CODE]=0;
+	return(str);
+}
+
 /****************************************************************************/
 /* Auto-toggle daylight savings time in US time-zones						*/
 /****************************************************************************/
diff --git a/src/sbbs3/main.cpp b/src/sbbs3/main.cpp
index e23960923cc6159c2d5549e575fcffbcd9389854..b2f1c65810b5691faf722f198ff4be309d1a60cc 100644
--- a/src/sbbs3/main.cpp
+++ b/src/sbbs3/main.cpp
@@ -2724,10 +2724,10 @@ void event_thread(void* arg)
 					,sbbs->cfg.data_dir,sbbs->cfg.qhub[i]->id);
 				file=sbbs->nopen(str,O_RDONLY);
 				for(j=0;j<sbbs->cfg.qhub[i]->subs;j++) {
-					sbbs->subscan[sbbs->cfg.qhub[i]->sub[j]].ptr=0;
+					sbbs->subscan[sbbs->cfg.qhub[i]->sub[j]->subnum].ptr=0;
 					if(file!=-1) {
-						lseek(file,sbbs->cfg.sub[sbbs->cfg.qhub[i]->sub[j]]->ptridx*sizeof(int32_t),SEEK_SET);
-						read(file,&sbbs->subscan[sbbs->cfg.qhub[i]->sub[j]].ptr,sizeof(sbbs->subscan[sbbs->cfg.qhub[i]->sub[j]].ptr)); 
+						lseek(file,sbbs->cfg.sub[sbbs->cfg.qhub[i]->sub[j]->subnum]->ptridx*sizeof(int32_t),SEEK_SET);
+						read(file,&sbbs->subscan[sbbs->cfg.qhub[i]->sub[j]->subnum].ptr,sizeof(sbbs->subscan[sbbs->cfg.qhub[i]->sub[j]->subnum].ptr)); 
 					}
 				}
 				if(file!=-1)
@@ -2741,14 +2741,14 @@ void event_thread(void* arg)
 					else {
 						for(j=l=0;j<sbbs->cfg.qhub[i]->subs;j++) {
 							while(filelength(file)<
-								sbbs->cfg.sub[sbbs->cfg.qhub[i]->sub[j]]->ptridx*4L) {
+								sbbs->cfg.sub[sbbs->cfg.qhub[i]->sub[j]->subnum]->ptridx*4L) {
 								l32=l;
 								write(file,&l32,4);		/* initialize ptrs to null */
 							}
 							lseek(file
-								,sbbs->cfg.sub[sbbs->cfg.qhub[i]->sub[j]]->ptridx*sizeof(int32_t)
+								,sbbs->cfg.sub[sbbs->cfg.qhub[i]->sub[j]->subnum]->ptridx*sizeof(int32_t)
 								,SEEK_SET);
-							write(file,&sbbs->subscan[sbbs->cfg.qhub[i]->sub[j]].ptr,sizeof(sbbs->subscan[sbbs->cfg.qhub[i]->sub[j]].ptr)); 
+							write(file,&sbbs->subscan[sbbs->cfg.qhub[i]->sub[j]->subnum].ptr,sizeof(sbbs->subscan[sbbs->cfg.qhub[i]->sub[j]->subnum].ptr)); 
 						}
 						close(file); 
 					} 
diff --git a/src/sbbs3/pack_rep.cpp b/src/sbbs3/pack_rep.cpp
index 304eceef8f1b524f8f98df248c7639a6410ddd90..7a5327e9e68e9219caa30939737f035dc2305755 100644
--- a/src/sbbs3/pack_rep.cpp
+++ b/src/sbbs3/pack_rep.cpp
@@ -165,7 +165,7 @@ bool sbbs_t::pack_rep(uint hubnum)
 		free(mail);
 
 	for(i=0;i<cfg.qhub[hubnum]->subs;i++) {
-		j=cfg.qhub[hubnum]->sub[i]; 			/* j now equals the real sub num */
+		j=cfg.qhub[hubnum]->sub[i]->subnum; 			/* j now equals the real sub num */
 		msgs=getlastmsg(j,&last,0);
 		lncntr=0;						/* defeat pause */
 		if(!msgs || last<=subscan[j].ptr) {
diff --git a/src/sbbs3/qwk.cpp b/src/sbbs3/qwk.cpp
index 007e512955459da662eee12178560918f4ff01fa..106cea120500ac19a225feb85771e853e931791c 100644
--- a/src/sbbs3/qwk.cpp
+++ b/src/sbbs3/qwk.cpp
@@ -1016,7 +1016,7 @@ uint sbbs_t::resolve_qwkconf(uint n, int hubnum)
 	if(hubnum >= 0 && hubnum < cfg.total_qhubs) {
 		for(j=0;j<cfg.qhub[hubnum]->subs;j++)
 			if(cfg.qhub[hubnum]->conf[j] == n)
-				return cfg.qhub[hubnum]->sub[j];
+				return cfg.qhub[hubnum]->sub[j]->subnum;
 		return INVALID_SUB;
 	}
 
diff --git a/src/sbbs3/sbbs.h b/src/sbbs3/sbbs.h
index fa8c5d5ae22b13561d21c709fe34c4daa0d56d3b..1cc5899ad9890b49210f451a7ef22a08d28ad83c 100644
--- a/src/sbbs3/sbbs.h
+++ b/src/sbbs3/sbbs.h
@@ -1083,6 +1083,7 @@ extern "C" {
 	DLLEXPORT void		DLLCALL free_cfg(scfg_t* cfg);
 	DLLEXPORT void		DLLCALL free_text(char* text[]);
 	DLLEXPORT ushort	DLLCALL sys_timezone(scfg_t* cfg);
+	DLLEXPORT char *	DLLCALL prep_dir(const char* base, char* dir, size_t buflen);
 
 	/* scfgsave.c */
 	DLLEXPORT BOOL		DLLCALL save_cfg(scfg_t* cfg, int backup_level);
@@ -1098,9 +1099,6 @@ extern "C" {
 	DLLEXPORT void		DLLCALL refresh_cfg(scfg_t* cfg);
 
 
-	/* scfglib1.c */
-	DLLEXPORT char *	DLLCALL prep_dir(const char* base, char* dir, size_t buflen);
-
 	/* logfile.cpp */
 	DLLEXPORT int		DLLCALL errorlog(scfg_t* cfg, const char* host, const char* text);
 
@@ -1316,6 +1314,7 @@ int		strsame(const char *str1, const char *str2);	/* Compares number of same cha
 
 /* load_cfg.c */
 BOOL 	md(char *path);
+char*	prep_code(char *str, const char* prefix);
 
 #ifdef SBBS /* These aren't exported */
 
diff --git a/src/sbbs3/sbbsdefs.h b/src/sbbs3/sbbsdefs.h
index ded9e5cbe4e0532f83d8870ff464891182478baf..4e12592d1a6eebe291db77b0a9ec388f1ac708e7 100644
--- a/src/sbbs3/sbbsdefs.h
+++ b/src/sbbs3/sbbsdefs.h
@@ -220,6 +220,7 @@ typedef struct js_callback {
 
 									/* Bit values for sub[x].misc */
 #define SUB_NOVOTING	(1L<<0)		/* No voting allowed in this sub-board */
+#define SUB_TEMPLATE	(1L<<1)		/* Use this sub as template for new subs (in this group) */
 #define SUB_QNET		(1L<<3) 	/* Sub-board is netted via QWK network */
 #define SUB_PNET		(1L<<4) 	/* Sub-board is netted via PostLink */
 #define SUB_FIDO		(1L<<5) 	/* Sub-board is netted via FidoNet */
@@ -250,28 +251,32 @@ typedef struct js_callback {
 #define SUB_NOUSERSIG	(1L<<30)	/* Suppress user signatures */
 #define SUB_HDRMOD		(1L<<31)	/* Modified sub-board header info (SCFG) */
 
+                                    /* Bit values for lib[x].misc */
+#define LIB_DIRS	(1<<0) 			/* Local directory (sub-directory of lib parent) access */
+
                                     /* Bit values for dir[x].misc */
-#define DIR_FCHK	(1<<0) 			/* Check for file existence */
-#define DIR_RATE	(1<<1) 			/* Force uploads to be rated G,R, or X */
-#define DIR_MULT	(1<<2) 			/* Ask for multi-disk numbering */
-#define DIR_DUPES	(1<<3) 			/* Search this dir for upload dupes */
-#define DIR_FREE	(1<<4) 			/* Free downloads */
-#define DIR_TFREE	(1<<5) 			/* Time to download is free */
-#define DIR_CDTUL	(1<<6) 			/* Credit Uploads */
-#define DIR_CDTDL	(1<<7) 			/* Credit Downloads */
-#define DIR_ANON	(1<<8) 			/* Anonymous uploads */
-#define DIR_AONLY	(1<<9) 			/* Anonymous only */
-#define DIR_ULDATE	(1<<10)			/* Include upload date in listing */
-#define DIR_DIZ 	(1<<11)			/* FILE_ID.DIZ and DESC.SDI support */
-#define DIR_NOSCAN	(1<<12)			/* Don't new-scan this directory */
-#define DIR_NOAUTO	(1<<13)			/* Don't auto-add this directory */
-#define DIR_ULTIME	(1<<14)			/* Deduct time during uploads */
-#define DIR_CDTMIN	(1<<15)			/* Give uploader minutes instead of cdt */
-#define DIR_SINCEDL (1<<16)			/* Purge based on days since last dl */
-#define DIR_MOVENEW (1<<17)			/* Files marked as new when moved */
-#define DIR_QUIET	(1<<18)			/* Do not notify uploader of downloads */
-#define DIR_NOSTAT	(1<<19)			/* Do not include transfers in system stats */
-#define DIR_FILES	(1<<20)			/* List/access files not in database */
+#define DIR_FCHK		(1<<0) 		/* Check for file existence */
+#define DIR_RATE		(1<<1) 		/* Force uploads to be rated G,R, or X */
+#define DIR_MULT		(1<<2) 		/* Ask for multi-disk numbering */
+#define DIR_DUPES		(1<<3) 		/* Search this dir for upload dupes */
+#define DIR_FREE		(1<<4) 		/* Free downloads */
+#define DIR_TFREE		(1<<5) 		/* Time to download is free */
+#define DIR_CDTUL		(1<<6) 		/* Credit Uploads */
+#define DIR_CDTDL		(1<<7) 		/* Credit Downloads */
+#define DIR_ANON		(1<<8) 		/* Anonymous uploads */
+#define DIR_AONLY		(1<<9) 		/* Anonymous only */
+#define DIR_ULDATE		(1<<10)		/* Include upload date in listing */
+#define DIR_DIZ 		(1<<11)		/* FILE_ID.DIZ and DESC.SDI support */
+#define DIR_NOSCAN		(1<<12)		/* Don't new-scan this directory */
+#define DIR_NOAUTO		(1<<13)		/* Don't auto-add this directory */
+#define DIR_ULTIME		(1<<14)		/* Deduct time during uploads */
+#define DIR_CDTMIN		(1<<15)		/* Give uploader minutes instead of cdt */
+#define DIR_SINCEDL		(1<<16)		/* Purge based on days since last dl */
+#define DIR_MOVENEW		(1<<17)		/* Files marked as new when moved */
+#define DIR_QUIET		(1<<18)		/* Do not notify uploader of downloads */
+#define DIR_NOSTAT		(1<<19)		/* Do not include transfers in system stats */
+#define DIR_FILES		(1<<20)		/* List/access files not in database */
+#define DIR_TEMPLATE	(1<<21)		/* Use this dir as template for new dirs (in this lib) */
 
                                     /* Bit values for file_t.misc */
 #define FM_EXTDESC  (1<<0)          /* Extended description exists */
@@ -310,6 +315,14 @@ enum {                              /* Values for dir[x].sort */
     ,SORT_DATE_D                    /* Sort by upload date, descending */
     };
 
+/* Values for grp[x].sort */
+enum area_sort {
+	AREA_SORT_NONE,
+	AREA_SORT_LNAME,
+	AREA_SORT_SNAME,
+	AREA_SORT_CODE,
+};
+
 enum {
 	 clr_mnehigh
 	,clr_mnelow
diff --git a/src/sbbs3/scfgdefs.h b/src/sbbs3/scfgdefs.h
index 9e1687c07033d27ed90f0c6c104ac2ac296cc86a..f3c10d026b948a9d8322a6b779400bdb44ec8008 100644
--- a/src/sbbs3/scfgdefs.h
+++ b/src/sbbs3/scfgdefs.h
@@ -41,8 +41,8 @@
 typedef struct {							/* Message sub board info */
 	char		code[LEN_EXTCODE+1];		/* Internal code (with optional lib prefix) */
 	char		code_suffix[LEN_CODE+1];	/* Eight character code suffix */
-	char		lname[LEN_SLNAME+1],		/* Short name - used for prompts */
-				sname[LEN_SSNAME+1],		/* Long name - used for listing */
+	char		lname[LEN_SLNAME+1],		/* Long name - used for listing */
+				sname[LEN_SSNAME+1],		/* Short name - used for prompts */
 				arstr[LEN_ARSTR+1],			/* Access requirements */
 				read_arstr[LEN_ARSTR+1],	/* Read requirements */
 				post_arstr[LEN_ARSTR+1],	/* Post requirements */
@@ -62,7 +62,8 @@ typedef struct {							/* Message sub board info */
 	uint16_t	grp,						/* Which group this sub belongs to */
 				ptridx, 					/* Index into pointer file */
 				qwkconf,					/* QWK conference number */
-				maxage; 					/* Max age of messages (in days) */
+				maxage, 					/* Max age of messages (in days) */
+				subnum;						/* ephemeral index of this sub in cfg.sub[] */
 	uint32_t	misc,						/* Miscellaneous flags */
 				maxmsgs,					/* Max number of messages allowed */
 				maxcrcs;					/* Max number of CRCs to keep */
@@ -71,19 +72,20 @@ typedef struct {							/* Message sub board info */
 } sub_t;
 
 typedef struct {							/* Message group info */
-	char		lname[LEN_GLNAME+1],		/* Short name */
-				sname[LEN_GSNAME+1],		/* Long name */
+	char		lname[LEN_GLNAME+1],		/* Long name */
+				sname[LEN_GSNAME+1],		/* Short name */
 				arstr[LEN_ARSTR+1],			/* Access requirements */
 				code_prefix[LEN_CODE+1];	/* Prefix for internal code */
 	uchar		*ar;
+	enum area_sort sort;
 
 } grp_t;
 
 typedef struct {							/* Transfer Directory Info */
 	char		code[LEN_EXTCODE+1];		/* Internal code (with optional lib prefix) */
 	char		code_suffix[LEN_CODE+1];	/* Eight character code suffix */
-	char		lname[LEN_SLNAME+1],		/* Short name - used for prompts */
-				sname[LEN_SSNAME+1],		/* Long name - used for listing */
+	char		lname[LEN_SLNAME+1],		/* Long name - used for listing */
+				sname[LEN_SSNAME+1],		/* Short name - used for prompts */
 				arstr[LEN_ARSTR+1],			/* Access Requirements */
 				ul_arstr[LEN_ARSTR+1], 		/* Upload Requirements */
 				dl_arstr[LEN_ARSTR+1], 		/* Download Requirements */
@@ -104,19 +106,22 @@ typedef struct {							/* Transfer Directory Info */
 				maxage, 					/* Max age of files (in days) */
 				up_pct, 					/* Percentage of credits on uloads */
 				dn_pct, 					/* Percentage of credits on dloads */
-				lib;						/* Which library this dir is in */
+				lib,						/* Which library this dir is in */
+				dirnum;						/* ephemeral index of this dir in cfg.dir[] */
 	uint32_t	misc;						/* Miscellaneous bits */
 
 } dir_t;
 
 typedef struct {							/* Transfer Library Information */
-	char		lname[LEN_GLNAME+1],		/* Short Name - used for prompts */
-				sname[LEN_GSNAME+1],		/* Long Name - used for listings */
+	char		lname[LEN_GLNAME+1],		/* Long Name - used for listings */
+				sname[LEN_GSNAME+1],		/* Short Name - used for prompts */
 				arstr[LEN_ARSTR+1],			/* Access Requirements */
 				code_prefix[LEN_CODE+1],	/* Prefix for internal code */
 				parent_path[48];			/* Parent for dir paths */
 	uchar		*ar;
 	uint32_t	offline_dir;				/* Offline file directory */
+	uint32_t	misc;						/* Miscellaneous bits */
+	enum area_sort sort;
 
 } lib_t;
 
@@ -313,7 +318,7 @@ typedef struct {							/* QWK Network Hub */
 				freq,						/* Frequency of call-outs */
 				subs,						/* Number Sub-boards carried */
 				*conf;						/* Conference number of ea. */
-	ulong		*sub;						/* Number of local sub-board for ea. */
+	sub_t**		sub;
 	time32_t	last;						/* Last network attempt */
 	uint32_t	misc;						/* QHUB_* flags */
 
diff --git a/src/sbbs3/scfglib1.c b/src/sbbs3/scfglib1.c
index 4691207c43f76bd4755881de8d6a9b3efbd3bdb5..ea1025a82ff6b260ba170e312934666c98859729 100644
--- a/src/sbbs3/scfglib1.c
+++ b/src/sbbs3/scfglib1.c
@@ -475,9 +475,10 @@ BOOL read_msgs_cfg(scfg_t* cfg, char* error)
 		get_str(cfg->grp[i]->code_prefix,instream);
 
 		get_int(c,instream);
+		cfg->grp[i]->sort = c;
 		for(j=0;j<43;j++)
 			get_int(n,instream);
-		}
+	}
 	cfg->total_grps=i;
 
 	/**********************/
@@ -498,6 +499,8 @@ BOOL read_msgs_cfg(scfg_t* cfg, char* error)
 			return allocerr(instream,error,offset,fname,sizeof(sub_t));
 		memset(cfg->sub[i],0,sizeof(sub_t));
 
+		cfg->sub[i]->subnum = i;
+
 		get_int(cfg->sub[i]->grp,instream);
 		get_str(cfg->sub[i]->lname,instream);
 		get_str(cfg->sub[i]->sname,instream);
@@ -625,7 +628,7 @@ BOOL read_msgs_cfg(scfg_t* cfg, char* error)
 		get_int(k,instream);
 
 		if(k) {
-			if((cfg->qhub[i]->sub=(ulong *)malloc(sizeof(ulong)*k))==NULL)
+			if((cfg->qhub[i]->sub=(sub_t**)malloc(sizeof(sub_t*)*k))==NULL)
 				return allocerr(instream,error,offset,fname,sizeof(ulong)*k);
 			if((cfg->qhub[i]->conf=(ushort *)malloc(sizeof(ushort)*k))==NULL)
 				return allocerr(instream,error,offset,fname,sizeof(ushort)*k);
@@ -634,18 +637,20 @@ BOOL read_msgs_cfg(scfg_t* cfg, char* error)
 		}
 
 		for(j=0;j<k;j++) {
+			uint16_t	confnum;
 			uint16_t	subnum;
+			uint8_t		mode;
 			if(feof(instream)) break;
-			get_int(cfg->qhub[i]->conf[cfg->qhub[i]->subs],instream);
-			get_int(subnum,instream);
-			cfg->qhub[i]->sub[cfg->qhub[i]->subs]=subnum;
-			get_int(cfg->qhub[i]->mode[cfg->qhub[i]->subs],instream);
-			if(cfg->qhub[i]->sub[cfg->qhub[i]->subs]<cfg->total_subs)
-				cfg->sub[cfg->qhub[i]->sub[cfg->qhub[i]->subs]]->misc|=SUB_QNET;
-			else
-				continue;
-			if(cfg->qhub[i]->sub[cfg->qhub[i]->subs]!=INVALID_SUB)
+			get_int(confnum,instream);
+			get_int(subnum, instream);
+			get_int(mode, instream);
+			if(subnum < cfg->total_subs) {
+				cfg->sub[subnum]->misc |= SUB_QNET;
+				cfg->qhub[i]->sub[cfg->qhub[i]->subs]	= cfg->sub[subnum];
+				cfg->qhub[i]->mode[cfg->qhub[i]->subs]	= mode;
+				cfg->qhub[i]->conf[cfg->qhub[i]->subs]	= confnum;
 				cfg->qhub[i]->subs++;
+			}
 		}
 		get_int(cfg->qhub[i]->misc, instream);
 		for(j=0;j<30;j++)
diff --git a/src/sbbs3/scfglib2.c b/src/sbbs3/scfglib2.c
index 770d6286ae7e46e70f4ed6ec8d07656293cbcf3b..366e2a87d90872d1930187caae65c3d16bc787e3 100644
--- a/src/sbbs3/scfglib2.c
+++ b/src/sbbs3/scfglib2.c
@@ -310,8 +310,11 @@ BOOL read_file_cfg(scfg_t* cfg, char* error)
 		get_str(cfg->lib[i]->code_prefix,instream);
 
 		get_int(c,instream);
+		cfg->lib[i]->sort = c;
 
-		for(j=0;j<3;j++)
+		get_int(cfg->lib[i]->misc, instream);
+		
+		for(j=0;j<1;j++)
 			get_int(n,instream);	/* 0x0000 */
 
 		for(j=0;j<16;j++)
@@ -338,6 +341,8 @@ BOOL read_file_cfg(scfg_t* cfg, char* error)
 			return allocerr(instream,error,offset,fname,sizeof(dir_t));
 		memset(cfg->dir[i],0,sizeof(dir_t));
 
+		cfg->dir[i]->dirnum = i;
+
 		get_int(cfg->dir[i]->lib,instream);
 		get_str(cfg->dir[i]->lname,instream);
 		get_str(cfg->dir[i]->sname,instream);
diff --git a/src/sbbs3/scfgsave.c b/src/sbbs3/scfgsave.c
index 71cc0a041b02be5d223d41a2316d5597b7b8013b..fcd6971fc6da80662f72bd51de05f928c500a2f6 100644
--- a/src/sbbs3/scfgsave.c
+++ b/src/sbbs3/scfgsave.c
@@ -477,7 +477,7 @@ BOOL DLLCALL write_msgs_cfg(scfg_t* cfg, int backup_level)
 		put_str(cfg->grp[i]->sname,stream);
 		put_str(cfg->grp[i]->arstr,stream);
 		put_str(cfg->grp[i]->code_prefix,stream);
-		c=0;
+		c=cfg->grp[i]->sort;
 		put_int(c,stream);
 		n=0;
 		for(j=0;j<27;j++)
@@ -498,97 +498,101 @@ BOOL DLLCALL write_msgs_cfg(scfg_t* cfg, int backup_level)
 		if(cfg->sub[i]->grp < cfg->total_grps)	/* total VALID sub-boards */
 			n++;
 	put_int(n,stream);
-	for(i=0;i<cfg->total_subs;i++) {
-		if(cfg->sub[i]->grp >= cfg->total_grps) 	/* skip bogus group numbers */
-			continue;
-		put_int(cfg->sub[i]->grp,stream);
-		put_str(cfg->sub[i]->lname,stream);
-		put_str(cfg->sub[i]->sname,stream);
-		put_str(cfg->sub[i]->qwkname,stream);
-		put_str(cfg->sub[i]->code_suffix,stream);
-#if 1
-		if(cfg->sub[i]->data_dir[0]) {
-			backslash(cfg->sub[i]->data_dir);
-			md(cfg->sub[i]->data_dir);
-		}
-#endif
-		put_str(cfg->sub[i]->data_dir,stream);
-		put_str(cfg->sub[i]->arstr,stream);
-		put_str(cfg->sub[i]->read_arstr,stream);
-		put_str(cfg->sub[i]->post_arstr,stream);
-		put_str(cfg->sub[i]->op_arstr,stream);
-		l=(cfg->sub[i]->misc&(~SUB_HDRMOD));    /* Don't write mod bit */
-		put_int(l,stream);
-		put_str(cfg->sub[i]->tagline,stream);
-		put_str(cfg->sub[i]->origline,stream);
-		put_str(cfg->sub[i]->post_sem,stream);
-		put_str(cfg->sub[i]->newsgroup,stream);
-		put_int(cfg->sub[i]->faddr,stream);
-		put_int(cfg->sub[i]->maxmsgs,stream);
-		put_int(cfg->sub[i]->maxcrcs,stream);
-		put_int(cfg->sub[i]->maxage,stream);
-		put_int(cfg->sub[i]->ptridx,stream);
-		put_str(cfg->sub[i]->mod_arstr,stream);
-		put_int(cfg->sub[i]->qwkconf,stream);
-		c=0;
-		put_int(c,stream);
-		n=0;
-		for(k=0;k<26;k++)
-			put_int(n,stream);
-
-		if(all_msghdr || (cfg->sub[i]->misc&SUB_HDRMOD && !no_msghdr)) {
-			if(!cfg->sub[i]->data_dir[0])
-				SAFEPRINTF(smb.file,"%ssubs",cfg->data_dir);
-			else
-				SAFECOPY(smb.file,cfg->sub[i]->data_dir);
-			prep_dir(cfg->ctrl_dir,smb.file,sizeof(smb.file));
-			SAFEPRINTF2(str,"%s%s"
-				,cfg->grp[cfg->sub[i]->grp]->code_prefix
-				,cfg->sub[i]->code_suffix);
-			strlwr(str);
-			strcat(smb.file,str);
-			if(smb_open(&smb)!=0) {
-				/* errormsg(WHERE,ERR_OPEN,smb.file,x); */
-				continue; 
+	unsigned int subnum = 0;	/* New sub-board numbering (as saved) */
+	for(unsigned grp = 0; grp < cfg->total_grps; grp++) {
+		for(i=0;i<cfg->total_subs;i++) {
+			if(cfg->sub[i]->grp != grp)
+				continue;
+			cfg->sub[i]->subnum = subnum++;
+			put_int(cfg->sub[i]->grp,stream);
+			put_str(cfg->sub[i]->lname,stream);
+			put_str(cfg->sub[i]->sname,stream);
+			put_str(cfg->sub[i]->qwkname,stream);
+			put_str(cfg->sub[i]->code_suffix,stream);
+	#if 1
+			if(cfg->sub[i]->data_dir[0]) {
+				backslash(cfg->sub[i]->data_dir);
+				md(cfg->sub[i]->data_dir);
 			}
-			if(!filelength(fileno(smb.shd_fp))) {
-				smb.status.max_crcs=cfg->sub[i]->maxcrcs;
+	#endif
+			put_str(cfg->sub[i]->data_dir,stream);
+			put_str(cfg->sub[i]->arstr,stream);
+			put_str(cfg->sub[i]->read_arstr,stream);
+			put_str(cfg->sub[i]->post_arstr,stream);
+			put_str(cfg->sub[i]->op_arstr,stream);
+			l=(cfg->sub[i]->misc&(~SUB_HDRMOD));    /* Don't write mod bit */
+			put_int(l,stream);
+			put_str(cfg->sub[i]->tagline,stream);
+			put_str(cfg->sub[i]->origline,stream);
+			put_str(cfg->sub[i]->post_sem,stream);
+			put_str(cfg->sub[i]->newsgroup,stream);
+			put_int(cfg->sub[i]->faddr,stream);
+			put_int(cfg->sub[i]->maxmsgs,stream);
+			put_int(cfg->sub[i]->maxcrcs,stream);
+			put_int(cfg->sub[i]->maxage,stream);
+			put_int(cfg->sub[i]->ptridx,stream);
+			put_str(cfg->sub[i]->mod_arstr,stream);
+			put_int(cfg->sub[i]->qwkconf,stream);
+			c=0;
+			put_int(c,stream);
+			n=0;
+			for(k=0;k<26;k++)
+				put_int(n,stream);
+
+			if(all_msghdr || (cfg->sub[i]->misc&SUB_HDRMOD && !no_msghdr)) {
+				if(!cfg->sub[i]->data_dir[0])
+					SAFEPRINTF(smb.file,"%ssubs",cfg->data_dir);
+				else
+					SAFECOPY(smb.file,cfg->sub[i]->data_dir);
+				prep_dir(cfg->ctrl_dir,smb.file,sizeof(smb.file));
+				SAFEPRINTF2(str,"%s%s"
+					,cfg->grp[cfg->sub[i]->grp]->code_prefix
+					,cfg->sub[i]->code_suffix);
+				strlwr(str);
+				strcat(smb.file,str);
+				if(smb_open(&smb)!=0) {
+					/* errormsg(WHERE,ERR_OPEN,smb.file,x); */
+					continue; 
+				}
+				if(!filelength(fileno(smb.shd_fp))) {
+					smb.status.max_crcs=cfg->sub[i]->maxcrcs;
+					smb.status.max_msgs=cfg->sub[i]->maxmsgs;
+					smb.status.max_age=cfg->sub[i]->maxage;
+					smb.status.attr=cfg->sub[i]->misc&SUB_HYPER ? SMB_HYPERALLOC :0;
+					if(smb_create(&smb)!=0)
+						/* errormsg(WHERE,ERR_CREATE,smb.file,x) */;
+					smb_close(&smb);
+					continue; 
+				}
+				if(smb_locksmbhdr(&smb)!=0) {
+					smb_close(&smb);
+					/* errormsg(WHERE,ERR_LOCK,smb.file,x) */;
+					continue; 
+				}
+				if(smb_getstatus(&smb)!=0) {
+					smb_close(&smb);
+					/* errormsg(WHERE,ERR_READ,smb.file,x) */;
+					continue; 
+				}
+				if((!(cfg->sub[i]->misc&SUB_HYPER) || smb.status.attr&SMB_HYPERALLOC)
+					&& smb.status.max_msgs==cfg->sub[i]->maxmsgs
+					&& smb.status.max_crcs==cfg->sub[i]->maxcrcs
+					&& smb.status.max_age==cfg->sub[i]->maxage) {	/* No change */
+					smb_close(&smb);
+					continue; 
+				}
 				smb.status.max_msgs=cfg->sub[i]->maxmsgs;
+				smb.status.max_crcs=cfg->sub[i]->maxcrcs;
 				smb.status.max_age=cfg->sub[i]->maxage;
-				smb.status.attr=cfg->sub[i]->misc&SUB_HYPER ? SMB_HYPERALLOC :0;
-				if(smb_create(&smb)!=0)
-					/* errormsg(WHERE,ERR_CREATE,smb.file,x) */;
-				smb_close(&smb);
-				continue; 
-			}
-			if(smb_locksmbhdr(&smb)!=0) {
-				smb_close(&smb);
-				/* errormsg(WHERE,ERR_LOCK,smb.file,x) */;
-				continue; 
-			}
-			if(smb_getstatus(&smb)!=0) {
-				smb_close(&smb);
-				/* errormsg(WHERE,ERR_READ,smb.file,x) */;
-				continue; 
-			}
-			if((!(cfg->sub[i]->misc&SUB_HYPER) || smb.status.attr&SMB_HYPERALLOC)
-				&& smb.status.max_msgs==cfg->sub[i]->maxmsgs
-				&& smb.status.max_crcs==cfg->sub[i]->maxcrcs
-				&& smb.status.max_age==cfg->sub[i]->maxage) {	/* No change */
-				smb_close(&smb);
-				continue; 
-			}
-			smb.status.max_msgs=cfg->sub[i]->maxmsgs;
-			smb.status.max_crcs=cfg->sub[i]->maxcrcs;
-			smb.status.max_age=cfg->sub[i]->maxage;
-			if(cfg->sub[i]->misc&SUB_HYPER)
-				smb.status.attr|=SMB_HYPERALLOC;
-			if(smb_putstatus(&smb)!=0) {
-				smb_close(&smb);
-				/* errormsg(WHERE,ERR_WRITE,smb.file,x); */
-				continue; 
+				if(cfg->sub[i]->misc&SUB_HYPER)
+					smb.status.attr|=SMB_HYPERALLOC;
+				if(smb_putstatus(&smb)!=0) {
+					smb_close(&smb);
+					/* errormsg(WHERE,ERR_WRITE,smb.file,x); */
+					continue; 
+				}
+				smb_close(&smb); 
 			}
-			smb_close(&smb); 
 		}
 	}
 
@@ -631,10 +635,15 @@ BOOL DLLCALL write_msgs_cfg(scfg_t* cfg, int backup_level)
 		put_str(cfg->qhub[i]->call,stream);
 		put_str(cfg->qhub[i]->pack,stream);
 		put_str(cfg->qhub[i]->unpack,stream);
-		put_int(cfg->qhub[i]->subs,stream);
+		n = 0;
+		for(j=0;j<cfg->qhub[i]->subs;j++)
+			if(cfg->qhub[j]->sub[i] != NULL) n++;
+		put_int(n,stream);
 		for(j=0;j<cfg->qhub[i]->subs;j++) {
+			if(cfg->qhub[j]->sub[i] == NULL)
+				continue;
 			put_int(cfg->qhub[i]->conf[j],stream);
-			n=(uint16_t)cfg->qhub[i]->sub[j];
+			n=(uint16_t)cfg->qhub[i]->sub[j]->subnum;
 			put_int(n,stream);
 			put_int(cfg->qhub[i]->mode[j],stream); 
 		}
@@ -852,10 +861,11 @@ BOOL DLLCALL write_file_cfg(scfg_t* cfg, int backup_level)
 		put_str(cfg->lib[i]->parent_path,stream);
 		put_str(cfg->lib[i]->code_prefix,stream);
 
-		c=0;
+		c = cfg->lib[i]->sort;
 		put_int(c,stream);
+		put_int(cfg->lib[i]->misc,stream);
 		n=0x0000;
-		for(j=0;j<3;j++)
+		for(j=0;j<1;j++)
 			put_int(n,stream); 
 
 		n=0xffff;
@@ -871,9 +881,11 @@ BOOL DLLCALL write_file_cfg(scfg_t* cfg, int backup_level)
 		if (cfg->dir[i]->lib < cfg->total_libs)	/* total VALID file dirs */
 			n++;
 	put_int(n,stream);
+	unsigned int dirnum = 0;	/* New directory numbering (as saved) */
 	for (j = 0; j < cfg->total_libs; j++) {
 		for (i = 0; i < cfg->total_dirs; i++) {
 			if (cfg->dir[i]->lib == j) {
+				cfg->dir[i]->dirnum = dirnum++;
 				put_int(cfg->dir[i]->lib, stream);
 				put_str(cfg->dir[i]->lname, stream);
 				put_str(cfg->dir[i]->sname, stream);