From efe0655f05093a5fdc1414196ef4772bcfc9d103 Mon Sep 17 00:00:00 2001
From: jakcron <jkrcarr@gmail.com>
Date: Fri, 29 Jun 2018 00:20:56 +0800
Subject: [PATCH] [nx|nstool] Replace FacHeader & FacBinary

---
 .../include/nx/AccessControlInfoBinary.h      |   8 +-
 .../include/nx/AccessControlInfoDescBinary.h  |  12 +-
 lib/libnx/include/nx/FacHeader.h              |  77 -------
 ...nary.h => FileSystemAccessControlBinary.h} |  29 +--
 lib/libnx/include/nx/fac.h                    |   1 +
 lib/libnx/source/AccessControlInfoBinary.cpp  |   4 +-
 .../source/AccessControlInfoDescBinary.cpp    |   4 +-
 lib/libnx/source/FacBinary.cpp                | 150 -------------
 lib/libnx/source/FacHeader.cpp                | 166 --------------
 .../source/FileSystemAccessControlBinary.cpp  | 205 ++++++++++++++++++
 programs/nstool/source/NpdmProcess.cpp        |  10 +-
 programs/nstool/source/NpdmProcess.h          |   2 +-
 12 files changed, 245 insertions(+), 423 deletions(-)
 delete mode 100644 lib/libnx/include/nx/FacHeader.h
 rename lib/libnx/include/nx/{FacBinary.h => FileSystemAccessControlBinary.h} (56%)
 delete mode 100644 lib/libnx/source/FacBinary.cpp
 delete mode 100644 lib/libnx/source/FacHeader.cpp
 create mode 100644 lib/libnx/source/FileSystemAccessControlBinary.cpp

diff --git a/lib/libnx/include/nx/AccessControlInfoBinary.h b/lib/libnx/include/nx/AccessControlInfoBinary.h
index 3eedace..9743374 100644
--- a/lib/libnx/include/nx/AccessControlInfoBinary.h
+++ b/lib/libnx/include/nx/AccessControlInfoBinary.h
@@ -3,7 +3,7 @@
 #include <fnd/types.h>
 #include <fnd/ISerialisable.h>
 #include <nx/aci.h>
-#include <nx/FacBinary.h>
+#include <nx/FileSystemAccessControlBinary.h>
 #include <nx/SacBinary.h>
 #include <nx/KcBinary.h>
 
@@ -30,8 +30,8 @@ namespace nx
 		uint64_t getProgramId() const;
 		void setProgramId(uint64_t program_id);
 
-		const nx::FacBinary& getFileSystemAccessControl() const;
-		void setFileSystemAccessControl(const FacBinary& fac);
+		const nx::FileSystemAccessControlBinary& getFileSystemAccessControl() const;
+		void setFileSystemAccessControl(const FileSystemAccessControlBinary& fac);
 
 		const nx::SacBinary& getServiceAccessControl() const;
 		void setServiceAccessControl(const SacBinary& sac);
@@ -46,7 +46,7 @@ namespace nx
 
 		// variables
 		uint64_t mProgramId;
-		nx::FacBinary mFileSystemAccessControl;
+		nx::FileSystemAccessControlBinary mFileSystemAccessControl;
 		nx::SacBinary mServiceAccessControl;
 		nx::KcBinary mKernelCapabilities;
 	};
diff --git a/lib/libnx/include/nx/AccessControlInfoDescBinary.h b/lib/libnx/include/nx/AccessControlInfoDescBinary.h
index 362bb22..dc72356 100644
--- a/lib/libnx/include/nx/AccessControlInfoDescBinary.h
+++ b/lib/libnx/include/nx/AccessControlInfoDescBinary.h
@@ -4,7 +4,7 @@
 #include <fnd/List.h>
 #include <fnd/ISerialisable.h>
 #include <nx/aci.h>
-#include <nx/FacBinary.h>
+#include <nx/FileSystemAccessControlBinary.h>
 #include <nx/SacBinary.h>
 #include <nx/KcBinary.h>
 
@@ -63,13 +63,13 @@ namespace nx
 		const sProgramIdRestrict& getProgramIdRestrict() const;
 		void setProgramIdRestrict(const sProgramIdRestrict& pid_restrict);
 
-		const FacBinary& getFileSystemAccessControl() const;
-		void setFileSystemAccessControl(const FacBinary& fac);
+		const nx::FileSystemAccessControlBinary& getFileSystemAccessControl() const;
+		void setFileSystemAccessControl(const FileSystemAccessControlBinary& fac);
 
-		const SacBinary& getServiceAccessControl() const;
+		const nx::SacBinary& getServiceAccessControl() const;
 		void setServiceAccessControl(const SacBinary& sac);
 
