diff --git a/lib/nx/FacBinary.cpp b/lib/nx/FacBinary.cpp new file mode 100644 index 0000000..5da0b5f --- /dev/null +++ b/lib/nx/FacBinary.cpp @@ -0,0 +1,167 @@ +#include "FacBinary.h" + + + +FacBinary::FacBinary() : + mHeader() +{ + clearVariables(); +} + +FacBinary::FacBinary(const FacBinary & other) +{ + importBinary(other.getBytes(), other.getSize()); +} + +FacBinary::FacBinary(const u8 * bytes, size_t len) +{ + importBinary(bytes, len); +} + +bool FacBinary::operator==(const FacBinary & other) const +{ + return isEqual(other); +} + +bool FacBinary::operator!=(const FacBinary & other) const +{ + return !isEqual(other); +} + +void FacBinary::operator=(const FacBinary & other) +{ + copyFrom(other); +} + +const u8 * FacBinary::getBytes() const +{ + return mBinaryBlob.getBytes(); +} + +size_t FacBinary::getSize() const +{ + return mBinaryBlob.getSize(); +} + +void FacBinary::exportBinary() +{ + mHeader.setFsaRights(mFsaRights); + mHeader.setContentOwnerIdSize(mContentOwnerIds.getSize() * sizeof(u32)); + mHeader.setSaveDataOwnerIdSize(mSaveDataOwnerIds.getSize() * sizeof(u32)); + mHeader.exportBinary(); + + mBinaryBlob.alloc(mHeader.getFacSize()); + memcpy(mBinaryBlob.getBytes(), mHeader.getBytes(), mHeader.getSize()); + + u32* rawContentOwnerIds = (u32*)(mBinaryBlob.getBytes() + mHeader.getContentOwnerIdOffset()); + for (size_t i = 0; i < mContentOwnerIds.getSize(); i++) + { + rawContentOwnerIds[i] = le_word(mContentOwnerIds[i]); + } + + u32* rawSaveDataOwnerIds = (u32*)(mBinaryBlob.getBytes() + mHeader.getSaveDataOwnerIdOffset()); + for (size_t i = 0; i < mSaveDataOwnerIds.getSize(); i++) + { + rawSaveDataOwnerIds[i] = le_word(mSaveDataOwnerIds[i]); + } +} + +void FacBinary::importBinary(const u8 * bytes) +{ + throw fnd::Exception(kModuleName, "Unsupported operation. importBinary(const u8* bytes) is not supported for variable size structures."); +} + +void FacBinary::importBinary(const u8 * bytes, size_t len) +{ + clearVariables(); + mHeader.importBinary(bytes, len); + if (mHeader.getFacSize() > len) + { + throw fnd::Exception(kModuleName, "FAC binary too small"); + } + + mBinaryBlob.alloc(mHeader.getFacSize()); + memcpy(mBinaryBlob.getBytes(), bytes, mBinaryBlob.getSize()); + + mFsaRights = mHeader.getFsaRights(); + u32* rawContentOwnerIds = (u32*)(mBinaryBlob.getBytes() + mHeader.getContentOwnerIdOffset()); + size_t rawContentOwnerIdNum = mHeader.getContentOwnerIdSize() / sizeof(u32); + for (size_t i = 0; i < rawContentOwnerIdNum; i++) + { + addContentOwnerId(le_word(rawContentOwnerIds[i])); + } + + u32* rawSaveDataOwnerIds = (u32*)(mBinaryBlob.getBytes() + mHeader.getSaveDataOwnerIdOffset()); + size_t rawSaveDataOwnerIdNum = mHeader.getSaveDataOwnerIdSize() / sizeof(u32); + for (size_t i = 0; i < rawSaveDataOwnerIdNum; i++) + { + addSaveDataOwnerId(le_word(rawSaveDataOwnerIds[i])); + } +} + +bool FacBinary::isPermissionSet(FsAccessFlag flag) const +{ + return (mFsaRights & flag) == flag; +} + +void FacBinary::addPermission(FsAccessFlag flag) +{ + mFsaRights |= flag; +} + +void FacBinary::removePermission(FsAccessFlag flag) +{ + mFsaRights &= ~(u64)flag; +} + +const fnd::List& FacBinary::getContentOwnerIds() const +{ + return mContentOwnerIds; +} + +void FacBinary::addContentOwnerId(u32 id) +{ + mContentOwnerIds.addElement(id); +} + +const fnd::List& FacBinary::getSaveDataOwnerIds() const +{ + return mSaveDataOwnerIds; +} + +void FacBinary::addSaveDataOwnerId(u32 id) +{ + mSaveDataOwnerIds.addElement(id); +} + +void FacBinary::clearVariables() +{ + mHeader = FacHeader(); + mFsaRights = 0; + mContentOwnerIds.clear(); + mSaveDataOwnerIds.clear(); +} + +bool FacBinary::isEqual(const FacBinary & other) const +{ + return (mHeader == other.mHeader) \ + && (mFsaRights == other.mFsaRights) \ + && (mContentOwnerIds == other.mContentOwnerIds) \ + && (mSaveDataOwnerIds == other.mSaveDataOwnerIds); +} + +void FacBinary::copyFrom(const FacBinary & other) +{ + if (other.getSize()) + { + importBinary(other.getBytes(), other.getSize()); + } + else + { + mBinaryBlob.clear(); + mHeader = other.mHeader; + mFsaRights = other.mFsaRights; + mContentOwnerIds = other.mContentOwnerIds; + mSaveDataOwnerIds = other.mSaveDataOwnerIds; + } +} diff --git a/lib/nx/FacBinary.h b/lib/nx/FacBinary.h new file mode 100644 index 0000000..61eebba --- /dev/null +++ b/lib/nx/FacBinary.h @@ -0,0 +1,81 @@ +#pragma once +#include +#include +#include +#include +#include + +class FacBinary : + public ISerialiseableBinary +{ +public: + enum FsAccessFlag + { + FSA_APPLICATION_INFO = BIT(0), + FSA_BOOT_MODE_CONTROL = BIT(1), + FSA_CALIBRATION = BIT(2), + FSA_SYSTEM_SAVE_DATA = BIT(3), + FSA_GAME_CARD = BIT(4), + FSA_SAVE_DATA_BACKUP = BIT(5), + FSA_SAVE_DATA_MANAGEMENT = BIT(6), + FSA_BIS_ALL_RAW = BIT(7), + FSA_GAME_CARD_RAW = BIT(8), + FSA_GAME_CARD_PRIVATE = BIT(9), + FSA_SET_TIME = BIT(10), + FSA_CONTENT_MANAGER = BIT(11), + FSA_IMAGE_MANAGER = BIT(12), + FSA_CREATE_SAVE_DATA = BIT(13), + FSA_SYSTEM_SAVE_DATA_MANAGEMENT = BIT(14), + FSA_BIS_FILE_SYSTEM = BIT(15), + FSA_SYSTEM_UPDATE = BIT(16), + FSA_SAVE_DATA_META = BIT(17), + FSA_DEVICE_SAVE_CONTROL = BIT(19), + FSA_SETTINGS_CONTROL = BIT(20), + FSA_DEBUG = BIT(62), + FSA_FULL_PERMISSION = BIT(63), + }; + + FacBinary(); + FacBinary(const FacBinary& other); + FacBinary(const u8* bytes, size_t len); + + bool operator==(const FacBinary& other) const; + bool operator!=(const FacBinary& other) const; + void operator=(const FacBinary& other); + + // to be used after export + const u8* getBytes() const; + size_t getSize() const; + + // export/import binary + void exportBinary(); + void importBinary(const u8* bytes); + void importBinary(const u8* bytes, size_t len); + + // variables + bool isPermissionSet(FsAccessFlag flag) const; + void addPermission(FsAccessFlag flag); + void removePermission(FsAccessFlag flag); + + const fnd::List& getContentOwnerIds() const; + void addContentOwnerId(u32 id); + + const fnd::List& getSaveDataOwnerIds() const; + void addSaveDataOwnerId(u32 id); +private: + const std::string kModuleName = "FAC_BINARY"; + + // raw binary + fnd::MemoryBlob mBinaryBlob; + + // variables + FacHeader mHeader; + u64 mFsaRights; + fnd::List mContentOwnerIds; + fnd::List mSaveDataOwnerIds; + + void clearVariables(); + bool isEqual(const FacBinary& other) const; + void copyFrom(const FacBinary& other); +}; + diff --git a/lib/nx/FacHeader.cpp b/lib/nx/FacHeader.cpp new file mode 100644 index 0000000..b415892 --- /dev/null +++ b/lib/nx/FacHeader.cpp @@ -0,0 +1,171 @@ +#include "FacHeader.h" + + + +FacHeader::FacHeader() +{ + clearVariables(); +} + +FacHeader::FacHeader(const FacHeader & other) +{ + copyFrom(other); +} + +FacHeader::FacHeader(const u8 * bytes) +{ + importBinary(bytes); +} + +bool FacHeader::operator==(const FacHeader & other) const +{ + return isEqual(other); +} + +bool FacHeader::operator!=(const FacHeader & other) const +{ + return !isEqual(other); +} + +void FacHeader::operator=(const FacHeader & other) +{ + copyFrom(other); +} + +const u8 * FacHeader::getBytes() const +{ + return mBinaryBlob.getBytes(); +} + +size_t FacHeader::getSize() const +{ + return mBinaryBlob.getSize(); +} + +void FacHeader::exportBinary() +{ + mBinaryBlob.alloc(sizeof(sFacHeader)); + sFacHeader* hdr = (sFacHeader*)mBinaryBlob.getBytes(); + + hdr->set_version(kFacFormatVersion); + hdr->set_fac_flags(mFsaRights); + + calculateOffsets(); + hdr->content_owner_ids().set_start(mContentOwnerIdPos.offset); + hdr->content_owner_ids().set_end(mContentOwnerIdPos.offset + mContentOwnerIdPos.size); + hdr->save_data_owner_ids().set_start(mSaveDataOwnerIdPos.offset); + hdr->save_data_owner_ids().set_end(mSaveDataOwnerIdPos.offset + mSaveDataOwnerIdPos.size); +} + +void FacHeader::importBinary(const u8 * bytes) +{ + mBinaryBlob.alloc(sizeof(sFacHeader)); + memcpy(mBinaryBlob.getBytes(), bytes, mBinaryBlob.getSize()); + sFacHeader* hdr = (sFacHeader*)mBinaryBlob.getBytes(); + + if (hdr->version() != kFacFormatVersion) + { + throw fnd::Exception(kModuleName, "Unsupported FAC format version"); + } + + mFsaRights = hdr->fac_flags(); + mContentOwnerIdPos.offset = hdr->content_owner_ids().start(); + mContentOwnerIdPos.size = hdr->content_owner_ids().end() - hdr->content_owner_ids().start(); + mSaveDataOwnerIdPos.offset = hdr->save_data_owner_ids().start(); + mSaveDataOwnerIdPos.size = hdr->save_data_owner_ids().end() - hdr->save_data_owner_ids().start(); +} + +void FacHeader::importBinary(const u8 * bytes, size_t len) +{ + if (len < sizeof(sFacHeader)) + { + throw fnd::Exception(kModuleName, "FAC header too small"); + } + importBinary(bytes); +} + +u64 FacHeader::getFacSize() const +{ + return getSaveDataOwnerIdOffset() + getSaveDataOwnerIdSize(); +} + +u64 FacHeader::getFsaRights() const +{ + return mFsaRights; +} + +void FacHeader::setFsaRights(u64 flag) +{ + mFsaRights = flag; +} + +size_t FacHeader::getContentOwnerIdOffset() const +{ + return mContentOwnerIdPos.offset; +} + +size_t FacHeader::getContentOwnerIdSize() const +{ + return mContentOwnerIdPos.size; +} + +void FacHeader::setContentOwnerIdSize(size_t size) +{ + mContentOwnerIdPos.size = size; +} + +size_t FacHeader::getSaveDataOwnerIdOffset() const +{ + return mSaveDataOwnerIdPos.offset; +} + +size_t FacHeader::getSaveDataOwnerIdSize() const +{ + return mSaveDataOwnerIdPos.size; +} + +void FacHeader::setSaveDataOwnerIdSize(size_t size) +{ + mSaveDataOwnerIdPos.size = size; +} + +void FacHeader::clearVariables() +{ + mFsaRights = 0; + mContentOwnerIdPos.offset = 0; + mContentOwnerIdPos.size = 0; + mSaveDataOwnerIdPos.offset = 0; + mSaveDataOwnerIdPos.size = 0; +} + +void FacHeader::calculateOffsets() +{ + mContentOwnerIdPos.offset = align(sizeof(sFacHeader), 4); + mSaveDataOwnerIdPos.offset = mContentOwnerIdPos.offset + align(mContentOwnerIdPos.size, 4); +} + +bool FacHeader::isEqual(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); +} + +void FacHeader::copyFrom(const FacHeader & other) +{ + if (other.getSize()) + { + importBinary(other.getBytes(), other.getSize()); + } + else + { + mBinaryBlob.clear(); + mFsaRights = other.mFsaRights; + mContentOwnerIdPos.offset = other.mContentOwnerIdPos.offset; + mContentOwnerIdPos.size = other.mContentOwnerIdPos.size; + mSaveDataOwnerIdPos.offset = other.mSaveDataOwnerIdPos.offset; + mSaveDataOwnerIdPos.size = other.mSaveDataOwnerIdPos.size; + } +} diff --git a/lib/nx/FacHeader.h b/lib/nx/FacHeader.h new file mode 100644 index 0000000..143f7c9 --- /dev/null +++ b/lib/nx/FacHeader.h @@ -0,0 +1,93 @@ +#pragma once +#include +#include +#include + +class FacHeader : + public ISerialiseableBinary +{ +public: + FacHeader(); + FacHeader(const FacHeader& other); + FacHeader(const u8* bytes); + + bool operator==(const FacHeader& other) const; + bool operator!=(const FacHeader& other) const; + void operator=(const FacHeader& other); + + // to be used after export + const u8* getBytes() const; + size_t getSize() const; + + // export/import binary + void exportBinary(); + void importBinary(const u8* bytes); + void importBinary(const u8* bytes, size_t len); + + // variables + u64 getFacSize() const; + + u64 getFsaRights() const; + void setFsaRights(u64 flag); + + size_t getContentOwnerIdOffset() const; + size_t getContentOwnerIdSize() const; + void setContentOwnerIdSize(size_t size); + + size_t getSaveDataOwnerIdOffset() const; + size_t getSaveDataOwnerIdSize() const; + void setSaveDataOwnerIdSize(size_t size); + +private: + const std::string kModuleName = "FAC_HEADER"; + static const u32 kFacFormatVersion = 1; + +#pragma pack (push, 1) + struct sFacHeader + { + private: + u32 version_; // default 1 + u64 fac_flags_; + struct sFacSection + { + private: + u32 start_; + u32 end_; + public: + u32 start() const { return le_word(start_); } + void set_start(u32 start) { start_ = le_word(start); } + + u32 end() const { return le_word(end_); } + void set_end(u32 end) { end_ = le_word(end); } + } content_owner_ids_, save_data_owner_ids_; // the data for these follow later in binary. start/end relative to base of FacData instance + public: + u32 version() const { return le_word(version_); } + void set_version(u32 version) { version_ = le_word(version); } + + u64 fac_flags() const { return le_dword(fac_flags_); } + void set_fac_flags(u64 fac_flags) { fac_flags_ = le_dword(fac_flags); } + + const sFacSection& content_owner_ids() const { return content_owner_ids_; } + sFacSection& content_owner_ids() { return content_owner_ids_; } + + const sFacSection& save_data_owner_ids() const { return save_data_owner_ids_; } + sFacSection& save_data_owner_ids() { return save_data_owner_ids_; } + }; +#pragma pack (pop) + + // raw binary + fnd::MemoryBlob mBinaryBlob; + + // variables + u64 mFsaRights; + struct sSection { + size_t offset; + size_t size; + } mContentOwnerIdPos, mSaveDataOwnerIdPos; + + void clearVariables(); + void calculateOffsets(); + bool isEqual(const FacHeader& other) const; + void copyFrom(const FacHeader& other); +}; + diff --git a/lib/nx/SacBinary.cpp b/lib/nx/SacBinary.cpp new file mode 100644 index 0000000..6f16c3e --- /dev/null +++ b/lib/nx/SacBinary.cpp @@ -0,0 +1,111 @@ +#include "SacBinary.h" + + + +SacBinary::SacBinary() +{ +} + +SacBinary::SacBinary(const SacBinary & other) +{ + copyFrom(other); +} + +SacBinary::SacBinary(const u8 * bytes, size_t len) +{ + importBinary(bytes, len); +} + +bool SacBinary::operator==(const SacBinary & other) const +{ + return isEqual(other); +} + +bool SacBinary::operator!=(const SacBinary & other) const +{ + return !isEqual(other); +} + +void SacBinary::operator=(const SacBinary & other) +{ + copyFrom(other); +} + +const u8 * SacBinary::getBytes() const +{ + return mBinaryBlob.getBytes(); +} + +size_t SacBinary::getSize() const +{ + return mBinaryBlob.getSize(); +} + +void SacBinary::exportBinary() +{ + size_t totalSize = 0; + for (size_t i = 0; i < mServices.getSize(); i++) + { + mServices[i].exportBinary(); + totalSize += mServices[i].getSize(); + } + + mBinaryBlob.alloc(totalSize); + for (size_t i = 0, pos = 0; i < mServices.getSize(); pos += mServices[i].getSize(), i++) + { + memcpy((mBinaryBlob.getBytes() + pos), mServices[i].getBytes(), mServices[i].getSize()); + } +} + +void SacBinary::importBinary(const u8 * bytes) +{ + throw fnd::Exception(kModuleName, "Unsupported operation. importBinary(const u8* bytes) is not supported for variable size structures."); +} + +void SacBinary::importBinary(const u8 * bytes, size_t len) +{ + clearVariables(); + mBinaryBlob.alloc(len); + memcpy(mBinaryBlob.getBytes(), bytes, mBinaryBlob.getSize()); + + SacEntry svc; + for (size_t pos = 0; pos < len; pos += mServices.atBack().getSize()) + { + svc.importBinary((const u8*)(mBinaryBlob.getBytes() + pos), len - pos); + mServices.addElement(svc); + } +} + +const fnd::List& SacBinary::getServiceList() const +{ + return mServices; +} + +void SacBinary::addService(const SacEntry& service) +{ + mServices.addElement(service); +} + +void SacBinary::clearVariables() +{ + mBinaryBlob.clear(); + mServices.clear(); +} + +bool SacBinary::isEqual(const SacBinary & other) const +{ + return mServices == other.mServices; +} + +void SacBinary::copyFrom(const SacBinary & other) +{ + if (other.getSize()) + { + importBinary(other.getBytes(), other.getSize()); + } + else + { + this->mBinaryBlob.clear(); + this->mServices = other.mServices; + } +} diff --git a/lib/nx/SacBinary.h b/lib/nx/SacBinary.h new file mode 100644 index 0000000..f9b7d81 --- /dev/null +++ b/lib/nx/SacBinary.h @@ -0,0 +1,46 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +class SacBinary : + public ISerialiseableBinary +{ +public: + SacBinary(); + SacBinary(const SacBinary& other); + SacBinary(const u8* bytes, size_t len); + + bool operator==(const SacBinary& other) const; + bool operator!=(const SacBinary& other) const; + void operator=(const SacBinary& other); + + // to be used after export + const u8* getBytes() const; + size_t getSize() const; + + // export/import binary + void exportBinary(); + void importBinary(const u8* bytes); + void importBinary(const u8* bytes, size_t len); + + // variables + const fnd::List& getServiceList() const; + void addService(const SacEntry& service); +private: + const std::string kModuleName = "SAC_BINARY"; + + // raw binary + fnd::MemoryBlob mBinaryBlob; + + // variables + fnd::List mServices; + + void clearVariables(); + bool isEqual(const SacBinary& other) const; + void copyFrom(const SacBinary& other); +}; + diff --git a/lib/nx/nx.vcxproj b/lib/nx/nx.vcxproj index 82f5dae..49ef6ff 100644 --- a/lib/nx/nx.vcxproj +++ b/lib/nx/nx.vcxproj @@ -20,14 +20,20 @@ + + + + + + diff --git a/lib/nx/nx.vcxproj.filters b/lib/nx/nx.vcxproj.filters index f57d62b..da41b74 100644 --- a/lib/nx/nx.vcxproj.filters +++ b/lib/nx/nx.vcxproj.filters @@ -30,6 +30,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files + @@ -41,5 +50,14 @@ Source Files + + Source Files + + + Source Files + + + Source Files + \ No newline at end of file