protected String unpackRoomFromXML(Area forceArea, List<XMLTag> xml, boolean andContent, boolean andSave)
	{
		Area myArea;
		String areaName;
		if(forceArea!=null)
		{
			myArea=forceArea;
			areaName = forceArea.Name();
		}
		else
		{
			areaName = CMLib.xml().getValFromPieces(xml,"RAREA");
			myArea=CMLib.map().getArea(areaName);
		}
		if(myArea==null)
			return unpackErr("Room","null RAREA '"+areaName+"'",xml);
		final String roomClass=CMLib.xml().getValFromPieces(xml,"RCLAS");
		final Room newRoom=CMClass.getLocale(roomClass);
		if(newRoom==null)
			return unpackErr("Room","null RCLAS '"+roomClass+"'",xml);
		newRoom.setRoomID(CMLib.xml().getValFromPieces(xml,"ROOMID"));
		if(newRoom.roomID().equals("NEW"))
			newRoom.setRoomID(myArea.getNewRoomID(newRoom,-1));
		if((forceArea==null) && CMLib.map().getRoom(newRoom.roomID())!=null)
			return "Room Exists: "+newRoom.roomID();
		newRoom.setArea(myArea);
		if(andSave)
			CMLib.database().DBCreateRoom(newRoom);
		newRoom.setDisplayText(CMLib.xml().getValFromPieces(xml,"RDISP"));
		newRoom.setDescription(CMLib.xml().getValFromPieces(xml,"RDESC"));
		newRoom.setMiscText(CMLib.xml().restoreAngleBrackets(CMLib.xml().getValFromPieces(xml,"RTEXT")));

		// now EXITS!
		final List<XMLLibrary.XMLTag> xV=CMLib.xml().getContentsFromPieces(xml,"ROOMEXITS");
		if(xV==null)
			return unpackErr("Room","null 'ROOMEXITS' in room "+newRoom.roomID(),xml);
		for(int x=0;x<xV.size();x++)
		{
			final XMLTag xblk=xV.get(x);
			if((!xblk.tag().equalsIgnoreCase("REXIT"))||(xblk.contents()==null))
				return unpackErr("Room","??"+xblk.tag()+" in room "+newRoom.roomID(),xblk);
			final int dir=xblk.getIntFromPieces("XDIRE");
			final String doorID=xblk.getValFromPieces("XDOOR");
			if((dir<0)||(dir>=Directions.NUM_DIRECTIONS()))
			{
				if((dir>255)&&(!(newRoom instanceof GridLocale)))
					return unpackErr("Room","Not GridLocale, tried "+dir+" exit for room '"+newRoom.roomID()+"'",xblk);
				else
				if(dir>255)
				{
					final String xdata=xblk.getValFromPieces("XDATA");
					final List<String> CEs=CMParms.parseSemicolons(xdata.trim(),true);
					for(int ces=0;ces<CEs.size();ces++)
					{
						final Vector<String> SCE=CMParms.parse(CEs.get(ces).trim());
						final GridLocale.CrossExit CE=new GridLocale.CrossExit();
						if(SCE.size()<3)
							continue;
						CE.x=CMath.s_int(SCE.elementAt(0));
						CE.y=CMath.s_int(SCE.elementAt(1));
						final int codeddir=CMath.s_int(SCE.elementAt(2));
						if(SCE.size()>=4)
							CE.destRoomID=doorID+SCE.elementAt(3);
						else
							CE.destRoomID=doorID;
						CE.out=(codeddir&256)==256;
						CE.dir=codeddir&255;
						((GridLocale)newRoom).addOuterExit(CE);
						Room linkRoom=null;
						if(forceArea!=null)
							linkRoom=forceArea.getRoom(doorID);
						if(linkRoom==null)
							linkRoom=CMLib.map().getRoom(doorID);
						if((!CE.out)&&(linkRoom!=null)&&(!(linkRoom instanceof GridLocale)))
						{
							linkRoom.rawDoors()[CE.dir]=newRoom;
							linkRoom.setRawExit(CE.dir,CMClass.getExit("Open"));
							if(andSave)
								CMLib.database().DBUpdateExits(linkRoom);
						}
					}
				}
				else
					return unpackErr("Room","Unknown direction: "+dir+" in room "+newRoom.roomID());
			}
			else
			{
				final List<XMLLibrary.XMLTag> xxV=xblk.getContentsFromPieces("XEXIT");
				if(xxV==null)
					return unpackErr("Room","null 'XEXIT' in room "+newRoom.roomID(),xblk.contents());
				Exit exit=null;
				if(xxV.size()>0)
				{
					String exitID=CMLib.xml().getValFromPieces(xxV,"EXID");
					exit=CMClass.getExit(exitID);
					if(exit==null)
						return unpackErr("Room","null EXID '"+exitID+"' in room "+newRoom.roomID());
					exit.setTemporaryDoorLink("{{#"+newRoom.roomID()+"#}}");
					exit.setMiscText(CMLib.xml().restoreAngleBrackets(CMLib.xml().getValFromPieces(xxV,"EXDAT")));
					newRoom.setRawExit(dir,exit);
				}
				else
					exit=CMClass.getExit("GenExit");
				exit.recoverPhyStats();
				if(doorID.length()>0)
				{
					Room linkRoom=null;
					if(forceArea!=null)
						linkRoom=forceArea.getRoom(doorID);
					if(linkRoom==null)
						linkRoom=CMLib.map().getRoom(doorID);
					if(linkRoom!=null)
						newRoom.rawDoors()[dir]=linkRoom;
					else
					{
						newRoom.setRawExit(dir,exit); // get will get the fake one too!
						exit.setTemporaryDoorLink(doorID);
					}
				}
			}
		}

		// find any mis-linked exits and fix them!
		try
		{
			if(forceArea == null)
			{
				for(final Enumeration<Room> r=CMLib.map().rooms();r.hasMoreElements();)
				{
					Room R=r.nextElement();
					synchronized(("SYNC"+R.roomID()).intern())
					{
						R=CMLib.map().getRoom(R);
						fixFillingRoomUnlinkedExits(newRoom, R, andSave);
					}
				}
			}
			else
			{
				for(final Enumeration<Room> r=forceArea.getProperMap();r.hasMoreElements();)
				{
					Room R=r.nextElement();
					synchronized(("SYNC"+R.roomID()).intern())
					{
						R=CMLib.map().getRoom(R);
						fixFillingRoomUnlinkedExits(newRoom, R, andSave);
					}
				}
			}
		}
		catch(final NoSuchElementException e)
		{
		}
		if(andSave)
		{
			CMLib.database().DBUpdateRoom(newRoom);
			CMLib.database().DBUpdateExits(newRoom);
		}
		if(andContent)
		{
			final Map<String,Physical> identTable=new Hashtable<String,Physical>();

			final List<XMLLibrary.XMLTag> cV=CMLib.xml().getContentsFromPieces(xml,"ROOMCONTENT");
			if(cV==null)
				return unpackErr("Room","null 'ROOMCONTENT' in room "+newRoom.roomID(),xml);
			if(cV.size()>0)
			{
				final Map<MOB,String> mobRideTable=new Hashtable<MOB,String>();
				final List<XMLLibrary.XMLTag> mV=CMLib.xml().getContentsFromPieces(cV,"ROOMMOBS");
				if(mV!=null)
				{
					for(int m=0;m<mV.size();m++)
					{
						final XMLTag mblk=mV.get(m);
						if((!mblk.tag().equalsIgnoreCase("RMOB"))||(mblk.contents()==null))
							return unpackErr("Room","bad 'mblk' in room "+newRoom.roomID(),mblk);
						final String mClass=mblk.getValFromPieces("MCLAS");
						final MOB newMOB=CMClass.getMOB(mClass);
						if(newMOB==null)
							return unpackErr("Room","null 'mClass': "+mClass+" in room "+newRoom.roomID());
	
						// for rideables AND leaders now!
						final String iden=mblk.getValFromPieces("MIDEN");
						if((iden!=null)&&(iden.length()>0))
							identTable.put(iden,newMOB);
	
						newMOB.setMiscText(CMLib.xml().restoreAngleBrackets(mblk.getValFromPieces("MTEXT")));
						newMOB.basePhyStats().setLevel(mblk.getIntFromPieces("MLEVL"));
						newMOB.basePhyStats().setAbility(mblk.getIntFromPieces("MABLE"));
						newMOB.basePhyStats().setRejuv(mblk.getIntFromPieces("MREJV"));
						final String ride=mblk.getValFromPieces("MRIDE");
						if((ride!=null)&&(ride.length()>0))
							mobRideTable.put(newMOB,ride);
						newMOB.setStartRoom(newRoom);
						newMOB.setLocation(newRoom);
						newMOB.recoverCharStats();
						newMOB.recoverPhyStats();
						newMOB.recoverMaxState();
						newMOB.resetToMaxState();
						newMOB.bringToLife(newRoom,true);
					}
				}

				final Map<Item,String> itemLocTable=new Hashtable<Item,String>();
				final List<XMLLibrary.XMLTag> iV=CMLib.xml().getContentsFromPieces(cV,"ROOMITEMS");
				if(iV!=null)
				{
					for(int i=0;i<iV.size();i++)
					{
						final XMLTag iblk=iV.get(i);
						if((!iblk.tag().equalsIgnoreCase("RITEM"))||(iblk.contents()==null))
							return unpackErr("Room","bad 'iblk' in room "+newRoom.roomID(),iblk);
						final int itemCount = iblk.parms().containsKey("COUNT") ? CMath.s_int(iblk.getParmValue("COUNT")) : 1;  
						for(int inum=0;inum<itemCount;inum++)
						{
							final String iClass=iblk.getValFromPieces("ICLAS");
							final Item newItem=CMClass.getItem(iClass);
							if(newItem instanceof ArchonOnly)
								break;
							if(newItem==null)
								return unpackErr("Room","null 'iClass': "+iClass+" in room "+newRoom.roomID(),iblk);
							if((newItem instanceof Container)||(newItem instanceof Rideable))
							{
								final String iden=iblk.getValFromPieces("IIDEN");
								if((iden!=null)&&(iden.length()>0))
									identTable.put(iden,newItem);
							}
							final String iloc=iblk.getValFromPieces("ILOCA");
							if(iloc.length()>0)
								itemLocTable.put(newItem,iloc);
							newItem.basePhyStats().setLevel(iblk.getIntFromPieces("ILEVL"));
							newItem.basePhyStats().setAbility(iblk.getIntFromPieces("IABLE"));
							newItem.basePhyStats().setRejuv(iblk.getIntFromPieces("IREJV"));
							newItem.setUsesRemaining(iblk.getIntFromPieces("IUSES"));
							newItem.setOwner(newRoom); // temporary measure to take care of behaviors
							newItem.setMiscText(CMLib.xml().restoreAngleBrackets(iblk.getValFromPieces("ITEXT")));
							newItem.setContainer(null);
							newItem.recoverPhyStats();
							newRoom.addItem(newItem);
							newItem.recoverPhyStats();
						}
					}
				}
				for(final Item childI : itemLocTable.keySet())
				{
					final String loc=itemLocTable.get(childI);
					final Item parentI=(Item)identTable.get(loc);
					if(parentI!=null)
					{
						if(parentI instanceof Container)
							childI.setContainer((Container)parentI);
						childI.recoverPhyStats();
						parentI.recoverPhyStats();
					}
				}
				for(final MOB M : mobRideTable.keySet())
				{
					final String ride=mobRideTable.get(M);
					if((ride!=null)&&(ride.length()>0))
					{
						final Environmental E=identTable.get(ride);
						if(E instanceof Rideable)
							M.setRiding((Rideable)E);
						else
						if(E instanceof MOB)
							M.setFollowing((MOB)E);
					}
				}
			}
		}
		// equivalent to clear debriandrestart
		CMLib.threads().clearDebri(newRoom,0);
		if(andSave)
			CMLib.database().DBUpdateItems(newRoom);
		newRoom.startItemRejuv();
		if(andSave)
			CMLib.database().DBUpdateMOBs(newRoom);
		return "";
	}