-		const KcBinary& getKernelCapabilities() const;
+		const nx::KcBinary& getKernelCapabilities() const;
 		void setKernelCapabilities(const KcBinary& kc);
 	private:
 		const std::string kModuleName = "ACCESS_CONTROL_INFO_DESC_BINARY";
@@ -81,7 +81,7 @@ namespace nx
 		crypto::rsa::sRsa2048Key mNcaHeaderSignature2Key;
 		fnd::List<aci::Flag> mFlags;
 		sProgramIdRestrict mProgramIdRestrict;
-		nx::FacBinary mFileSystemAccessControl;
+		nx::FileSystemAccessControlBinary mFileSystemAccessControl;
 		nx::SacBinary mServiceAccessControl;
 		nx::KcBinary mKernelCapabilities;
 	};
diff --git a/lib/libnx/include/nx/FacHeader.h b/lib/libnx/include/nx/FacHeader.h
deleted file mode 100644
index 08ab345..0000000
--- a/lib/libnx/include/nx/FacHeader.h
+++ /dev/null
@@ -1,77 +0,0 @@
-#pragma once
-#include <string>
-#include <fnd/ISerialisable.h>
-#include <fnd/List.h>
-#include <nx/fac.h>
-
-namespace nx
-{
-	class FacHeader :
-		public fnd::ISerialisable
-	{
-	public:
-		struct sSection
-		{
-			size_t offset;
-			size_t size;
-
-			void operator=(const sSection& other)
-			{
-				offset = other.offset;
-				size = other.size;
-			}
-
-			bool operator==(const sSection& other) const
-			{
-				return (offset == other.offset) \
-					&& (size == other.size);
-			}
-
-			bool operator!=(const sSection& other) const
-			{
-				return !operator==(other);
-			}
-		};
-
-		FacHeader();
-		FacHeader(const FacHeader& other);
-
-		void operator=(const FacHeader& other);
-		bool operator==(const FacHeader& other) const;
-		bool operator!=(const FacHeader& other) const;
-
-		// export/import binary
-		void toBytes();
-		void fromBytes(const byte_t* bytes, size_t len);
-		const fnd::Vec<byte_t>& getBytes() const;
-
-		// variables
-		void clear();
-		size_t getFacSize() const;
-
-		uint32_t getFormatVersion() const;
-		void setFormatVersion(uint32_t version);
-
-		const fnd::List<fac::FsAccessFlag>& getFsaRightsList() const;
-		void setFsaRightsList(const fnd::List<fac::FsAccessFlag>& list);
-
-		const sSection& getContentOwnerIdPos() const;
-		void setContentOwnerIdPos(const sSection& pos);
-
-		const sSection& getSaveDataOwnerIdPos() const;;
-		void setSaveDataOwnerIdPos(const sSection& pos);
-
-	private:
-		const std::string kModuleName = "FAC_HEADER";
-		
-		// raw binary
-		fnd::Vec<byte_t> mRawBinary;
-
-		// variables
-		uint32_t mVersion;
-		fnd::List<fac::FsAccessFlag> mFsaRights;
-		sSection mContentOwnerIdPos;
-		sSection mSaveDataOwnerIdPos;
-	};
-}
-
diff --git a/lib/libnx/include/nx/FacBinary.h b/lib/libnx/include/nx/FileSystemAccessControlBinary.h
similarity index 56%
rename from lib/libnx/include/nx/FacBinary.h
rename to lib/libnx/include/nx/FileSystemAccessControlBinary.h
index 1d38e23..3a822bc 100644
--- a/lib/libnx/include/nx/FacBinary.h
+++ b/lib/libnx/include/nx/FileSystemAccessControlBinary.h
@@ -1,30 +1,34 @@
 #pragma once
 #include <string>
+#include <fnd/types.h>
 #include <fnd/ISerialisable.h>
 #include <fnd/List.h>
 #include <nx/fac.h>
 
