working file extractor; removed hard-coded paths

This commit is contained in:
Jack Andersen 2015-06-30 09:38:51 -10:00
parent 293b7c0ce6
commit a1b2a262bf
13 changed files with 194 additions and 100 deletions

View File

@ -2,34 +2,53 @@
#include <string.h>
#include "NODLib.hpp"
static void printHelp()
{
fprintf(stderr, "Usage:\n"
" nodlib extract [-f] <image-in> [<dir-out>]\n"
" nodlib make <dir-in> [<image-out>]\n");
}
int main(int argc, char* argv[])
{
if (argc < 2)
if (argc < 3)
{
fprintf(stderr, "Usage: nodlib <image-in>\n");
printHelp();
return -1;
}
std::unique_ptr<NOD::DiscBase> 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 ; a<argc ; ++a)
{
for (const NOD::DiscBase::IPartition::Node& node : dataPart->getFSTRoot())
{
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<NOD::DiscBase> 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;

View File

@ -5,6 +5,7 @@
#include <memory>
#include <string>
#include <stdio.h>
#include <stdint.h>
#include "Util.hpp"
#include "IDiscIO.hpp"
#include "IFileIO.hpp"
@ -71,8 +72,8 @@ public:
Kind m_kind;
std::unique_ptr<IFileIO> m_hddFile;
size_t m_discOffset;
size_t m_discLength;
uint64_t m_discOffset;
uint64_t m_discLength;
std::string m_name;
std::vector<Node>::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<Node> 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<IPartReadStream> beginReadStream(size_t offset=0) const=0;
virtual std::unique_ptr<IPartReadStream> 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<IPartition>& 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<IPartition>& part : m_partitions)
if (part->getKind() == IPartition::PART_UPDATE)

View File

@ -4,6 +4,7 @@
#include <memory>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
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<IReadStream> beginReadStream(size_t offset=0) const=0;
virtual std::unique_ptr<IReadStream> 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<IWriteStream> beginWriteStream(size_t offset=0) const=0;
virtual std::unique_ptr<IWriteStream> 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;
};
}

View File

@ -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<IWriteStream> 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<IReadStream> beginReadStream() const=0;
};
std::unique_ptr<IFileIO> NewFileIO(const std::string& path);
std::unique_ptr<IFileIO> NewMemIO(void* buf, size_t size);
std::unique_ptr<IFileIO> NewMemIO(void* buf, uint64_t size);
}

View File

@ -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;
};

View File

@ -2,6 +2,10 @@
#include "DiscBase.hpp"
#include "IFileIO.hpp"
#ifndef _WIN32
#include <unistd.h>
#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<uint8_t[]> buf(new uint8_t[m_apploaderSz]);
std::unique_ptr<IPartReadStream> rs = beginReadStream(0x2440);
rs->read(buf.get(), m_apploaderSz);
std::unique_ptr<IFileIO::IWriteStream> ws = NewFileIO(path + "/apploader.bin")->beginWriteStream();
ws->write(buf.get(), m_apploaderSz);
}
/* Extract Filesystem */
m_nodes[0].extractToDirectory(path, force);
}
}

View File

@ -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<IDiscIO::IReadStream> 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<IPartReadStream> beginReadStream(size_t offset) const
std::unique_ptr<IPartReadStream> beginReadStream(uint64_t offset) const
{
return std::unique_ptr<IPartReadStream>(new PartReadStream(*this, offset));
}

View File

@ -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<IReadStream> beginReadStream(size_t offset) const
std::unique_ptr<IReadStream> 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<IReadStream>();
}
fseek(fp, offset, SEEK_SET);
fseeko(fp, offset, SEEK_SET);
return std::unique_ptr<IReadStream>(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<IWriteStream> beginWriteStream(size_t offset) const
std::unique_ptr<IWriteStream> 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<IWriteStream>();
}
fseek(fp, offset, SEEK_SET);
fseeko(fp, offset, SEEK_SET);
return std::unique_ptr<IWriteStream>(new WriteStream(fp));
}
};

View File

@ -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<IReadStream> beginReadStream(size_t offset) const
std::unique_ptr<IReadStream> 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<IReadStream>();
}
fseek(fp, offset, SEEK_SET);
fseeko(fp, offset, SEEK_SET);
return std::unique_ptr<IReadStream>(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<IWriteStream> beginWriteStream(size_t offset) const
std::unique_ptr<IWriteStream> 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<IWriteStream>();
}
fseek(fp, offset, SEEK_SET);
fseeko(fp, offset, SEEK_SET);
return std::unique_ptr<IWriteStream>(new WriteStream(fp));
}
};

View File

@ -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<numContents ; ++c)
for (uint16_t c=0 ; c<numContents ; ++c)
{
contents.emplace_back();
contents.back().read(s);
@ -175,11 +176,11 @@ class PartitionWii : public DiscBase::IPartition
Certificate m_tmdCert;
Certificate m_ticketCert;
size_t m_dataOff;
uint64_t m_dataOff;
uint8_t m_decKey[16];
public:
PartitionWii(const DiscWii& parent, Kind kind, size_t offset)
PartitionWii(const DiscWii& parent, Kind kind, uint64_t offset)
: IPartition(parent, kind, offset)
{
std::unique_ptr<IDiscIO::IReadStream> 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<IAES> 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<IDiscIO::IReadStream> 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<IPartReadStream> beginReadStream(size_t offset) const
std::unique_ptr<IPartReadStream> beginReadStream(uint64_t offset) const
{
return std::unique_ptr<IPartReadStream>(new PartReadStream(*this, m_dataOff, offset));
}
uint64_t normalizeOffset(uint64_t anOffset) const {return anOffset << 2;}
};
DiscWii::DiscWii(std::unique_ptr<IDiscIO>&& dio)

View File

@ -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)

View File

@ -29,7 +29,7 @@ std::unique_ptr<DiscBase> 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)

View File

@ -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