diff --git a/driver/main.cpp b/driver/main.cpp index c99cda2..bf58d99 100644 --- a/driver/main.cpp +++ b/driver/main.cpp @@ -2,34 +2,53 @@ #include #include "NODLib.hpp" +static void printHelp() +{ + fprintf(stderr, "Usage:\n" + " nodlib extract [-f] []\n" + " nodlib make []\n"); +} + int main(int argc, char* argv[]) { - if (argc < 2) + if (argc < 3) { - fprintf(stderr, "Usage: nodlib \n"); + printHelp(); return -1; } - std::unique_ptr disc = NOD::OpenDiscFromImage(argv[1]); - if (!disc) - return -1; - - disc->extractToDirectory("/home/jacko/Desktop/TrilogyDump2"); - - const NOD::DiscBase::IPartition* dataPart = disc->getDataPartition(); - if (dataPart) + const char* inDir = nullptr; + const char* outDir = "."; + bool force = false; + for (int a=2 ; agetFSTRoot()) - { - if (node.getKind() == NOD::DiscBase::IPartition::Node::NODE_FILE) - printf("FILE: %s\n", node.getName().c_str()); - else if (node.getKind() == NOD::DiscBase::IPartition::Node::NODE_DIRECTORY) - { - printf("DIR: %s\n", node.getName().c_str()); - for (const NOD::DiscBase::IPartition::Node& subnode : node) - printf("SUBFILE: %s\n", subnode.getName().c_str()); - } - } + if (argv[a][0] == '-' && argv[a][1] == 'f') + force = true; + else if (!inDir) + inDir = argv[a]; + else + outDir = argv[a]; + } + + if (!strcasecmp(argv[1], "extract")) + { + std::unique_ptr disc = NOD::OpenDiscFromImage(inDir); + if (!disc) + return -1; + + NOD::DiscBase::IPartition* dataPart = disc->getDataPartition(); + if (!dataPart) + return -1; + + dataPart->extractToDirectory(outDir, force); + } + else if (!strcasecmp(argv[1], "make")) + { + } + else + { + printHelp(); + return -1; } return 0; diff --git a/include/DiscBase.hpp b/include/DiscBase.hpp index c48dc8e..bb60162 100644 --- a/include/DiscBase.hpp +++ b/include/DiscBase.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "Util.hpp" #include "IDiscIO.hpp" #include "IFileIO.hpp" @@ -71,8 +72,8 @@ public: Kind m_kind; std::unique_ptr m_hddFile; - size_t m_discOffset; - size_t m_discLength; + uint64_t m_discOffset; + uint64_t m_discLength; std::string m_name; std::vector::iterator m_childrenBegin; @@ -82,7 +83,7 @@ public: Node(const IPartition& parent, const FSTNode& node, const char* name) : m_parent(parent), m_kind(node.isDir() ? NODE_DIRECTORY : NODE_FILE), - m_discOffset(node.getOffset()), + m_discOffset(parent.normalizeOffset(node.getOffset())), m_discLength(node.getLength()), m_name(name) {} inline Kind getKind() const {return m_kind;} @@ -123,25 +124,31 @@ public: void extractToDirectory(const std::string& basePath, bool force=false); }; protected: - uint32_t m_dolOff; - uint32_t m_fstOff; - uint32_t m_fstSz; - uint32_t m_apploaderOff; + uint64_t m_dolOff; + uint64_t m_fstOff; + uint64_t m_fstSz; + uint64_t m_apploaderSz; std::vector m_nodes; void parseFST(IPartReadStream& s); const DiscBase& m_parent; Kind m_kind; - size_t m_offset; + uint64_t m_offset; public: - IPartition(const DiscBase& parent, Kind kind, size_t offset) + IPartition(const DiscBase& parent, Kind kind, uint64_t offset) : m_parent(parent), m_kind(kind), m_offset(offset) {} + virtual uint64_t normalizeOffset(uint64_t anOffset) const {return anOffset;} inline Kind getKind() const {return m_kind;} - virtual std::unique_ptr beginReadStream(size_t offset=0) const=0; + virtual std::unique_ptr beginReadStream(uint64_t offset=0) const=0; inline const Node& getFSTRoot() const {return m_nodes[0];} inline Node& getFSTRoot() {return m_nodes[0];} - inline void extractToDirectory(const std::string& path, bool force=false) - {m_nodes[0].extractToDirectory(path, force);} + std::string pathOfNode(const Node& node); + void extractToDirectory(const std::string& path, bool force=false); + private: + bool _recursivePathOfNode(const std::string& basePath, + const Node& refNode, + const Node& curNode, + std::string& result); }; protected: @@ -153,14 +160,14 @@ public: virtual bool commit()=0; inline const Header& getHeader() const {return m_header;} inline const IDiscIO& getDiscIO() const {return *m_discIO.get();} - inline const IPartition* getDataPartition() const + inline IPartition* getDataPartition() { for (const std::unique_ptr& part : m_partitions) if (part->getKind() == IPartition::PART_DATA) return part.get(); return nullptr; } - inline const IPartition* getUpdatePartition() const + inline IPartition* getUpdatePartition() { for (const std::unique_ptr& part : m_partitions) if (part->getKind() == IPartition::PART_UPDATE) diff --git a/include/IDiscIO.hpp b/include/IDiscIO.hpp index bedec1f..e82d4e9 100644 --- a/include/IDiscIO.hpp +++ b/include/IDiscIO.hpp @@ -4,6 +4,7 @@ #include #include #include +#include namespace NOD { @@ -15,27 +16,27 @@ public: struct IReadStream { - virtual size_t read(void* buf, size_t length)=0; - virtual void seek(size_t offset, int whence=SEEK_SET)=0; + virtual uint64_t read(void* buf, uint64_t length)=0; + virtual void seek(int64_t offset, int whence=SEEK_SET)=0; }; - virtual std::unique_ptr beginReadStream(size_t offset=0) const=0; + virtual std::unique_ptr beginReadStream(uint64_t offset=0) const=0; struct IWriteStream { - virtual size_t write(void* buf, size_t length)=0; + virtual uint64_t write(void* buf, uint64_t length)=0; }; - virtual std::unique_ptr beginWriteStream(size_t offset=0) const=0; + virtual std::unique_ptr beginWriteStream(uint64_t offset=0) const=0; }; struct IPartReadStream { - virtual void seek(size_t offset, int whence=SEEK_SET)=0; - virtual size_t read(void* buf, size_t length)=0; + virtual void seek(int64_t offset, int whence=SEEK_SET)=0; + virtual uint64_t read(void* buf, uint64_t length)=0; }; struct IPartWriteStream { - virtual size_t write(void* buf, size_t length)=0; + virtual uint64_t write(void* buf, uint64_t length)=0; }; } diff --git a/include/IFileIO.hpp b/include/IFileIO.hpp index 5d8260d..c3a6fc2 100644 --- a/include/IFileIO.hpp +++ b/include/IFileIO.hpp @@ -12,25 +12,27 @@ class IFileIO { public: virtual ~IFileIO() {} - virtual size_t size()=0; + virtual uint64_t size()=0; struct IWriteStream { virtual ~IWriteStream() {} - virtual size_t copyFromDisc(struct IPartReadStream& discio, size_t length)=0; + virtual uint64_t write(void* buf, uint64_t length)=0; + virtual uint64_t copyFromDisc(struct IPartReadStream& discio, uint64_t length)=0; }; virtual std::unique_ptr beginWriteStream() const=0; struct IReadStream { virtual ~IReadStream() {} - virtual size_t copyToDisc(struct IPartWriteStream& discio, size_t length)=0; + virtual uint64_t read(void* buf, uint64_t length)=0; + virtual uint64_t copyToDisc(struct IPartWriteStream& discio, uint64_t length)=0; }; virtual std::unique_ptr beginReadStream() const=0; }; std::unique_ptr NewFileIO(const std::string& path); -std::unique_ptr NewMemIO(void* buf, size_t size); +std::unique_ptr NewMemIO(void* buf, uint64_t size); } diff --git a/include/aes.hpp b/include/aes.hpp index 93a9960..a4f7907 100644 --- a/include/aes.hpp +++ b/include/aes.hpp @@ -11,8 +11,8 @@ namespace NOD class IAES { public: - virtual void encrypt(uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len)=0; - virtual void decrypt(uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len)=0; + virtual void encrypt(uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, uint64_t len)=0; + virtual void decrypt(uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, uint64_t len)=0; virtual void setKey(const uint8_t* key)=0; }; diff --git a/lib/DiscBase.cpp b/lib/DiscBase.cpp index e321aba..696bde3 100644 --- a/lib/DiscBase.cpp +++ b/lib/DiscBase.cpp @@ -2,6 +2,10 @@ #include "DiscBase.hpp" #include "IFileIO.hpp" +#ifndef _WIN32 +#include +#endif + namespace NOD { @@ -52,8 +56,8 @@ void DiscBase::IPartition::Node::extractToDirectory(const std::string& basePath, { if (mkdir(path.c_str(), 0755) && errno != EEXIST) throw std::runtime_error("unable to mkdir '" + path + "'"); - for (DiscBase::IPartition::Node& subnode : *this) - subnode.extractToDirectory(path); + for (Node& subnode : *this) + subnode.extractToDirectory(path, force); } else if (m_kind == NODE_FILE) { @@ -68,4 +72,52 @@ void DiscBase::IPartition::Node::extractToDirectory(const std::string& basePath, } } +bool DiscBase::IPartition::_recursivePathOfNode(const std::string& basePath, + const Node& refNode, + const Node& curNode, + std::string& result) +{ + std::string path = basePath + "/" + curNode.getName(); + if (&refNode == &curNode) + { + result = path; + return true; + } + else if (curNode.m_kind == Node::NODE_DIRECTORY) + { + for (const Node& subnode : curNode) + if (_recursivePathOfNode(path, refNode, subnode, result)) + break; + } + return false; +} + +std::string DiscBase::IPartition::pathOfNode(const Node& node) +{ + std::string result; + _recursivePathOfNode("", node, m_nodes[0], result); + return result; +} + +void DiscBase::IPartition::extractToDirectory(const std::string& path, bool force) +{ + struct stat theStat; + if (mkdir(path.c_str(), 0755) && errno != EEXIST) + throw std::runtime_error("unable to mkdir '" + path + "'"); + + /* Extract Apploader */ + std::string apploaderPath = path + "/apploader.bin"; + if (force || stat(apploaderPath.c_str(), &theStat)) + { + std::unique_ptr buf(new uint8_t[m_apploaderSz]); + std::unique_ptr rs = beginReadStream(0x2440); + rs->read(buf.get(), m_apploaderSz); + std::unique_ptr ws = NewFileIO(path + "/apploader.bin")->beginWriteStream(); + ws->write(buf.get(), m_apploaderSz); + } + + /* Extract Filesystem */ + m_nodes[0].extractToDirectory(path, force); +} + } diff --git a/lib/DiscGCN.cpp b/lib/DiscGCN.cpp index bc0572f..91d04ff 100644 --- a/lib/DiscGCN.cpp +++ b/lib/DiscGCN.cpp @@ -6,7 +6,7 @@ namespace NOD class PartitionGCN : public DiscBase::IPartition { public: - PartitionGCN(const DiscGCN& parent, Kind kind, size_t offset) + PartitionGCN(const DiscGCN& parent, Kind kind, uint64_t offset) : IPartition(parent, kind, offset) { /* GCN-specific header reads */ @@ -16,6 +16,9 @@ public: m_dolOff = SBig(vals[0]); m_fstOff = SBig(vals[1]); m_fstSz = SBig(vals[2]); + s->seek(0x2440 + 0x14); + s->read(vals, 8); + m_apploaderSz = SBig(vals[0]) + SBig(vals[1]); /* Yay files!! */ parseFST(*s.get()); @@ -27,16 +30,16 @@ public: std::unique_ptr m_dio; public: - PartReadStream(const PartitionGCN& parent, size_t offset) + PartReadStream(const PartitionGCN& parent, uint64_t offset) : m_parent(parent) {m_dio = m_parent.m_parent.getDiscIO().beginReadStream(offset);} - void seek(size_t offset, int whence) + void seek(int64_t offset, int whence) {m_dio->seek(offset, whence);} - size_t read(void* buf, size_t length) + uint64_t read(void* buf, uint64_t length) {return m_dio->read(buf, length);} }; - std::unique_ptr beginReadStream(size_t offset) const + std::unique_ptr beginReadStream(uint64_t offset) const { return std::unique_ptr(new PartReadStream(*this, offset)); } diff --git a/lib/DiscIOISO.cpp b/lib/DiscIOISO.cpp index 6a32eec..556f1b8 100644 --- a/lib/DiscIOISO.cpp +++ b/lib/DiscIOISO.cpp @@ -20,12 +20,12 @@ public: : fp(fpin) {} ~ReadStream() {fclose(fp);} public: - size_t read(void* buf, size_t length) + uint64_t read(void* buf, uint64_t length) {return fread(buf, 1, length, fp);} - void seek(size_t offset, int whence) - {fseek(fp, offset, whence);} + void seek(int64_t offset, int whence) + {fseeko(fp, offset, whence);} }; - std::unique_ptr beginReadStream(size_t offset) const + std::unique_ptr beginReadStream(uint64_t offset) const { FILE* fp = fopen(filepath.c_str(), "rb"); if (!fp) @@ -33,7 +33,7 @@ public: throw std::runtime_error("Unable to open '" + filepath + "' for reading"); return std::unique_ptr(); } - fseek(fp, offset, SEEK_SET); + fseeko(fp, offset, SEEK_SET); return std::unique_ptr(new ReadStream(fp)); } @@ -45,10 +45,10 @@ public: : fp(fpin) {} ~WriteStream() {fclose(fp);} public: - size_t write(void* buf, size_t length) + uint64_t write(void* buf, uint64_t length) {return fwrite(buf, 1, length, fp);} }; - std::unique_ptr beginWriteStream(size_t offset) const + std::unique_ptr beginWriteStream(uint64_t offset) const { FILE* fp = fopen(filepath.c_str(), "wb"); if (!fp) @@ -56,7 +56,7 @@ public: throw std::runtime_error("Unable to open '" + filepath + "' for writing"); return std::unique_ptr(); } - fseek(fp, offset, SEEK_SET); + fseeko(fp, offset, SEEK_SET); return std::unique_ptr(new WriteStream(fp)); } }; diff --git a/lib/DiscIOWBFS.cpp b/lib/DiscIOWBFS.cpp index c8cbb83..0069fa8 100644 --- a/lib/DiscIOWBFS.cpp +++ b/lib/DiscIOWBFS.cpp @@ -20,12 +20,12 @@ public: : fp(fpin) {} ~ReadStream() {fclose(fp);} public: - size_t read(void* buf, size_t length) + uint64_t read(void* buf, uint64_t length) {return fread(buf, 1, length, fp);} - void seek(size_t offset, int whence) - {fseek(fp, offset, whence);} + void seek(int64_t offset, int whence) + {fseeko(fp, offset, whence);} }; - std::unique_ptr beginReadStream(size_t offset) const + std::unique_ptr beginReadStream(uint64_t offset) const { FILE* fp = fopen(filepath.c_str(), "rb"); if (!fp) @@ -33,7 +33,7 @@ public: throw std::runtime_error("Unable to open '" + filepath + "' for reading"); return std::unique_ptr(); } - fseek(fp, offset, SEEK_SET); + fseeko(fp, offset, SEEK_SET); return std::unique_ptr(new ReadStream(fp)); } @@ -45,10 +45,10 @@ public: : fp(fpin) {} ~WriteStream() {fclose(fp);} public: - size_t write(void* buf, size_t length) + uint64_t write(void* buf, uint64_t length) {return fwrite(buf, 1, length, fp);} }; - std::unique_ptr beginWriteStream(size_t offset) const + std::unique_ptr beginWriteStream(uint64_t offset) const { FILE* fp = fopen(filepath.c_str(), "wb"); if (!fp) @@ -56,7 +56,7 @@ public: throw std::runtime_error("Unable to open '" + filepath + "' for writing"); return std::unique_ptr(); } - fseek(fp, offset, SEEK_SET); + fseeko(fp, offset, SEEK_SET); return std::unique_ptr(new WriteStream(fp)); } }; diff --git a/lib/DiscWii.cpp b/lib/DiscWii.cpp index 238aad5..fee5387 100644 --- a/lib/DiscWii.cpp +++ b/lib/DiscWii.cpp @@ -6,6 +6,7 @@ namespace NOD { +/* Not much of a secret anymore I suppose */ static const uint8_t COMMON_KEY[] = {0xeb, 0xe4, 0x2a, 0x22, 0x5e, 0x85, 0x93, 0xe4, 0x48, 0xd9, 0xc5, 0x45, @@ -124,7 +125,7 @@ class PartitionWii : public DiscBase::IPartition titleVersion = SBig(titleVersion); numContents = SBig(numContents); bootIdx = SBig(bootIdx); - for (size_t c=0 ; c s = parent.getDiscIO().beginReadStream(offset); @@ -233,6 +234,9 @@ public: m_dolOff = SBig(vals[0]) << 2; m_fstOff = SBig(vals[1]) << 2; m_fstSz = SBig(vals[2]) << 2; + ds->seek(0x2440 + 0x14); + ds->read(vals, 8); + m_apploaderSz = SBig(vals[0]) + SBig(vals[1]); /* Yay files!! */ parseFST(*ds.get()); @@ -242,8 +246,8 @@ public: { std::unique_ptr m_aes; const PartitionWii& m_parent; - size_t m_baseOffset; - size_t m_offset; + uint64_t m_baseOffset; + uint64_t m_offset; std::unique_ptr m_dio; size_t m_curBlock = SIZE_MAX; @@ -256,7 +260,7 @@ public: m_aes->decrypt(&m_encBuf[0x3d0], &m_encBuf[0x400], m_decBuf, 0x7c00); } public: - PartReadStream(const PartitionWii& parent, size_t baseOffset, size_t offset) + PartReadStream(const PartitionWii& parent, uint64_t baseOffset, uint64_t offset) : m_aes(NewAES()), m_parent(parent), m_baseOffset(baseOffset), m_offset(offset) { m_aes->setKey(parent.m_decKey); @@ -265,7 +269,7 @@ public: decryptBlock(); m_curBlock = block; } - void seek(size_t offset, int whence) + void seek(int64_t offset, int whence) { if (whence == SEEK_SET) m_offset = offset; @@ -281,11 +285,11 @@ public: m_curBlock = block; } } - size_t read(void* buf, size_t length) + uint64_t read(void* buf, uint64_t length) { size_t block = m_offset / 0x7c00; size_t cacheOffset = m_offset % 0x7c00; - size_t cacheSize; + uint64_t cacheSize; uint8_t* dst = (uint8_t*)buf; while (length) @@ -312,10 +316,12 @@ public: } }; - std::unique_ptr beginReadStream(size_t offset) const + std::unique_ptr beginReadStream(uint64_t offset) const { return std::unique_ptr(new PartReadStream(*this, m_dataOff, offset)); } + + uint64_t normalizeOffset(uint64_t anOffset) const {return anOffset << 2;} }; DiscWii::DiscWii(std::unique_ptr&& dio) diff --git a/lib/FileIOFILE.cpp b/lib/FileIOFILE.cpp index 01fc7ff..38d1f16 100644 --- a/lib/FileIOFILE.cpp +++ b/lib/FileIOFILE.cpp @@ -18,13 +18,13 @@ public: FileIOFILE(const std::string& path) : m_path(path) {} - size_t size() + uint64_t size() { FILE* fp = fopen(m_path.c_str(), "rb"); if (!fp) return 0; - fseek(fp, 0, SEEK_END); - size_t result = ftell(fp); + fseeko(fp, 0, SEEK_END); + uint64_t result = ftello(fp); fclose(fp); return result; } @@ -40,13 +40,15 @@ public: throw std::runtime_error("unable to open '" + path + "' for writing"); } ~WriteStream() {fclose(fp);} - size_t copyFromDisc(IPartReadStream& discio, size_t length) + uint64_t write(void* buf, uint64_t length) + {return fwrite(buf, 1, length, fp);} + uint64_t copyFromDisc(IPartReadStream& discio, uint64_t length) { - size_t read = 0; + uint64_t read = 0; while (length) { - size_t thisSz = MIN(0x7c00, length); - size_t readSz = discio.read(buf, thisSz); + uint64_t thisSz = MIN(0x7c00, length); + uint64_t readSz = discio.read(buf, thisSz); if (thisSz != readSz) throw std::runtime_error("unable to read enough from disc"); if (fwrite(buf, 1, readSz, fp) != readSz) @@ -71,12 +73,14 @@ public: throw std::runtime_error("unable to open '" + path + "' for reading"); } ~ReadStream() {fclose(fp);} - size_t copyToDisc(IPartWriteStream& discio, size_t length) + uint64_t read(void* buf, uint64_t length) + {return fread(buf, 1, length, fp);} + uint64_t copyToDisc(IPartWriteStream& discio, uint64_t length) { - size_t written = 0; + uint64_t written = 0; while (length) { - size_t thisSz = MIN(0x7c00, length); + uint64_t thisSz = MIN(0x7c00, length); if (fread(buf, 1, thisSz, fp) != thisSz) throw std::runtime_error("unable to read enough from file"); if (discio.write(buf, thisSz) != thisSz) diff --git a/lib/NODLib.cpp b/lib/NODLib.cpp index 53dd96f..edd38b5 100644 --- a/lib/NODLib.cpp +++ b/lib/NODLib.cpp @@ -29,7 +29,7 @@ std::unique_ptr OpenDiscFromImage(const char* path, bool& isWii) } else { - fseek(fp, 0x18, SEEK_SET); + fseeko(fp, 0x18, SEEK_SET); fread(&magic, 1, 4, fp); magic = NOD::SBig(magic); if (magic == 0x5D1C9EA3) diff --git a/lib/aes.cpp b/lib/aes.cpp index ff7a16d..946973f 100644 --- a/lib/aes.cpp +++ b/lib/aes.cpp @@ -76,8 +76,8 @@ protected: void _decrypt(uint8_t* buff); public: - void encrypt(uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len); - void decrypt(uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len); + void encrypt(uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, uint64_t len); + void decrypt(uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, uint64_t len); void setKey(const uint8_t* key); }; @@ -440,7 +440,7 @@ void SoftwareAES::decrypt(uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, si } // CBC mode encryption -void SoftwareAES::encrypt(uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len) +void SoftwareAES::encrypt(uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, uint64_t len) { uint8_t block[16]; unsigned int blockno = 0, i; @@ -485,10 +485,10 @@ class NiAES : public IAES __m128i m_ekey[11]; __m128i m_dkey[11]; public: - void encrypt(uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len) + void encrypt(uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, uint64_t len) { __m128i feedback,data; - size_t i,j; + uint64_t i,j; if (len%16) len = len/16+1; else @@ -505,10 +505,10 @@ public: _mm_storeu_si128(&((__m128i*)outbuf)[i], feedback); } } - void decrypt(uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len) + void decrypt(uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, uint64_t len) { __m128i data,feedback,last_in; - size_t i,j; + uint64_t i,j; if (len%16) len = len/16+1; else