+
 namespace nx
 {
-	class FacBinary :
-		public fnd::ISerialisable
+	class FileSystemAccessControlBinary : public fnd::ISerialisable
 	{
 	public:
-		FacBinary();
-		FacBinary(const FacBinary& other);
+		FileSystemAccessControlBinary();
+		FileSystemAccessControlBinary(const FileSystemAccessControlBinary& other);
 
-		void operator=(const FacBinary& other);
-		bool operator==(const FacBinary& other) const;
-		bool operator!=(const FacBinary& other) const;
+		void operator=(const FileSystemAccessControlBinary& other);
+		bool operator==(const FileSystemAccessControlBinary& other) const;
+		bool operator!=(const FileSystemAccessControlBinary& other) const;
 
 		// export/import binary
 		void toBytes();
-		void fromBytes(const byte_t* bytes, size_t len);
+		void fromBytes(const byte_t* data, size_t len);
 		const fnd::Vec<byte_t>& getBytes() const;
 
 		// variables
 		void clear();
 
+		uint32_t getFormatVersion() const;
+		void setFormatVersion(uint32_t version);
+
 		const fnd::List<fac::FsAccessFlag>& getFsaRightsList() const;
 		void setFsaRightsList(const fnd::List<fac::FsAccessFlag>& list);
 
@@ -33,17 +37,16 @@ namespace nx
 
 		const fnd::List<uint32_t>& getSaveDataOwnerIdList() const;
 		void setSaveDataOwnerIdList(const fnd::List<uint32_t>& list);
-
 	private:
-		const std::string kModuleName = "FAC_BINARY";
+		const std::string kModuleName = "FILE_SYSTEM_ACCESS_CONTROL_BINARY";
 
-		// raw binary
+		// raw data
 		fnd::Vec<byte_t> mRawBinary;
 
 		// variables
+		uint32_t mVersion;
 		fnd::List<fac::FsAccessFlag> mFsaRights;
 		fnd::List<uint32_t> mContentOwnerIdList;
 		fnd::List<uint32_t> mSaveDataOwnerIdList;
 	};
-}
-
+}
\ No newline at end of file
diff --git a/lib/libnx/include/nx/fac.h b/lib/libnx/include/nx/fac.h
index 4202698..06ed970 100644
--- a/lib/libnx/include/nx/fac.h
+++ b/lib/libnx/include/nx/fac.h
@@ -32,6 +32,7 @@ namespace nx
 		};
 
 		static const uint32_t kFacFormatVersion = 1;
+		static const size_t kSectionAlignSize = 4;
 	}
 
 #pragma pack(push,1)
diff --git a/lib/libnx/source/AccessControlInfoBinary.cpp b/lib/libnx/source/AccessControlInfoBinary.cpp
index bdf3460..6839500 100644
--- a/lib/libnx/source/AccessControlInfoBinary.cpp
+++ b/lib/libnx/source/AccessControlInfoBinary.cpp
@@ -141,12 +141,12 @@ void nx::AccessControlInfoBinary::setProgramId(uint64_t program_id)
 	mProgramId = program_id;
 }
 
-const nx::FacBinary& nx::AccessControlInfoBinary::getFileSystemAccessControl() const
+const nx::FileSystemAccessControlBinary& nx::AccessControlInfoBinary::getFileSystemAccessControl() const
 {
 	return mFileSystemAccessControl;
 }
 
-void nx::AccessControlInfoBinary::setFileSystemAccessControl(const nx::FacBinary& fac)
+void nx::AccessControlInfoBinary::setFileSystemAccessControl(const nx::FileSystemAccessControlBinary& fac)
 {
 	mFileSystemAccessControl = fac;
 }
diff --git a/lib/libnx/source/AccessControlInfoDescBinary.cpp b/lib/libnx/source/AccessControlInfoDescBinary.cpp
index 7fa7310..28b2085 100644
--- a/lib/libnx/source/AccessControlInfoDescBinary.cpp
+++ b/lib/libnx/source/AccessControlInfoDescBinary.cpp
@@ -219,12 +219,12 @@ void nx::AccessControlInfoDescBinary::setProgramIdRestrict(const sProgramIdRestr
 	mProgramIdRestrict = pid_restrict;
 }
 
-const nx::FacBinary& nx::AccessControlInfoDescBinary::getFileSystemAccessControl() const
+const nx::FileSystemAccessControlBinary& nx::AccessControlInfoDescBinary::getFileSystemAccessControl() const
 {
 	return mFileSystemAccessControl;
 }
 
-void nx::AccessControlInfoDescBinary::setFileSystemAccessControl(const nx::FacBinary& fac)
+void nx::AccessControlInfoDescBinary::setFileSystemAccessControl(const nx::FileSystemAccessControlBinary& fac)
 {
 	mFileSystemAccessControl = fac;
 }
diff --git a/lib/libnx/source/FacBinary.cpp b/lib/libnx/source/FacBinary.cpp
deleted file mode 100644
index cf391af..0000000
--- a/lib/libnx/source/FacBinary.cpp
+++ /dev/null
@@ -1,150 +0,0 @@
-#include <nx/FacBinary.h>
-#include <nx/FacHeader.h>
-
-nx::FacBinary::FacBinary()
-{
-	clear();
-}
-
-nx::FacBinary::FacBinary(const FacBinary & other)
-{
-	*this = other;
-}
-
-void nx::FacBinary::operator=(const FacBinary & other)
-{
-	if (other.getBytes().size())
-	{
-		fromBytes(other.getBytes().data(), other.getBytes().size());
-	}
-	else
-	{
-		clear();
-		mFsaRights = other.mFsaRights;
-		mContentOwnerIdList = other.mContentOwnerIdList;
-		mSaveDataOwnerIdList = other.mSaveDataOwnerIdList;
-	}
-}
-
-bool nx::FacBinary::operator==(const FacBinary & other) const
-{
-	return (mFsaRights == other.mFsaRights) \
-		&& (mContentOwnerIdList == other.mContentOwnerIdList) \
-		&& (mSaveDataOwnerIdList == other.mSaveDataOwnerIdList);
-}
-
-bool nx::FacBinary::operator!=(const FacBinary & other) const
-{
-	return !(*this == other);
-}
-
-void nx::FacBinary::toBytes()
-{
-	FacHeader hdr;
-
-	FacHeader::sSection content_id_list_pos, savedata_owner_id_list_pos;
-
-	content_id_list_pos.size = mContentOwnerIdList.size() * sizeof(uint32_t);
-	content_id_list_pos.offset = align(sizeof(sFacHeader), 4);
-	savedata_owner_id_list_pos.size = mSaveDataOwnerIdList.size() * sizeof(uint32_t);
-	savedata_owner_id_list_pos.offset = content_id_list_pos.offset + align(content_id_list_pos.size, 4);
-
-	hdr.setFormatVersion(fac::kFacFormatVersion);
-	hdr.setFsaRightsList(mFsaRights);
-	hdr.setContentOwnerIdPos(content_id_list_pos);
-	hdr.setSaveDataOwnerIdPos(savedata_owner_id_list_pos);
-	hdr.toBytes();
-
-	mRawBinary.alloc(hdr.getFacSize());
-	memcpy(mRawBinary.data(), hdr.getBytes().data(), hdr.getBytes().size());
-
-	uint32_t* rawContentOwnerIds = (uint32_t*)(mRawBinary.data() + content_id_list_pos.offset);
-	for (size_t i = 0; i < mContentOwnerIdList.size(); i++)
-	{
-		rawContentOwnerIds[i] = le_word(mContentOwnerIdList[i]);
-	}
-
-	uint32_t* rawSaveDataOwnerIds = (uint32_t*)(mRawBinary.data() + savedata_owner_id_list_pos.offset);
-	for (size_t i = 0; i < mSaveDataOwnerIdList.size(); i++)
-	{
-		rawSaveDataOwnerIds[i] = le_word(mSaveDataOwnerIdList[i]);
-	}
-}
-
-void nx::FacBinary::fromBytes(const byte_t* data, size_t len)
-{
-	clear();
-
-	FacHeader hdr;
-	hdr.fromBytes(data, len);
-
-	mFsaRights = hdr.getFsaRightsList();
-
-	if (hdr.getFacSize() > len)
-	{
-		throw fnd::Exception(kModuleName, "FAC binary too small");
-	}
-
-	mRawBinary.alloc(hdr.getFacSize());
-	memcpy(mRawBinary.data(), data, mRawBinary.size());
-
-	uint32_t* rawContentOwnerIds = (uint32_t*)(mRawBinary.data() + hdr.getContentOwnerIdPos().offset);
-	size_t rawContentOwnerIdNum = hdr.getContentOwnerIdPos().size / sizeof(uint32_t);
-	for (size_t i = 0; i < rawContentOwnerIdNum; i++)
-	{
-		mContentOwnerIdList.addElement(le_word(rawContentOwnerIds[i]));
-	}
-
-	uint32_t* rawSaveDataOwnerIds = (uint32_t*)(mRawBinary.data() + hdr.getSaveDataOwnerIdPos().offset);
-	size_t rawSaveDataOwnerIdNum = hdr.getSaveDataOwnerIdPos().size / sizeof(uint32_t);
-	for (size_t i = 0; i < rawSaveDataOwnerIdNum; i++)
-	{
-		mSaveDataOwnerIdList.addElement(le_word(rawSaveDataOwnerIds[i]));
-	}
-}
-
-const fnd::Vec<byte_t>& nx::FacBinary::getBytes() const
-{
-	return mRawBinary;
-}
-
-void nx::FacBinary::clear()
-{
-	mRawBinary.clear();
-	mContentOwnerIdList.clear();
-	mSaveDataOwnerIdList.clear();
-}
-
-const fnd::List<nx::fac::FsAccessFlag>& nx::FacBinary::getFsaRightsList() const
-{
-	return mFsaRights;
-}
-
-void nx::FacBinary::setFsaRightsList(const fnd::List<fac::FsAccessFlag>& list)
-{
-	mFsaRights.clear();
-	for (size_t i = 0; i < list.size(); i++)
-	{
-		mFsaRights.hasElement(list[i]) ? mFsaRights.addElement(list[i]) : throw fnd::Exception(kModuleName, "FSA right already exists");
-	}
-}
-
-const fnd::List<uint32_t>& nx::FacBinary::getContentOwnerIdList() const
-{
-	return mContentOwnerIdList;
-}
-
-void nx::FacBinary::setContentOwnerIdList(const fnd::List<uint32_t>& list)
-{
-	mContentOwnerIdList = list;
-}
-
-const fnd::List<uint32_t>& nx::FacBinary::getSaveDataOwnerIdList() const
-{
-	return mSaveDataOwnerIdList;
-}
-
-void nx::FacBinary::setSaveDataOwnerIdList(const fnd::List<uint32_t>& list)
-{
-	mSaveDataOwnerIdList = list;
-}
diff --git a/lib/libnx/source/FacHeader.cpp b/lib/libnx/source/FacHeader.cpp
deleted file mode 100644
index d1a06b3..0000000
--- a/lib/libnx/source/FacHeader.cpp
+++ /dev/null
@@ -1,166 +0,0 @@
-#include <nx/FacHeader.h>
-
-nx::FacHeader::FacHeader() :
-	mFsaRights()
-{
-	clear();
-}
-
-nx::FacHeader::FacHeader(const FacHeader & other) :
-	mFsaRights()
-{
-	*this = other;
-}
-
-void nx::FacHeader::operator=(const FacHeader & other)
-{
-	if (other.getBytes().size())
-	{
-		fromBytes(other.getBytes().data(), other.getBytes().size());
-	}
-	else
-	{
-		clear();
-		mFsaRights = other.mFsaRights;
-		mContentOwnerIdPos.offset = other.mContentOwnerIdPos.offset;
-		mContentOwnerIdPos.size = other.mContentOwnerIdPos.size;
-		mSaveDataOwnerIdPos.offset = other.mSaveDataOwnerIdPos.offset;
-		mSaveDataOwnerIdPos.size = other.mSaveDataOwnerIdPos.size;
-	}
-}
-
-bool nx::FacHeader::operator==(const FacHeader & other) const
-{
-	return (mFsaRights == other.mFsaRights) \
-		&& (mContentOwnerIdPos.offset == other.mContentOwnerIdPos.offset) \
-		&& (mContentOwnerIdPos.size == other.mContentOwnerIdPos.size) \
-		&& (mSaveDataOwnerIdPos.offset == other.mSaveDataOwnerIdPos.offset) \
-		&& (mSaveDataOwnerIdPos.size == other.mSaveDataOwnerIdPos.size);
-}
-
-bool nx::FacHeader::operator!=(const FacHeader & other) const
-{
-	return !(*this == other);
-}
-
-void nx::FacHeader::toBytes()
-{
-	mRawBinary.alloc(sizeof(sFacHeader));
-	sFacHeader* hdr = (sFacHeader*)mRawBinary.data();
-
-	if (mVersion != fac::kFacFormatVersion)
-	{
-		fnd::Exception(kModuleName, "Unsupported format version");
-	}
-	hdr->version = (mVersion);
-
-	uint64_t flag = 0;
-	for (size_t i = 0; i < mFsaRights.size(); i++)
-	{
-		flag |= _BIT((uint64_t)mFsaRights[i]);
-	}
-	hdr->fac_flags = (flag);
-
-	hdr->content_owner_ids.start = (uint32_t)(mContentOwnerIdPos.offset);
-	hdr->content_owner_ids.end = (uint32_t)(mContentOwnerIdPos.offset + mContentOwnerIdPos.size);
-	hdr->save_data_owner_ids.start = (uint32_t)(mSaveDataOwnerIdPos.offset);
-	hdr->save_data_owner_ids.end = (uint32_t)(mSaveDataOwnerIdPos.offset + mSaveDataOwnerIdPos.size);
-}
-
-void nx::FacHeader::fromBytes(const byte_t* data, size_t len)
-{
-	if (len < sizeof(sFacHeader))
-	{
-		throw fnd::Exception(kModuleName, "FAC header too small");
-	}
-	
-	// clear internal members
-	clear();
-
-	mRawBinary.alloc(sizeof(sFacHeader));
-	memcpy(mRawBinary.data(), data, mRawBinary.size());
-	sFacHeader* hdr = (sFacHeader*)mRawBinary.data();
-
-	if (hdr->version.get() != fac::kFacFormatVersion)
-	{
-		throw fnd::Exception(kModuleName, "Unsupported FAC format version");
-	}
-	mVersion = hdr->version.get();
-
-	for (uint64_t i = 0; i < 64; i++)
-	{
-		if (_HAS_BIT(hdr->fac_flags.get(), i))
-		{
-			mFsaRights.addElement((fac::FsAccessFlag)i);
-		}
-	}
-	mContentOwnerIdPos.offset = hdr->content_owner_ids.start.get();
-	mContentOwnerIdPos.size = hdr->content_owner_ids.end.get() > hdr->content_owner_ids.start.get() ? hdr->content_owner_ids.end.get() - hdr->content_owner_ids.start.get() : 0;
-	mSaveDataOwnerIdPos.offset = hdr->save_data_owner_ids.start.get();
-	mSaveDataOwnerIdPos.size = hdr->save_data_owner_ids.end.get() > hdr->save_data_owner_ids.start.get() ? hdr->save_data_owner_ids.end.get() - hdr->save_data_owner_ids.start.get() : 0;
-}
-
-const fnd::Vec<byte_t>& nx::FacHeader::getBytes() const
-{
-	return mRawBinary;
-}
-
-void nx::FacHeader::clear()
-{
-	mFsaRights.clear();
-	mContentOwnerIdPos.offset = 0;
-	mContentOwnerIdPos.size = 0;
-	mSaveDataOwnerIdPos.offset = 0;
-	mSaveDataOwnerIdPos.size = 0;
-}
-
-size_t nx::FacHeader::getFacSize() const
-{
-	size_t savedata = mSaveDataOwnerIdPos.offset + mSaveDataOwnerIdPos.size;
-	size_t content = mContentOwnerIdPos.offset + mContentOwnerIdPos.size;
-	return _MAX(_MAX(savedata, content), sizeof(sFacHeader));
-}
-
-uint32_t nx::FacHeader::getFormatVersion() const
-{
-	return mVersion;
-}
-
-void nx::FacHeader::setFormatVersion(uint32_t version)
-{
-	mVersion = version;
-}
-
-const fnd::List<nx::fac::FsAccessFlag>& nx::FacHeader::getFsaRightsList() const
-{
-	return mFsaRights;
-}
-
-void nx::FacHeader::setFsaRightsList(const fnd::List<fac::FsAccessFlag>& list)
-{
-	mFsaRights.clear();
-	for (size_t i = 0; i < list.size(); i++)
-	{
-		mFsaRights.hasElement(list[i]) ? mFsaRights.addElement(list[i]) : throw fnd::Exception(kModuleName, "FSA right already exists");
-	}
-}
-
-const nx::FacHeader::sSection& nx::FacHeader::getContentOwnerIdPos() const
-{
-	return mContentOwnerIdPos;
-}
-
-void nx::FacHeader::setContentOwnerIdPos(const sSection& pos)
-{
-	mContentOwnerIdPos = pos;
-}
-
-const nx::FacHeader::sSection& nx::FacHeader::getSaveDataOwnerIdPos() const
-{
-	return mSaveDataOwnerIdPos;
-}
-
-void nx::FacHeader::setSaveDataOwnerIdPos(const sSection& pos)
-{
-	mSaveDataOwnerIdPos = pos;
-}
\ No newline at end of file
diff --git a/lib/libnx/source/FileSystemAccessControlBinary.cpp b/lib/libnx/source/FileSystemAccessControlBinary.cpp
new file mode 100644
index 0000000..60b1971
--- /dev/null
+++ b/lib/libnx/source/FileSystemAccessControlBinary.cpp
@@ -0,0 +1,205 @@
+#include <nx/FileSystemAccessControlBinary.h>
+
+#include <fnd/SimpleTextOutput.h>
+
+nx::FileSystemAccessControlBinary::FileSystemAccessControlBinary()
+{
+	clear();
+}
+
+nx::FileSystemAccessControlBinary::FileSystemAccessControlBinary(const FileSystemAccessControlBinary & other)
+{
+	*this = other;
+}
+
+void nx::FileSystemAccessControlBinary::operator=(const FileSystemAccessControlBinary & other)
+{
+	mRawBinary = other.mRawBinary;
+	mVersion = other.mVersion;
+	mFsaRights = other.mFsaRights;
+	mContentOwnerIdList = other.mContentOwnerIdList;
+	mSaveDataOwnerIdList = other.mSaveDataOwnerIdList;
+}
+
+bool nx::FileSystemAccessControlBinary::operator==(const FileSystemAccessControlBinary & other) const
+{
+	return (mVersion == other.mVersion) \
+		&& (mFsaRights == other.mFsaRights) \
+		&& (mContentOwnerIdList == other.mContentOwnerIdList) \
+		&& (mSaveDataOwnerIdList == other.mSaveDataOwnerIdList);
+}
+
+bool nx::FileSystemAccessControlBinary::operator!=(const FileSystemAccessControlBinary & other) const
+{
+	return !(*this == other);
+}
+
+void nx::FileSystemAccessControlBinary::toBytes()
+{
+	// determine section layout
+	struct sLayout {
+		uint32_t offset, size;
+	} content, savedata;
+
+	content.offset = align(sizeof(sFacHeader), fac::kSectionAlignSize);
+	content.size = (uint32_t)(mContentOwnerIdList.size() * sizeof(uint32_t));
+	savedata.offset = content.offset + align(content.size, fac::kSectionAlignSize);
+	savedata.size = (uint32_t)(mSaveDataOwnerIdList.size() * sizeof(uint32_t));
+
+	// get total size
+	size_t total_size = _MAX(_MAX(content.offset + content.size, savedata.offset + savedata.size), align(sizeof(sFacHeader), fac::kSectionAlignSize)); 
+
+	mRawBinary.alloc(total_size);
+	sFacHeader* hdr = (sFacHeader*)mRawBinary.data();
+
+	// set type
+	hdr->version = mVersion;
+
+	// flags
+	uint64_t flag = 0;
+	for (size_t i = 0; i < mFsaRights.size(); i++)
+	{
+		flag |= _BIT((uint64_t)mFsaRights[i]);
+	}
+	hdr->fac_flags = flag;
+
+	// set offset/size
+	hdr->content_owner_ids.start = content.offset;
+	if (content.size > 0)
+		hdr->content_owner_ids.end = content.offset + content.size;
+	hdr->save_data_owner_ids.start = savedata.offset;
+	if (savedata.size > 0)
+		hdr->save_data_owner_ids.end = savedata.offset + savedata.size;
+
+	// set ids
+	le_uint32_t* content_owner_ids = (le_uint32_t*)(mRawBinary.data() + content.offset);
+	for (size_t i = 0; i < mContentOwnerIdList.size(); i++)
+	{
+		content_owner_ids[i] = mContentOwnerIdList[i];
+	}
+
+	le_uint32_t* save_data_owner_ids = (le_uint32_t*)(mRawBinary.data() + savedata.offset);
+	for (size_t i = 0; i < mSaveDataOwnerIdList.size(); i++)
+	{
+		save_data_owner_ids[i] = mSaveDataOwnerIdList[i];
+	}
+}
+
+void nx::FileSystemAccessControlBinary::fromBytes(const byte_t* data, size_t len)
+{
+	// check size
+	if (len < sizeof(sFacHeader))
+	{
+		throw fnd::Exception(kModuleName, "FileSystemAccessControlInfo binary is too small");
+	}
+	
+	// clear variables
+	clear();
+
+	// save a copy of the header
+	sFacHeader hdr;
+	memcpy((void*)&hdr, data, sizeof(sFacHeader));
+
+	// check format version
+	if (hdr.version.get() != fac::kFacFormatVersion)
+	{
+		throw fnd::Exception(kModuleName, "FileSystemAccessControlInfo format version unsupported");
+	}
+	
+	// get total size
+	size_t total_size = _MAX(_MAX(hdr.content_owner_ids.end.get(), hdr.save_data_owner_ids.end.get()), align(sizeof(sFacHeader), fac::kSectionAlignSize)); 
+
+	// validate binary size
+	if (len < total_size)
+	{
+		throw fnd::Exception(kModuleName, "FileSystemAccessControlInfo binary is too small");
+	}
+
+	// allocate memory
+	mRawBinary.alloc(total_size);
+	memcpy(mRawBinary.data(), data, mRawBinary.size());
+
+	// save variables
+	mVersion = hdr.version.get();
+	for (size_t i = 0; i < 64; i++)
+	{
+		if (_HAS_BIT(hdr.fac_flags.get(), i))
+		{
+			mFsaRights.addElement((fac::FsAccessFlag)i);
+		}
+	}
+
+	// save ids
+	if (hdr.content_owner_ids.end.get() > hdr.content_owner_ids.start.get())
+	{
+		le_uint32_t* content_owner_ids = (le_uint32_t*)(mRawBinary.data() + hdr.content_owner_ids.start.get());
+		size_t content_owner_id_num = (hdr.content_owner_ids.end.get() - hdr.content_owner_ids.start.get()) / sizeof(uint32_t);
+		for (size_t i = 0; i < content_owner_id_num; i++)
+		{
+			mContentOwnerIdList.addElement(content_owner_ids[i].get());
+		}
+	}
+	if (hdr.save_data_owner_ids.end.get() > hdr.save_data_owner_ids.start.get())
+	{
+		le_uint32_t* save_data_owner_ids = (le_uint32_t*)(mRawBinary.data() + hdr.save_data_owner_ids.start.get());
+		size_t save_data_owner_id_num = (hdr.save_data_owner_ids.end.get() - hdr.save_data_owner_ids.start.get()) / sizeof(uint32_t);
+		for (size_t i = 0; i < save_data_owner_id_num; i++)
+		{
+			mSaveDataOwnerIdList.addElement(save_data_owner_ids[i].get());
+		}
+	}
+}
+
+const fnd::Vec<byte_t>& nx::FileSystemAccessControlBinary::getBytes() const
+{
+	return mRawBinary;
+}
+
+void nx::FileSystemAccessControlBinary::clear()
+{
+	mRawBinary.clear();
+	mVersion = 0;
+	mFsaRights.clear();
+	mContentOwnerIdList.clear();
+	mSaveDataOwnerIdList.clear();
+}
+
+uint32_t nx::FileSystemAccessControlBinary::getFormatVersion() const
+{
+	return mVersion;
+}
+
+void nx::FileSystemAccessControlBinary::setFormatVersion(uint32_t format_version)
+{
+	mVersion = format_version;
+}
+
+const fnd::List<nx::fac::FsAccessFlag>& nx::FileSystemAccessControlBinary::getFsaRightsList() const
+{
+	return mFsaRights;
+}
+
+void nx::FileSystemAccessControlBinary::setFsaRightsList(const fnd::List<fac::FsAccessFlag>& list)
+{
+	mFsaRights = list;
+}
+
+const fnd::List<uint32_t>& nx::FileSystemAccessControlBinary::getContentOwnerIdList() const
+{
+	return mContentOwnerIdList;
+}
+
+void nx::FileSystemAccessControlBinary::setContentOwnerIdList(const fnd::List<uint32_t>& list)
+{
+	mContentOwnerIdList = list;
+}
+
+const fnd::List<uint32_t>& nx::FileSystemAccessControlBinary::getSaveDataOwnerIdList() const
+{
+	return mSaveDataOwnerIdList;
+}
+
+void nx::FileSystemAccessControlBinary::setSaveDataOwnerIdList(const fnd::List<uint32_t>& list)
+{
+	mSaveDataOwnerIdList = list;
+}
\ No newline at end of file
diff --git a/programs/nstool/source/NpdmProcess.cpp b/programs/nstool/source/NpdmProcess.cpp
index 30b12f0..65fc8a8 100644
--- a/programs/nstool/source/NpdmProcess.cpp
+++ b/programs/nstool/source/NpdmProcess.cpp
@@ -570,9 +570,10 @@ void NpdmProcess::displayAciDescHdr(const nx::AccessControlInfoDescBinary& acid)
 	printf("    Max:           0x%016" PRIx64 "\n", acid.getProgramIdRestrict().max);
 }
 
-void NpdmProcess::displayFac(const nx::FacBinary& fac)
+void NpdmProcess::displayFac(const nx::FileSystemAccessControlBinary& fac)
 {
 	printf("[FS Access Control]\n");
+	printf("  Format Version:  %d\n", fac.getFormatVersion());
 
 	if (fac.getFsaRightsList().size())
 	{
@@ -583,7 +584,12 @@ void NpdmProcess::displayFac(const nx::FacBinary& fac)
 			{
 				printf("%s    ", i != 0 ? "\n" : "");
 			}
-			printf("%s%s", kFsaFlag[fac.getFsaRightsList()[i]].c_str(), fac.getFsaRightsList()[i] != fac.getFsaRightsList().atBack() ? ", " : "\n");
+			printf("%s", kFsaFlag[fac.getFsaRightsList()[i]].c_str());
+			if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
+				printf(" (mask 0x%" PRIx64 ")", _BIT(fac.getFsaRightsList()[i]));
+			printf("%s", fac.getFsaRightsList()[i] != fac.getFsaRightsList().atBack() ? ", " : "\n");
+
+			
 		}
 	}
 	else
diff --git a/programs/nstool/source/NpdmProcess.h b/programs/nstool/source/NpdmProcess.h
index b0b977c..9997f9b 100644
--- a/programs/nstool/source/NpdmProcess.h
+++ b/programs/nstool/source/NpdmProcess.h
@@ -38,7 +38,7 @@ private:
 	void displayNpdmHeader(const nx::NpdmHeader& hdr);
 	void displayAciHdr(const nx::AccessControlInfoBinary& aci);
 	void displayAciDescHdr(const nx::AccessControlInfoDescBinary& aci);
-	void displayFac(const nx::FacBinary& fac);
+	void displayFac(const nx::FileSystemAccessControlBinary& fac);
 	void displaySac(const nx::SacBinary& sac);
 	void displayKernelCap(const nx::KcBinary& kern);
 };
\ No newline at end of file