mirror of https://github.com/AxioDL/nod.git
Refactor of extracted directory structure and API simplification
This commit is contained in:
parent
e49568ac83
commit
41148a1368
13
README.md
13
README.md
|
@ -51,12 +51,12 @@ auto progFunc = [&](size_t idx, const nod::SystemString& name, size_t bytes)
|
|||
};
|
||||
|
||||
/* Making a GCN image */
|
||||
nod::DiscBuilderGCN b(isoOutPath, gameID, gameTitle, dolLoadAddress, progFunc);
|
||||
ret = b.buildFromDirectory(fsRootDirPath, bootDolPath, apploaderPath);
|
||||
nod::DiscBuilderGCN b(isoOutPath, progFunc);
|
||||
ret = b.buildFromDirectory(fsRootDirPath);
|
||||
|
||||
/* Making a Wii image */
|
||||
nod::DiscBuilderWii b(isoOutPath, gameID, gameTitle, dualLayer, progFunc);
|
||||
ret = b.buildFromDirectory(fsRootDirPath, bootDolPath, apploaderPath, partitionHeadPath);
|
||||
nod::DiscBuilderWii b(isoOutPath, dualLayer, progFunc);
|
||||
ret = b.buildFromDirectory(fsRootDirPath);
|
||||
```
|
||||
|
||||
Wii images are fakesigned using a commonly-applied [signing bug](http://wiibrew.org/wiki/Signing_bug).
|
||||
|
@ -75,7 +75,6 @@ An extract/repack works like so:
|
|||
>$ cd <dir-out>
|
||||
|
||||
# Then one of:
|
||||
>$ nodtool makegcn <gameid> <game-title> fsroot boot.dol apploader.bin [<image-out>]
|
||||
>$ nodtool makewiisl <gameid> <game-title> fsroot boot.dol apploader.bin partition_head.bin [<image-out>]
|
||||
>$ nodtool makewiidl <gameid> <game-title> fsroot boot.dol apploader.bin partition_head.bin [<image-out>]
|
||||
>$ nodtool makegcn fsroot [<image-out>]
|
||||
>$ nodtool makewii fsroot [<image-out>]
|
||||
```
|
||||
|
|
124
driver/main.cpp
124
driver/main.cpp
|
@ -7,8 +7,8 @@ static void printHelp()
|
|||
{
|
||||
fprintf(stderr, "Usage:\n"
|
||||
" nodtool extract [-f] <image-in> [<dir-out>]\n"
|
||||
" nodtool makegcn <gameid> <game-title> <fsroot-in> <dol-in> <apploader-in> [<image-out>]\n"
|
||||
" nodtool makewii <gameid> <game-title> <fsroot-in> <dol-in> <apploader-in> <parthead-in> [<image-out>]\n"
|
||||
" nodtool makegcn <fsroot-in> [<image-out>]\n"
|
||||
" nodtool makewii <fsroot-in> [<image-out>]\n"
|
||||
" nodtool mergegcn <fsroot-in> <image-in> [<image-out>]\n"
|
||||
" nodtool mergewii <fsroot-in> <image-in> [<image-out>]\n");
|
||||
}
|
||||
|
@ -26,8 +26,8 @@ int main(int argc, char* argv[])
|
|||
#endif
|
||||
{
|
||||
if (argc < 3 ||
|
||||
(!strcasecmp(argv[1], _S("makegcn")) && argc < 7) ||
|
||||
(!strcasecmp(argv[1], _S("makewii")) && argc < 8) ||
|
||||
(!strcasecmp(argv[1], _S("makegcn")) && argc < 3) ||
|
||||
(!strcasecmp(argv[1], _S("makewii")) && argc < 3) ||
|
||||
(!strcasecmp(argv[1], _S("mergegcn")) && argc < 4) ||
|
||||
(!strcasecmp(argv[1], _S("mergewii")) && argc < 4))
|
||||
{
|
||||
|
@ -39,9 +39,12 @@ int main(int argc, char* argv[])
|
|||
logvisor::RegisterStandardExceptions();
|
||||
logvisor::RegisterConsoleLogger();
|
||||
|
||||
nod::ExtractionContext ctx = { true, true, [&](const std::string& str, float c){
|
||||
fprintf(stderr, "Current node: %s, Extraction %g%% Complete\n", str.c_str(), c * 100.f);
|
||||
}};
|
||||
bool verbose = false;
|
||||
nod::ExtractionContext ctx = {true,
|
||||
[&](const std::string& str, float c) {
|
||||
if (verbose)
|
||||
fprintf(stderr, "Current node: %s, Extraction %g%% Complete\n", str.c_str(), c * 100.f);
|
||||
}};
|
||||
const nod::SystemChar* inDir = nullptr;
|
||||
const nod::SystemChar* outDir = _S(".");
|
||||
|
||||
|
@ -50,7 +53,7 @@ int main(int argc, char* argv[])
|
|||
if (argv[a][0] == '-' && argv[a][1] == 'f')
|
||||
ctx.force = true;
|
||||
else if (argv[a][0] == '-' && argv[a][1] == 'v')
|
||||
ctx.verbose = true;
|
||||
verbose = true;
|
||||
|
||||
else if (!inDir)
|
||||
inDir = argv[a];
|
||||
|
@ -77,10 +80,6 @@ int main(int argc, char* argv[])
|
|||
|
||||
nod::Mkdir(outDir, 0755);
|
||||
|
||||
if (isWii)
|
||||
static_cast<nod::DiscWii&>(*disc).writeOutDataPartitionHeader(
|
||||
(nod::SystemString(outDir) + _S("/partition_head.bin")).c_str());
|
||||
|
||||
nod::Partition* dataPart = disc->getDataPartition();
|
||||
if (!dataPart)
|
||||
return 1;
|
||||
|
@ -90,59 +89,30 @@ int main(int argc, char* argv[])
|
|||
}
|
||||
else if (!strcasecmp(argv[1], _S("makegcn")))
|
||||
{
|
||||
#if NOD_UCS2
|
||||
if (wcslen(argv[2]) < 6)
|
||||
{
|
||||
nod::LogModule.report(logvisor::Error, _S("game-id is not at least 6 characters"));
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
if (strlen(argv[2]) < 6)
|
||||
{
|
||||
nod::LogModule.report(logvisor::Error, _S("game-id is not at least 6 characters"));
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Pre-validate paths */
|
||||
/* Pre-validate path */
|
||||
nod::Sstat theStat;
|
||||
if (nod::Stat(argv[4], &theStat) || !S_ISDIR(theStat.st_mode))
|
||||
if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode))
|
||||
{
|
||||
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as directory"), argv[4]);
|
||||
return 1;
|
||||
}
|
||||
if (nod::Stat(argv[5], &theStat) || !S_ISREG(theStat.st_mode))
|
||||
{
|
||||
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as file"), argv[5]);
|
||||
return 1;
|
||||
}
|
||||
if (nod::Stat(argv[6], &theStat) || !S_ISREG(theStat.st_mode))
|
||||
{
|
||||
nod::LogModule.report(logvisor::Error, "unable to stat %s as file", argv[6]);
|
||||
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as directory"), argv[2]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
nod::SystemString gameIdSys(argv[2]);
|
||||
nod::SystemUTF8View gameId(gameIdSys);
|
||||
nod::SystemString gameTitleSys(argv[3]);
|
||||
nod::SystemUTF8View gameTitle(gameTitleSys);
|
||||
|
||||
if (nod::DiscBuilderGCN::CalculateTotalSizeRequired(argv[4], argv[5]) == -1)
|
||||
if (nod::DiscBuilderGCN::CalculateTotalSizeRequired(argv[2]) == -1)
|
||||
return 1;
|
||||
|
||||
nod::EBuildResult ret;
|
||||
|
||||
if (argc < 8)
|
||||
if (argc < 4)
|
||||
{
|
||||
nod::SystemString outPath(argv[4]);
|
||||
nod::SystemString outPath(argv[2]);
|
||||
outPath.append(_S(".iso"));
|
||||
nod::DiscBuilderGCN b(outPath.c_str(), gameId.utf8_str().c_str(), gameTitle.utf8_str().c_str(), 0x0003EB60, progFunc);
|
||||
ret = b.buildFromDirectory(argv[4], argv[5], argv[6]);
|
||||
nod::DiscBuilderGCN b(outPath.c_str(), progFunc);
|
||||
ret = b.buildFromDirectory(argv[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
nod::DiscBuilderGCN b(argv[7], gameId.utf8_str().c_str(), gameTitle.utf8_str().c_str(), 0x0003EB60, progFunc);
|
||||
ret = b.buildFromDirectory(argv[4], argv[5], argv[6]);
|
||||
nod::DiscBuilderGCN b(argv[3], progFunc);
|
||||
ret = b.buildFromDirectory(argv[2]);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
@ -151,65 +121,31 @@ int main(int argc, char* argv[])
|
|||
}
|
||||
else if (!strcasecmp(argv[1], _S("makewii")))
|
||||
{
|
||||
#if NOD_UCS2
|
||||
if (wcslen(argv[2]) < 6)
|
||||
{
|
||||
nod::LogModule.report(logvisor::Error, _S("game-id is not at least 6 characters"));
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
if (strlen(argv[2]) < 6)
|
||||
{
|
||||
nod::LogModule.report(logvisor::Error, _S("game-id is not at least 6 characters"));
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Pre-validate paths */
|
||||
/* Pre-validate path */
|
||||
nod::Sstat theStat;
|
||||
if (nod::Stat(argv[4], &theStat) || !S_ISDIR(theStat.st_mode))
|
||||
if (nod::Stat(argv[2], &theStat) || !S_ISDIR(theStat.st_mode))
|
||||
{
|
||||
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as directory"), argv[4]);
|
||||
return 1;
|
||||
}
|
||||
if (nod::Stat(argv[5], &theStat) || !S_ISREG(theStat.st_mode))
|
||||
{
|
||||
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as file"), argv[5]);
|
||||
return 1;
|
||||
}
|
||||
if (nod::Stat(argv[6], &theStat) || !S_ISREG(theStat.st_mode))
|
||||
{
|
||||
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as file"), argv[6]);
|
||||
return 1;
|
||||
}
|
||||
if (nod::Stat(argv[7], &theStat) || !S_ISREG(theStat.st_mode))
|
||||
{
|
||||
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as file"), argv[7]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
nod::SystemString gameIdSys(argv[2]);
|
||||
nod::SystemUTF8View gameId(gameIdSys);
|
||||
nod::SystemString gameTitleSys(argv[3]);
|
||||
nod::SystemUTF8View gameTitle(gameTitleSys);
|
||||
|
||||
bool dual = false;
|
||||
if (nod::DiscBuilderWii::CalculateTotalSizeRequired(argv[4], argv[5], dual) == -1)
|
||||
if (nod::DiscBuilderWii::CalculateTotalSizeRequired(argv[2], dual) == -1)
|
||||
return 1;
|
||||
|
||||
nod::EBuildResult ret;
|
||||
|
||||
if (argc < 9)
|
||||
if (argc < 4)
|
||||
{
|
||||
nod::SystemString outPath(argv[4]);
|
||||
nod::SystemString outPath(argv[2]);
|
||||
outPath.append(_S(".iso"));
|
||||
nod::DiscBuilderWii b(outPath.c_str(), gameId.utf8_str().c_str(), gameTitle.utf8_str().c_str(), dual, progFunc);
|
||||
ret = b.buildFromDirectory(argv[4], argv[5], argv[6], argv[7]);
|
||||
nod::DiscBuilderWii b(outPath.c_str(), dual, progFunc);
|
||||
ret = b.buildFromDirectory(argv[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
nod::DiscBuilderWii b(argv[8], gameId.utf8_str().c_str(), gameTitle.utf8_str().c_str(), dual, progFunc);
|
||||
ret = b.buildFromDirectory(argv[4], argv[5], argv[6], argv[7]);
|
||||
nod::DiscBuilderWii b(argv[3], dual, progFunc);
|
||||
ret = b.buildFromDirectory(argv[2]);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
|
|
@ -67,60 +67,126 @@ struct Header
|
|||
uint32_t m_debugMonOff;
|
||||
uint32_t m_debugLoadAddr;
|
||||
char m_unk3[0x18];
|
||||
uint32_t m_dolOff;
|
||||
uint32_t m_fstOff;
|
||||
uint32_t m_fstSz;
|
||||
uint32_t m_fstMaxSz;
|
||||
uint32_t m_fstMemoryAddress;
|
||||
uint32_t m_userPosition;
|
||||
uint32_t m_userSz;
|
||||
uint8_t padding1[4];
|
||||
|
||||
Header() = default;
|
||||
Header(IDiscIO& dio, bool& err)
|
||||
{
|
||||
std::unique_ptr<IDiscIO::IReadStream> s = dio.beginReadStream(0);
|
||||
if (!s)
|
||||
auto rs = dio.beginReadStream();
|
||||
if (!rs)
|
||||
{
|
||||
err = true;
|
||||
return;
|
||||
}
|
||||
s->read(this, sizeof(*this));
|
||||
read(*rs);
|
||||
}
|
||||
|
||||
void read(IReadStream& s)
|
||||
{
|
||||
memset(this, 0, sizeof(*this));
|
||||
s.read(this, sizeof(*this));
|
||||
m_wiiMagic = SBig(m_wiiMagic);
|
||||
m_gcnMagic = SBig(m_gcnMagic);
|
||||
m_debugMonOff = SBig(m_debugMonOff);
|
||||
m_debugLoadAddr = SBig(m_debugLoadAddr);
|
||||
m_dolOff = SBig(m_dolOff);
|
||||
m_fstOff = SBig(m_fstOff);
|
||||
m_fstSz = SBig(m_fstSz);
|
||||
m_fstMaxSz = SBig(m_fstMaxSz);
|
||||
m_fstMemoryAddress = SBig(m_fstMemoryAddress);
|
||||
m_userPosition = SBig(m_userPosition);
|
||||
m_userSz = SBig(m_userSz);
|
||||
}
|
||||
|
||||
Header(const char gameID[6], const char* gameTitle, bool wii, char discNum=0, char discVersion=0,
|
||||
char audioStreaming=1, char streamBufSz=0)
|
||||
{
|
||||
memset(this, 0, sizeof(*this));
|
||||
memcpy(m_gameID, gameID, 6);
|
||||
strncpy(m_gameTitle, gameTitle, 64);
|
||||
m_discNum = discNum;
|
||||
m_discVersion = discVersion;
|
||||
m_audioStreaming = audioStreaming;
|
||||
m_streamBufSz = streamBufSz;
|
||||
if (wii)
|
||||
m_wiiMagic = 0x5D1C9EA3;
|
||||
else
|
||||
m_gcnMagic = 0xC2339F3D;
|
||||
}
|
||||
|
||||
template <class WriteStream>
|
||||
void write(WriteStream& ws) const
|
||||
void write(IWriteStream& ws) const
|
||||
{
|
||||
Header hs(*this);
|
||||
hs.m_wiiMagic = SBig(hs.m_wiiMagic);
|
||||
hs.m_gcnMagic = SBig(hs.m_gcnMagic);
|
||||
hs.m_debugMonOff = SBig(hs.m_debugMonOff);
|
||||
hs.m_debugLoadAddr = SBig(hs.m_debugLoadAddr);
|
||||
hs.m_dolOff = SBig(hs.m_dolOff);
|
||||
hs.m_fstOff = SBig(hs.m_fstOff);
|
||||
hs.m_fstSz = SBig(hs.m_fstSz);
|
||||
hs.m_fstMaxSz = SBig(hs.m_fstMaxSz);
|
||||
hs.m_fstMemoryAddress = SBig(hs.m_fstMemoryAddress);
|
||||
hs.m_userPosition = SBig(hs.m_userPosition);
|
||||
hs.m_userSz = SBig(hs.m_userSz);
|
||||
ws.write(&hs, sizeof(hs));
|
||||
}
|
||||
};
|
||||
|
||||
/* Currently only kept for dolphin compatibility */
|
||||
struct BI2Header
|
||||
{
|
||||
int32_t m_debugMonitorSize;
|
||||
int32_t m_simMemSize;
|
||||
uint32_t m_argOffset;
|
||||
uint32_t m_debugFlag;
|
||||
uint32_t m_trkAddress;
|
||||
uint32_t m_trkSz;
|
||||
uint32_t m_countryCode;
|
||||
uint32_t m_unk1;
|
||||
uint32_t m_unk2;
|
||||
uint32_t m_unk3;
|
||||
uint32_t m_dolLimit;
|
||||
uint32_t m_unk4;
|
||||
uint8_t padding2[0x1FD0];
|
||||
|
||||
void read(IReadStream& rs)
|
||||
{
|
||||
memset(this, 0, sizeof(*this));
|
||||
rs.read(this, sizeof(*this));
|
||||
m_debugMonitorSize = SBig(m_debugMonitorSize);
|
||||
m_simMemSize = SBig(m_simMemSize);
|
||||
m_argOffset = SBig(m_argOffset);
|
||||
m_debugFlag = SBig(m_debugFlag);
|
||||
m_trkAddress = SBig(m_trkAddress);
|
||||
m_trkSz = SBig(m_trkSz);
|
||||
m_countryCode = SBig(m_countryCode);
|
||||
m_unk1 = SBig(m_unk1);
|
||||
m_unk2 = SBig(m_unk2);
|
||||
m_unk3 = SBig(m_unk3);
|
||||
m_dolLimit = SBig(m_dolLimit);
|
||||
m_unk4 = SBig(m_unk4);
|
||||
}
|
||||
|
||||
void write(IWriteStream& ws) const
|
||||
{
|
||||
BI2Header h = *this;
|
||||
h.m_debugMonitorSize = SBig(h.m_debugMonitorSize);
|
||||
h.m_simMemSize = SBig(h.m_simMemSize);
|
||||
h.m_argOffset = SBig(h.m_argOffset);
|
||||
h.m_debugFlag = SBig(h.m_debugFlag);
|
||||
h.m_trkAddress = SBig(h.m_trkAddress);
|
||||
h.m_trkSz = SBig(h.m_trkSz);
|
||||
h.m_countryCode = SBig(h.m_countryCode);
|
||||
h.m_unk1 = SBig(h.m_unk1);
|
||||
h.m_unk2 = SBig(h.m_unk2);
|
||||
h.m_unk3 = SBig(h.m_unk3);
|
||||
h.m_dolLimit = SBig(h.m_dolLimit);
|
||||
h.m_unk4 = SBig(h.m_unk4);
|
||||
ws.write(&h, sizeof(h));
|
||||
}
|
||||
};
|
||||
|
||||
struct ExtractionContext;
|
||||
class DiscBase
|
||||
{
|
||||
public:
|
||||
virtual ~DiscBase() {}
|
||||
virtual ~DiscBase() = default;
|
||||
|
||||
class IPartition
|
||||
{
|
||||
public:
|
||||
virtual ~IPartition() {}
|
||||
virtual ~IPartition() = default;
|
||||
enum class Kind : uint32_t
|
||||
{
|
||||
Data,
|
||||
|
@ -140,32 +206,6 @@ public:
|
|||
uint32_t entryPoint;
|
||||
};
|
||||
|
||||
/* Currently only kept for dolphin compatibility*/
|
||||
struct BI2Header
|
||||
{
|
||||
uint32_t dolOff;
|
||||
uint32_t fstOff;
|
||||
uint32_t fstSz;
|
||||
uint32_t fstMaxSz;
|
||||
uint32_t fstMemoryAddress;
|
||||
uint32_t userPosition;
|
||||
uint32_t userSz;
|
||||
uint8_t padding1[4];
|
||||
int32_t debugMonitorSize;
|
||||
int32_t simMemSize;
|
||||
uint32_t argOffset;
|
||||
uint32_t debugFlag;
|
||||
uint32_t trkAddress;
|
||||
uint32_t trkSz;
|
||||
uint32_t countryCode;
|
||||
uint32_t unk1;
|
||||
uint32_t unk2;
|
||||
uint32_t unk3;
|
||||
uint32_t dolLimit;
|
||||
uint32_t unk4;
|
||||
uint8_t padding2[0x1fd0];
|
||||
};
|
||||
|
||||
class Node
|
||||
{
|
||||
public:
|
||||
|
@ -259,11 +299,11 @@ public:
|
|||
bool extractToDirectory(const SystemString& basePath, const ExtractionContext& ctx) const;
|
||||
};
|
||||
protected:
|
||||
Header m_header;
|
||||
BI2Header m_bi2Header;
|
||||
uint64_t m_dolOff;
|
||||
uint64_t m_fstOff;
|
||||
uint64_t m_fstSz;
|
||||
uint64_t m_fstMemoryAddr;
|
||||
uint64_t m_apploaderSz;
|
||||
std::vector<Node> m_nodes;
|
||||
void parseFST(IPartReadStream& s);
|
||||
|
@ -325,8 +365,6 @@ public:
|
|||
return buf;
|
||||
}
|
||||
|
||||
inline uint64_t getFSTMemoryAddr() const {return m_fstMemoryAddr;}
|
||||
|
||||
inline uint64_t getApploaderSize() const {return m_apploaderSz;}
|
||||
inline std::unique_ptr<uint8_t[]> getApploaderBuf() const
|
||||
{
|
||||
|
@ -336,8 +374,9 @@ public:
|
|||
}
|
||||
|
||||
inline size_t getNodeCount() const { return m_nodes.size(); }
|
||||
inline const Header& getHeader() const { return m_parent.getHeader(); }
|
||||
inline const uint8_t* getBI2Buf() const { return reinterpret_cast<const uint8_t*>(&m_bi2Header); }
|
||||
inline const Header& getHeader() const { return m_header; }
|
||||
inline const BI2Header& getBI2() const { return m_bi2Header; }
|
||||
virtual bool extractCryptoFiles(const SystemString& path, const ExtractionContext& ctx) const { return true; }
|
||||
};
|
||||
|
||||
protected:
|
||||
|
@ -364,6 +403,7 @@ public:
|
|||
return part.get();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline IPartition* getUpdatePartition()
|
||||
{
|
||||
for (const std::unique_ptr<IPartition>& part : m_partitions)
|
||||
|
@ -371,12 +411,14 @@ public:
|
|||
return part.get();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline void extractToDirectory(const SystemString& path, const ExtractionContext& ctx)
|
||||
{
|
||||
for (std::unique_ptr<IPartition>& part : m_partitions)
|
||||
part->extractToDirectory(path, ctx);
|
||||
}
|
||||
|
||||
virtual bool extractDiscHeaderFiles(const SystemString& path, const ExtractionContext& ctx) const=0;
|
||||
};
|
||||
|
||||
class DiscBuilderBase
|
||||
|
@ -386,7 +428,7 @@ public:
|
|||
class PartitionBuilderBase
|
||||
{
|
||||
public:
|
||||
virtual ~PartitionBuilderBase() {}
|
||||
virtual ~PartitionBuilderBase() = default;
|
||||
enum class Kind : uint32_t
|
||||
{
|
||||
Data,
|
||||
|
@ -401,10 +443,10 @@ public:
|
|||
virtual uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws)=0;
|
||||
virtual uint32_t packOffset(uint64_t offset) const=0;
|
||||
|
||||
void recursiveBuildNodesPre(const SystemChar* dirIn, uint64_t dolInode);
|
||||
bool recursiveBuildNodes(IPartWriteStream& ws, bool system, const SystemChar* dirIn, uint64_t dolInode);
|
||||
void recursiveBuildNodesPre(const SystemChar* dirIn);
|
||||
bool recursiveBuildNodes(IPartWriteStream& ws, bool system, const SystemChar* dirIn);
|
||||
|
||||
bool recursiveBuildFST(const SystemChar* dirIn, uint64_t dolInode,
|
||||
bool recursiveBuildFST(const SystemChar* dirIn,
|
||||
std::function<void(void)> incParents);
|
||||
|
||||
void recursiveMergeNodesPre(const DiscBase::IPartition::Node* nodeIn, const SystemChar* dirIn);
|
||||
|
@ -428,32 +470,21 @@ public:
|
|||
|
||||
DiscBuilderBase& m_parent;
|
||||
Kind m_kind;
|
||||
|
||||
char m_gameID[6];
|
||||
std::string m_gameTitle;
|
||||
uint64_t m_dolOffset = 0;
|
||||
uint64_t m_dolSize = 0;
|
||||
public:
|
||||
PartitionBuilderBase(DiscBuilderBase& parent, Kind kind,
|
||||
const char gameID[6], const char* gameTitle)
|
||||
: m_parent(parent), m_kind(kind), m_gameTitle(gameTitle)
|
||||
{
|
||||
memcpy(m_gameID, gameID, 6);
|
||||
}
|
||||
PartitionBuilderBase(DiscBuilderBase& parent, Kind kind)
|
||||
: m_parent(parent), m_kind(kind)
|
||||
{}
|
||||
virtual std::unique_ptr<IPartWriteStream> beginWriteStream(uint64_t offset)=0;
|
||||
bool buildFromDirectory(IPartWriteStream& ws,
|
||||
const SystemChar* dirIn, const SystemChar* dolIn,
|
||||
const SystemChar* apploaderIn);
|
||||
static uint64_t CalculateTotalSizeBuild(const SystemChar* dolIn,
|
||||
const SystemChar* dirIn);
|
||||
const SystemChar* dirIn);
|
||||
static uint64_t CalculateTotalSizeBuild(const SystemChar* dirIn);
|
||||
bool mergeFromDirectory(IPartWriteStream& ws,
|
||||
const DiscBase::IPartition* partIn,
|
||||
const SystemChar* dirIn);
|
||||
static uint64_t CalculateTotalSizeMerge(const DiscBase::IPartition* partIn,
|
||||
const SystemChar* dirIn);
|
||||
|
||||
const char* getGameID() const {return m_gameID;}
|
||||
const std::string& getGameTitle() const {return m_gameTitle;}
|
||||
};
|
||||
protected:
|
||||
SystemString m_outPath;
|
||||
|
@ -480,13 +511,14 @@ public:
|
|||
}
|
||||
|
||||
virtual ~DiscBuilderBase() = default;
|
||||
DiscBuilderBase(const SystemChar* outPath, int64_t discCapacity, FProgress progressCB)
|
||||
DiscBuilderBase(const SystemChar* outPath,
|
||||
int64_t discCapacity, FProgress progressCB)
|
||||
: m_outPath(outPath), m_fileIO(NewFileIO(outPath, discCapacity)),
|
||||
m_discCapacity(discCapacity), m_progressCB(progressCB) {}
|
||||
DiscBuilderBase(DiscBuilderBase&&) = default;
|
||||
DiscBuilderBase& operator=(DiscBuilderBase&&) = default;
|
||||
|
||||
IFileIO& getFileIO() {return *m_fileIO;}
|
||||
IFileIO& getFileIO() { return *m_fileIO; }
|
||||
};
|
||||
|
||||
using Partition = DiscBase::IPartition;
|
||||
|
|
|
@ -13,17 +13,16 @@ class DiscGCN : public DiscBase
|
|||
DiscBuilderGCN makeMergeBuilder(const SystemChar* outPath, FProgress progressCB);
|
||||
public:
|
||||
DiscGCN(std::unique_ptr<IDiscIO>&& dio, bool& err);
|
||||
bool extractDiscHeaderFiles(const SystemString& path, const ExtractionContext& ctx) const;
|
||||
};
|
||||
|
||||
class DiscBuilderGCN : public DiscBuilderBase
|
||||
{
|
||||
friend class DiscMergerGCN;
|
||||
public:
|
||||
DiscBuilderGCN(const SystemChar* outPath, const char gameID[6], const char* gameTitle,
|
||||
uint32_t fstMemoryAddr, FProgress progressCB);
|
||||
EBuildResult buildFromDirectory(const SystemChar* dirIn, const SystemChar* dolIn,
|
||||
const SystemChar* apploaderIn);
|
||||
static uint64_t CalculateTotalSizeRequired(const SystemChar* dirIn, const SystemChar* dolIn);
|
||||
DiscBuilderGCN(const SystemChar* outPath, FProgress progressCB);
|
||||
EBuildResult buildFromDirectory(const SystemChar* dirIn);
|
||||
static uint64_t CalculateTotalSizeRequired(const SystemChar* dirIn);
|
||||
};
|
||||
|
||||
class DiscMergerGCN
|
||||
|
|
|
@ -12,21 +12,16 @@ class DiscWii : public DiscBase
|
|||
public:
|
||||
DiscWii(std::unique_ptr<IDiscIO>&& dio, bool& err);
|
||||
DiscBuilderWii makeMergeBuilder(const SystemChar* outPath, bool dualLayer, FProgress progressCB);
|
||||
bool writeOutDataPartitionHeader(const SystemChar* pathOut) const;
|
||||
bool extractDiscHeaderFiles(const SystemString& path, const ExtractionContext& ctx) const;
|
||||
};
|
||||
|
||||
class DiscBuilderWii : public DiscBuilderBase
|
||||
{
|
||||
bool m_dualLayer;
|
||||
public:
|
||||
DiscBuilderWii(const SystemChar* outPath, const char gameID[6], const char* gameTitle,
|
||||
bool dualLayer, FProgress progressCB);
|
||||
EBuildResult buildFromDirectory(const SystemChar* dirIn,
|
||||
const SystemChar* dolIn,
|
||||
const SystemChar* apploaderIn,
|
||||
const SystemChar* partHeadIn);
|
||||
static uint64_t CalculateTotalSizeRequired(const SystemChar* dirIn, const SystemChar* dolIn,
|
||||
bool& dualLayer);
|
||||
DiscBuilderWii(const SystemChar* outPath, bool dualLayer, FProgress progressCB);
|
||||
EBuildResult buildFromDirectory(const SystemChar* dirIn);
|
||||
static uint64_t CalculateTotalSizeRequired(const SystemChar* dirIn, bool& dualLayer);
|
||||
};
|
||||
|
||||
class DiscMergerWii
|
||||
|
|
|
@ -14,42 +14,38 @@
|
|||
namespace nod
|
||||
{
|
||||
|
||||
struct IReadStream
|
||||
{
|
||||
virtual ~IReadStream() = default;
|
||||
virtual uint64_t read(void* buf, uint64_t length)=0;
|
||||
virtual void seek(int64_t offset, int whence=SEEK_SET)=0;
|
||||
virtual uint64_t position() const=0;
|
||||
};
|
||||
|
||||
struct IWriteStream
|
||||
{
|
||||
virtual ~IWriteStream() = default;
|
||||
virtual uint64_t write(const void* buf, uint64_t length)=0;
|
||||
};
|
||||
|
||||
class IDiscIO
|
||||
{
|
||||
public:
|
||||
virtual ~IDiscIO() {}
|
||||
|
||||
struct IReadStream
|
||||
{
|
||||
virtual ~IReadStream() {}
|
||||
virtual uint64_t read(void* buf, uint64_t length)=0;
|
||||
virtual void seek(int64_t offset, int whence=SEEK_SET)=0;
|
||||
virtual uint64_t position() const=0;
|
||||
};
|
||||
virtual ~IDiscIO() = default;
|
||||
virtual std::unique_ptr<IReadStream> beginReadStream(uint64_t offset=0) const=0;
|
||||
|
||||
struct IWriteStream
|
||||
{
|
||||
virtual ~IWriteStream() {}
|
||||
virtual uint64_t write(const void* buf, uint64_t length)=0;
|
||||
};
|
||||
virtual std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset=0) const=0;
|
||||
};
|
||||
|
||||
struct IPartReadStream
|
||||
struct IPartReadStream : IReadStream
|
||||
{
|
||||
virtual ~IPartReadStream() {}
|
||||
virtual void seek(int64_t offset, int whence=SEEK_SET)=0;
|
||||
virtual uint64_t position() const=0;
|
||||
virtual uint64_t read(void* buf, uint64_t length)=0;
|
||||
virtual ~IPartReadStream() = default;
|
||||
};
|
||||
|
||||
struct IPartWriteStream
|
||||
struct IPartWriteStream : IWriteStream
|
||||
{
|
||||
virtual ~IPartWriteStream() {}
|
||||
virtual ~IPartWriteStream() = default;
|
||||
virtual void close()=0;
|
||||
virtual uint64_t position() const=0;
|
||||
virtual uint64_t write(const void* buf, uint64_t length)=0;
|
||||
};
|
||||
|
||||
#if NOD_ATHENA
|
||||
|
|
|
@ -13,15 +13,12 @@ namespace nod
|
|||
class IFileIO
|
||||
{
|
||||
public:
|
||||
virtual ~IFileIO() {}
|
||||
virtual ~IFileIO() = default;
|
||||
virtual bool exists()=0;
|
||||
virtual uint64_t size()=0;
|
||||
|
||||
struct IWriteStream
|
||||
struct IWriteStream : nod::IWriteStream
|
||||
{
|
||||
virtual ~IWriteStream() {}
|
||||
virtual uint64_t write(const void* buf, uint64_t length)=0;
|
||||
|
||||
uint64_t copyFromDisc(IPartReadStream& discio, uint64_t length)
|
||||
{
|
||||
uint64_t read = 0;
|
||||
|
@ -74,12 +71,8 @@ public:
|
|||
virtual std::unique_ptr<IWriteStream> beginWriteStream() const=0;
|
||||
virtual std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) const=0;
|
||||
|
||||
struct IReadStream
|
||||
struct IReadStream : nod::IReadStream
|
||||
{
|
||||
virtual ~IReadStream() {}
|
||||
virtual void seek(int64_t offset, int whence)=0;
|
||||
virtual int64_t position()=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;
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace nod
|
|||
class IAES
|
||||
{
|
||||
public:
|
||||
virtual ~IAES() {}
|
||||
virtual ~IAES() = default;
|
||||
virtual void encrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len)=0;
|
||||
virtual void decrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len)=0;
|
||||
virtual void setKey(const uint8_t* key)=0;
|
||||
|
|
|
@ -13,7 +13,6 @@ class DiscBase;
|
|||
|
||||
struct ExtractionContext final
|
||||
{
|
||||
bool verbose : 1;
|
||||
bool force : 1;
|
||||
std::function<void(const std::string&, float)> progressCB;
|
||||
};
|
||||
|
|
142
lib/DiscBase.cpp
142
lib/DiscBase.cpp
|
@ -98,7 +98,7 @@ bool DiscBase::IPartition::Node::extractToDirectory(const SystemString& basePath
|
|||
if (m_kind == Kind::Directory)
|
||||
{
|
||||
++m_parent.m_curNodeIdx;
|
||||
if (ctx.verbose && ctx.progressCB && !getName().empty())
|
||||
if (ctx.progressCB && !getName().empty())
|
||||
ctx.progressCB(getName(), m_parent.m_curNodeIdx / float(m_parent.getNodeCount()));
|
||||
if (Mkdir(path.c_str(), 0755) && errno != EEXIST)
|
||||
{
|
||||
|
@ -112,7 +112,7 @@ bool DiscBase::IPartition::Node::extractToDirectory(const SystemString& basePath
|
|||
else if (m_kind == Kind::File)
|
||||
{
|
||||
Sstat theStat;
|
||||
if (ctx.verbose && ctx.progressCB)
|
||||
if (ctx.progressCB)
|
||||
ctx.progressCB(getName(), m_parent.m_curNodeIdx / float(m_parent.getNodeCount()));
|
||||
|
||||
if (ctx.force || Stat(path.c_str(), &theStat))
|
||||
|
@ -124,7 +124,7 @@ bool DiscBase::IPartition::Node::extractToDirectory(const SystemString& basePath
|
|||
ws->copyFromDisc(*rs, m_discLength,
|
||||
[&](float prog)
|
||||
{
|
||||
if (ctx.verbose && ctx.progressCB)
|
||||
if (ctx.progressCB)
|
||||
ctx.progressCB(getName(), (m_parent.m_curNodeIdx + prog) / float(m_parent.getNodeCount()));
|
||||
});
|
||||
}
|
||||
|
@ -144,17 +144,31 @@ bool DiscBase::IPartition::extractToDirectory(const SystemString& path,
|
|||
return false;
|
||||
}
|
||||
|
||||
if (Mkdir((path + _S("/sys")).c_str(), 0755) && errno != EEXIST)
|
||||
if (Mkdir((path + _S("/DATA")).c_str(), 0755) && errno != EEXIST)
|
||||
{
|
||||
LogModule.report(logvisor::Error, _S("unable to mkdir '%s/sys'"), path.c_str());
|
||||
LogModule.report(logvisor::Error, _S("unable to mkdir '%s/DATA'"), path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Mkdir((path + _S("/DATA/sys")).c_str(), 0755) && errno != EEXIST)
|
||||
{
|
||||
LogModule.report(logvisor::Error, _S("unable to mkdir '%s/DATA/sys'"), path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Extract Disc Files */
|
||||
if (!m_parent.extractDiscHeaderFiles(path, ctx))
|
||||
return false;
|
||||
|
||||
/* Extract Crypto Files */
|
||||
if (!extractCryptoFiles(path, ctx))
|
||||
return false;
|
||||
|
||||
/* Extract Apploader */
|
||||
SystemString apploaderPath = path + _S("/sys/apploader.img");
|
||||
SystemString apploaderPath = path + _S("/DATA/sys/apploader.img");
|
||||
if (ctx.force || Stat(apploaderPath.c_str(), &theStat))
|
||||
{
|
||||
if (ctx.verbose && ctx.progressCB)
|
||||
if (ctx.progressCB)
|
||||
ctx.progressCB("apploader.bin", 0.f);
|
||||
std::unique_ptr<uint8_t[]> buf = getApploaderBuf();
|
||||
auto ws = NewFileIO(apploaderPath)->beginWriteStream();
|
||||
|
@ -164,10 +178,10 @@ bool DiscBase::IPartition::extractToDirectory(const SystemString& path,
|
|||
}
|
||||
|
||||
/* Extract Dol */
|
||||
SystemString dolPath = path + _S("/sys/main.dol");
|
||||
SystemString dolPath = path + _S("/DATA/sys/main.dol");
|
||||
if (ctx.force || Stat(dolPath.c_str(), &theStat))
|
||||
{
|
||||
if (ctx.verbose && ctx.progressCB)
|
||||
if (ctx.progressCB)
|
||||
ctx.progressCB("main.dol", 0.f);
|
||||
std::unique_ptr<uint8_t[]> buf = getDOLBuf();
|
||||
auto ws = NewFileIO(dolPath)->beginWriteStream();
|
||||
|
@ -177,32 +191,32 @@ bool DiscBase::IPartition::extractToDirectory(const SystemString& path,
|
|||
}
|
||||
|
||||
/* Extract Boot info */
|
||||
SystemString bootPath = path + _S("/sys/boot.bin");
|
||||
SystemString bootPath = path + _S("/DATA/sys/boot.bin");
|
||||
if (ctx.force || Stat(bootPath.c_str(), &theStat))
|
||||
{
|
||||
if (ctx.verbose && ctx.progressCB)
|
||||
if (ctx.progressCB)
|
||||
ctx.progressCB("boot.bin", 0.f);
|
||||
auto ws = NewFileIO(bootPath)->beginWriteStream();
|
||||
if (!ws)
|
||||
return false;
|
||||
getHeader().write(*ws.get());
|
||||
m_header.write(*ws.get());
|
||||
}
|
||||
|
||||
/* Extract BI2 info */
|
||||
SystemString bi2Path = path + _S("/sys/bi2.bin");
|
||||
SystemString bi2Path = path + _S("/DATA/sys/bi2.bin");
|
||||
if (ctx.force || Stat(bi2Path.c_str(), &theStat))
|
||||
{
|
||||
if (ctx.verbose && ctx.progressCB)
|
||||
if (ctx.progressCB)
|
||||
ctx.progressCB("bi2.bin", 0.f);
|
||||
|
||||
const uint8_t* buf = getBI2Buf();
|
||||
auto ws = NewFileIO(bi2Path)->beginWriteStream();
|
||||
if (!ws)
|
||||
return false;
|
||||
ws->write(buf, sizeof(BI2Header));
|
||||
m_bi2Header.write(*ws);
|
||||
}
|
||||
|
||||
/* Extract Filesystem */
|
||||
SystemString fsPath = path + _S("/files");
|
||||
SystemString fsPath = path + _S("/DATA/files");
|
||||
if (Mkdir(fsPath.c_str(), 0755) && errno != EEXIST)
|
||||
{
|
||||
LogModule.report(logvisor::Error, _S("unable to mkdir '%s'"), fsPath.c_str());
|
||||
|
@ -299,37 +313,28 @@ static size_t PatchDOL(IFileIO::IReadStream& in, IPartWriteStream& out, size_t s
|
|||
return out.write(buf.get(), sz);
|
||||
}
|
||||
|
||||
void DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodesPre(const SystemChar* dirIn,
|
||||
uint64_t dolInode)
|
||||
void DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodesPre(const SystemChar* filesIn)
|
||||
{
|
||||
DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
|
||||
DirectoryEnumerator dEnum(filesIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
|
||||
for (const DirectoryEnumerator::Entry& e : dEnum)
|
||||
{
|
||||
if (e.m_isDir)
|
||||
{
|
||||
recursiveBuildNodesPre(e.m_path.c_str(), dolInode);
|
||||
}
|
||||
recursiveBuildNodesPre(e.m_path.c_str());
|
||||
else
|
||||
{
|
||||
if (dolInode == GetInode(e.m_path.c_str()))
|
||||
continue;
|
||||
|
||||
++m_parent.m_progressTotal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodes(IPartWriteStream& ws,
|
||||
bool system,
|
||||
const SystemChar* dirIn,
|
||||
uint64_t dolInode)
|
||||
const SystemChar* filesIn)
|
||||
{
|
||||
DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
|
||||
DirectoryEnumerator dEnum(filesIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
|
||||
for (const DirectoryEnumerator::Entry& e : dEnum)
|
||||
{
|
||||
if (e.m_isDir)
|
||||
{
|
||||
if (!recursiveBuildNodes(ws, system, e.m_path.c_str(), dolInode))
|
||||
if (!recursiveBuildNodes(ws, system, e.m_path.c_str()))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
|
@ -339,9 +344,6 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodes(IPartWriteStream
|
|||
if (system ^ isSys)
|
||||
continue;
|
||||
|
||||
if (dolInode == GetInode(e.m_path.c_str()))
|
||||
continue;
|
||||
|
||||
size_t fileSz = ROUND_UP_32(e.m_fileSz);
|
||||
uint64_t fileOff = userAllocate(fileSz, ws);
|
||||
if (fileOff == -1)
|
||||
|
@ -380,10 +382,10 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodes(IPartWriteStream
|
|||
return true;
|
||||
}
|
||||
|
||||
bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildFST(const SystemChar* dirIn, uint64_t dolInode,
|
||||
bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildFST(const SystemChar* filesIn,
|
||||
std::function<void(void)> incParents)
|
||||
{
|
||||
DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
|
||||
DirectoryEnumerator dEnum(filesIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
|
||||
for (const DirectoryEnumerator::Entry& e : dEnum)
|
||||
{
|
||||
if (e.m_isDir)
|
||||
|
@ -392,19 +394,11 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildFST(const SystemChar*
|
|||
m_buildNodes.emplace_back(true, m_buildNameOff, 0, dirNodeIdx+1);
|
||||
addBuildName(e.m_name);
|
||||
incParents();
|
||||
if (!recursiveBuildFST(e.m_path.c_str(), dolInode, [&](){m_buildNodes[dirNodeIdx].incrementLength(); incParents();}))
|
||||
if (!recursiveBuildFST(e.m_path.c_str(), [&](){m_buildNodes[dirNodeIdx].incrementLength(); incParents();}))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dolInode == GetInode(e.m_path.c_str()))
|
||||
{
|
||||
m_buildNodes.emplace_back(false, m_buildNameOff, packOffset(m_dolOffset), m_dolSize);
|
||||
addBuildName(e.m_name);
|
||||
incParents();
|
||||
continue;
|
||||
}
|
||||
|
||||
std::pair<uint64_t,uint64_t> fileOffSz = m_fileOffsetsSizes.at(e.m_path);
|
||||
m_buildNodes.emplace_back(false, m_buildNameOff, packOffset(fileOffSz.first), fileOffSz.second);
|
||||
addBuildName(e.m_name);
|
||||
|
@ -796,20 +790,21 @@ bool DiscBuilderBase::PartitionBuilderBase::RecursiveCalculateTotalSize(
|
|||
}
|
||||
|
||||
bool DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(IPartWriteStream& ws,
|
||||
const SystemChar* dirIn,
|
||||
const SystemChar* dolIn,
|
||||
const SystemChar* apploaderIn)
|
||||
const SystemChar* dirIn)
|
||||
{
|
||||
if (!dirIn || !dolIn || !apploaderIn)
|
||||
if (!dirIn)
|
||||
{
|
||||
LogModule.report(logvisor::Error, _S("all arguments must be supplied to buildFromDirectory()"));
|
||||
return false;
|
||||
}
|
||||
|
||||
SystemString dirStr(dirIn);
|
||||
SystemString dolIn = dirStr + _S("/DATA/sys/main.dol");
|
||||
SystemString filesIn = dirStr + _S("/DATA/files");
|
||||
|
||||
/* 1st pass - Tally up total progress steps */
|
||||
uint64_t dolInode = GetInode(dolIn);
|
||||
m_parent.m_progressTotal += 2; /* Prep and DOL */
|
||||
recursiveBuildNodesPre(dirIn, dolInode);
|
||||
recursiveBuildNodesPre(filesIn.c_str());
|
||||
|
||||
/* Clear file */
|
||||
m_parent.m_progressCB(m_parent.getProgressFactor(), _S("Preparing output image"), -1);
|
||||
|
@ -822,9 +817,9 @@ bool DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(IPartWriteStream&
|
|||
/* Write Boot DOL first (first thing seeked to after Apploader) */
|
||||
{
|
||||
Sstat dolStat;
|
||||
if (Stat(dolIn, &dolStat))
|
||||
if (Stat(dolIn.c_str(), &dolStat))
|
||||
{
|
||||
LogModule.report(logvisor::Error, _S("unable to stat %s"), dolIn);
|
||||
LogModule.report(logvisor::Error, _S("unable to stat %s"), dolIn.c_str());
|
||||
return false;
|
||||
}
|
||||
size_t fileSz = ROUND_UP_32(dolStat.st_size);
|
||||
|
@ -833,12 +828,12 @@ bool DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(IPartWriteStream&
|
|||
return false;
|
||||
m_dolOffset = fileOff;
|
||||
m_dolSize = fileSz;
|
||||
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(dolIn)->beginReadStream();
|
||||
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(dolIn.c_str())->beginReadStream();
|
||||
if (!rs)
|
||||
return false;
|
||||
bool patched;
|
||||
size_t xferSz = PatchDOL(*rs, ws, dolStat.st_size, patched);
|
||||
m_parent.m_progressCB(m_parent.getProgressFactor(), SystemString(dolIn) +
|
||||
m_parent.m_progressCB(m_parent.getProgressFactor(), dolIn +
|
||||
(patched ? _S(" [PATCHED]") : _S("")), xferSz);
|
||||
++m_parent.m_progressIdx;
|
||||
for (size_t i=0 ; i<fileSz-xferSz ; ++i)
|
||||
|
@ -846,27 +841,30 @@ bool DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(IPartWriteStream&
|
|||
}
|
||||
|
||||
/* Gather files in root directory */
|
||||
if (!recursiveBuildNodes(ws, true, dirIn, dolInode))
|
||||
if (!recursiveBuildNodes(ws, true, filesIn.c_str()))
|
||||
return false;
|
||||
if (!recursiveBuildNodes(ws, false, dirIn, dolInode))
|
||||
if (!recursiveBuildNodes(ws, false, filesIn.c_str()))
|
||||
return false;
|
||||
if (!recursiveBuildFST(dirIn, dolInode, [&](){m_buildNodes[0].incrementLength();}))
|
||||
if (!recursiveBuildFST(filesIn.c_str(), [&](){m_buildNodes[0].incrementLength();}))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(const SystemChar* dolIn,
|
||||
const SystemChar* dirIn)
|
||||
uint64_t DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(const SystemChar* dirIn)
|
||||
{
|
||||
SystemString dirStr(dirIn);
|
||||
SystemString dolIn = dirStr + _S("/DATA/sys/main.dol");
|
||||
SystemString filesIn = dirStr + _S("/DATA/files");
|
||||
|
||||
Sstat dolStat;
|
||||
if (Stat(dolIn, &dolStat))
|
||||
if (Stat(dolIn.c_str(), &dolStat))
|
||||
{
|
||||
LogModule.report(logvisor::Error, _S("unable to stat %s"), dolIn);
|
||||
LogModule.report(logvisor::Error, _S("unable to stat %s"), dolIn.c_str());
|
||||
return -1;
|
||||
}
|
||||
uint64_t totalSz = ROUND_UP_32(dolStat.st_size);
|
||||
if (!RecursiveCalculateTotalSize(totalSz, nullptr, dirIn))
|
||||
if (!RecursiveCalculateTotalSize(totalSz, nullptr, filesIn.c_str()))
|
||||
return -1;
|
||||
return totalSz;
|
||||
}
|
||||
|
@ -881,9 +879,12 @@ bool DiscBuilderBase::PartitionBuilderBase::mergeFromDirectory(IPartWriteStream&
|
|||
return false;
|
||||
}
|
||||
|
||||
SystemString dirStr(dirIn);
|
||||
SystemString filesIn = dirStr + _S("/DATA/files");
|
||||
|
||||
/* 1st pass - Tally up total progress steps */
|
||||
m_parent.m_progressTotal += 2; /* Prep and DOL */
|
||||
recursiveMergeNodesPre(&partIn->getFSTRoot(), dirIn);
|
||||
recursiveMergeNodesPre(&partIn->getFSTRoot(), filesIn.c_str());
|
||||
|
||||
/* Clear file */
|
||||
m_parent.m_progressCB(m_parent.getProgressFactor(), _S("Preparing output image"), -1);
|
||||
|
@ -915,11 +916,11 @@ bool DiscBuilderBase::PartitionBuilderBase::mergeFromDirectory(IPartWriteStream&
|
|||
|
||||
/* Gather files in root directory */
|
||||
SystemString keyPath;
|
||||
if (!recursiveMergeNodes(ws, true, &partIn->getFSTRoot(), dirIn, keyPath))
|
||||
if (!recursiveMergeNodes(ws, true, &partIn->getFSTRoot(), filesIn.c_str(), keyPath))
|
||||
return false;
|
||||
if (!recursiveMergeNodes(ws, false, &partIn->getFSTRoot(), dirIn, keyPath))
|
||||
if (!recursiveMergeNodes(ws, false, &partIn->getFSTRoot(), filesIn.c_str(), keyPath))
|
||||
return false;
|
||||
if (!recursiveMergeFST(&partIn->getFSTRoot(), dirIn, [&](){m_buildNodes[0].incrementLength();}, keyPath))
|
||||
if (!recursiveMergeFST(&partIn->getFSTRoot(), filesIn.c_str(), [&](){m_buildNodes[0].incrementLength();}, keyPath))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -928,8 +929,11 @@ bool DiscBuilderBase::PartitionBuilderBase::mergeFromDirectory(IPartWriteStream&
|
|||
uint64_t DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge(const DiscBase::IPartition* partIn,
|
||||
const SystemChar* dirIn)
|
||||
{
|
||||
SystemString dirStr(dirIn);
|
||||
SystemString filesIn = dirStr + _S("/DATA/files");
|
||||
|
||||
uint64_t totalSz = ROUND_UP_32(partIn->getDOLSize());
|
||||
if (!RecursiveCalculateTotalSize(totalSz, &partIn->getFSTRoot(), dirIn))
|
||||
if (!RecursiveCalculateTotalSize(totalSz, &partIn->getFSTRoot(), filesIn.c_str()))
|
||||
return -1;
|
||||
return totalSz;
|
||||
}
|
||||
|
|
190
lib/DiscGCN.cpp
190
lib/DiscGCN.cpp
|
@ -1,4 +1,5 @@
|
|||
#include "nod/DiscGCN.hpp"
|
||||
#include "nod/nod.hpp"
|
||||
#include <inttypes.h>
|
||||
#define BUFFER_SZ 0x8000
|
||||
|
||||
|
@ -12,18 +13,17 @@ public:
|
|||
: IPartition(parent, kind, offset)
|
||||
{
|
||||
/* GCN-specific header reads */
|
||||
std::unique_ptr<IPartReadStream> s = beginReadStream(0x420);
|
||||
std::unique_ptr<IPartReadStream> s = beginReadStream(0x0);
|
||||
if (!s)
|
||||
{
|
||||
err = true;
|
||||
return;
|
||||
}
|
||||
|
||||
s->read(&m_bi2Header, sizeof(BI2Header));
|
||||
m_dolOff = SBig(m_bi2Header.dolOff);
|
||||
m_fstOff = SBig(m_bi2Header.fstOff);
|
||||
m_fstSz = SBig(m_bi2Header.fstSz);
|
||||
m_fstMemoryAddr = SBig(m_bi2Header.fstMemoryAddress);
|
||||
m_header.read(*s);
|
||||
m_bi2Header.read(*s);
|
||||
m_dolOff = m_header.m_dolOff;
|
||||
m_fstOff = m_header.m_fstOff;
|
||||
m_fstSz = m_header.m_fstSz;
|
||||
uint32_t vals[2];
|
||||
s->seek(0x2440 + 0x14);
|
||||
s->read(vals, 8);
|
||||
|
@ -40,7 +40,7 @@ public:
|
|||
class PartReadStream : public IPartReadStream
|
||||
{
|
||||
const PartitionGCN& m_parent;
|
||||
std::unique_ptr<IDiscIO::IReadStream> m_dio;
|
||||
std::unique_ptr<IReadStream> m_dio;
|
||||
|
||||
uint64_t m_offset;
|
||||
size_t m_curBlock = SIZE_MAX;
|
||||
|
@ -97,7 +97,7 @@ public:
|
|||
if (cacheSize + cacheOffset > BUFFER_SZ)
|
||||
cacheSize = BUFFER_SZ - cacheOffset;
|
||||
|
||||
memcpy(dst, m_buf + cacheOffset, cacheSize);
|
||||
memmove(dst, m_buf + cacheOffset, cacheSize);
|
||||
dst += cacheSize;
|
||||
rem -= cacheSize;
|
||||
cacheOffset = 0;
|
||||
|
@ -131,15 +131,42 @@ DiscGCN::DiscGCN(std::unique_ptr<IDiscIO>&& dio, bool& err)
|
|||
|
||||
DiscBuilderGCN DiscGCN::makeMergeBuilder(const SystemChar* outPath, FProgress progressCB)
|
||||
{
|
||||
IPartition* dataPart = getDataPartition();
|
||||
return DiscBuilderGCN(outPath, m_header.m_gameID, m_header.m_gameTitle,
|
||||
dataPart->getFSTMemoryAddr(), progressCB);
|
||||
return DiscBuilderGCN(outPath, progressCB);
|
||||
}
|
||||
|
||||
bool DiscGCN::extractDiscHeaderFiles(const SystemString& path, const ExtractionContext& ctx) const
|
||||
{
|
||||
if (Mkdir((path + _S("/DATA/disc")).c_str(), 0755) && errno != EEXIST)
|
||||
{
|
||||
LogModule.report(logvisor::Error, _S("unable to mkdir '%s/DATA/disc'"), path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
Sstat theStat;
|
||||
|
||||
/* Extract Header */
|
||||
SystemString headerPath = path + _S("/DATA/disc/header.bin");
|
||||
if (ctx.force || Stat(headerPath.c_str(), &theStat))
|
||||
{
|
||||
if (ctx.progressCB)
|
||||
ctx.progressCB("header.bin", 0.f);
|
||||
std::unique_ptr<IReadStream> rs = getDiscIO().beginReadStream(0x0);
|
||||
if (!rs)
|
||||
return false;
|
||||
Header header;
|
||||
header.read(*rs);
|
||||
auto ws = NewFileIO(headerPath)->beginWriteStream();
|
||||
if (!ws)
|
||||
return false;
|
||||
header.write(*ws);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class PartitionBuilderGCN : public DiscBuilderBase::PartitionBuilderBase
|
||||
{
|
||||
uint64_t m_curUser = 0x57058000;
|
||||
uint32_t m_fstMemoryAddr;
|
||||
|
||||
public:
|
||||
class PartWriteStream : public IPartWriteStream
|
||||
|
@ -171,9 +198,8 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
PartitionBuilderGCN(DiscBuilderBase& parent, Kind kind,
|
||||
const char gameID[6], const char* gameTitle, uint32_t fstMemoryAddr)
|
||||
: DiscBuilderBase::PartitionBuilderBase(parent, kind, gameID, gameTitle), m_fstMemoryAddr(fstMemoryAddr) {}
|
||||
PartitionBuilderGCN(DiscBuilderBase& parent)
|
||||
: DiscBuilderBase::PartitionBuilderBase(parent, Kind::Data) {}
|
||||
|
||||
uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws)
|
||||
{
|
||||
|
@ -202,17 +228,16 @@ public:
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool _build(const std::function<bool(IPartWriteStream&, size_t&)>& func)
|
||||
bool _build(const std::function<bool(IPartWriteStream&, uint32_t, uint32_t,
|
||||
uint32_t, uint32_t, uint32_t)>& headerFunc,
|
||||
const std::function<bool(IPartWriteStream&)>& bi2Func,
|
||||
const std::function<bool(IPartWriteStream&, size_t&)>& apploaderFunc)
|
||||
{
|
||||
std::unique_ptr<IPartWriteStream> ws = beginWriteStream(0);
|
||||
std::unique_ptr<IPartWriteStream> ws = beginWriteStream(0x2440);
|
||||
if (!ws)
|
||||
return false;
|
||||
Header header(m_gameID, m_gameTitle.c_str(), false);
|
||||
header.write(*ws);
|
||||
|
||||
ws = beginWriteStream(0x2440);
|
||||
size_t xferSz = 0;
|
||||
if (!func(*ws, xferSz))
|
||||
if (!apploaderFunc(*ws, xferSz))
|
||||
return false;
|
||||
|
||||
size_t fstOff = ROUND_UP_32(xferSz);
|
||||
|
@ -233,38 +258,89 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
ws = beginWriteStream(0x420);
|
||||
ws = beginWriteStream(0);
|
||||
if (!ws)
|
||||
return false;
|
||||
uint32_t vals[7];
|
||||
vals[0] = SBig(uint32_t(m_dolOffset));
|
||||
vals[1] = SBig(uint32_t(fstOff));
|
||||
vals[2] = SBig(uint32_t(fstSz));
|
||||
vals[3] = SBig(uint32_t(fstSz));
|
||||
vals[4] = SBig(uint32_t(m_fstMemoryAddr));
|
||||
vals[5] = SBig(uint32_t(m_curUser));
|
||||
vals[6] = SBig(uint32_t(0x57058000 - m_curUser));
|
||||
ws->write(vals, sizeof(vals));
|
||||
if (!headerFunc(*ws, m_dolOffset, fstOff, fstSz, m_curUser, 0x57058000 - m_curUser))
|
||||
return false;
|
||||
if (!bi2Func(*ws))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool buildFromDirectory(const SystemChar* dirIn, const SystemChar* dolIn, const SystemChar* apploaderIn)
|
||||
bool buildFromDirectory(const SystemChar* dirIn)
|
||||
{
|
||||
std::unique_ptr<IPartWriteStream> ws = beginWriteStream(0);
|
||||
if (!ws)
|
||||
return false;
|
||||
bool result = DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(*ws, dirIn, dolIn, apploaderIn);
|
||||
bool result = DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(*ws, dirIn);
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
return _build([this, apploaderIn](IPartWriteStream& ws, size_t& xferSz) -> bool
|
||||
SystemString dirStr(dirIn);
|
||||
|
||||
/* Check Apploader */
|
||||
SystemString apploaderIn = dirStr + _S("/DATA/sys/apploader.img");
|
||||
Sstat apploaderStat;
|
||||
if (Stat(apploaderIn.c_str(), &apploaderStat))
|
||||
{
|
||||
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(apploaderIn)->beginReadStream();
|
||||
LogModule.report(logvisor::Error, _S("unable to stat %s"), apploaderIn.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check Boot */
|
||||
SystemString bootIn = dirStr + _S("/DATA/sys/boot.bin");
|
||||
Sstat bootStat;
|
||||
if (Stat(bootIn.c_str(), &bootStat))
|
||||
{
|
||||
LogModule.report(logvisor::Error, _S("unable to stat %s"), bootIn.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check BI2 */
|
||||
SystemString bi2In = dirStr + _S("/DATA/sys/bi2.bin");
|
||||
Sstat bi2Stat;
|
||||
if (Stat(bi2In.c_str(), &bi2Stat))
|
||||
{
|
||||
LogModule.report(logvisor::Error, _S("unable to stat %s"), bi2In.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
return _build(
|
||||
[this, &bootIn](IPartWriteStream& ws, uint32_t dolOff, uint32_t fstOff, uint32_t fstSz,
|
||||
uint32_t userOff, uint32_t userSz) -> bool
|
||||
{
|
||||
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(bootIn.c_str())->beginReadStream();
|
||||
if (!rs)
|
||||
return false;
|
||||
Header header;
|
||||
header.read(*rs);
|
||||
header.m_dolOff = dolOff;
|
||||
header.m_fstOff = fstOff;
|
||||
header.m_fstSz = fstSz;
|
||||
header.m_fstMaxSz = fstSz;
|
||||
header.m_userPosition = userOff;
|
||||
header.m_userSz = userSz;
|
||||
header.write(ws);
|
||||
return true;
|
||||
},
|
||||
[this, &bi2In](IPartWriteStream& ws) -> bool
|
||||
{
|
||||
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(bi2In.c_str())->beginReadStream();
|
||||
if (!rs)
|
||||
return false;
|
||||
BI2Header bi2;
|
||||
bi2.read(*rs);
|
||||
bi2.write(ws);
|
||||
return true;
|
||||
},
|
||||
[this, &apploaderIn](IPartWriteStream& ws, size_t& xferSz) -> bool
|
||||
{
|
||||
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(apploaderIn.c_str())->beginReadStream();
|
||||
if (!rs)
|
||||
return false;
|
||||
char buf[8192];
|
||||
SystemString apploaderName(apploaderIn);
|
||||
while (true)
|
||||
{
|
||||
size_t rdSz = rs->read(buf, 8192);
|
||||
|
@ -278,7 +354,7 @@ public:
|
|||
"apploader flows into user area (one or the other is too big)");
|
||||
return false;
|
||||
}
|
||||
m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderName, xferSz);
|
||||
m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderIn, xferSz);
|
||||
}
|
||||
++m_parent.m_progressIdx;
|
||||
return true;
|
||||
|
@ -294,7 +370,26 @@ public:
|
|||
if (!result)
|
||||
return false;
|
||||
|
||||
return _build([this, partIn](IPartWriteStream& ws, size_t& xferSz) -> bool
|
||||
return _build(
|
||||
[this, partIn](IPartWriteStream& ws, uint32_t dolOff, uint32_t fstOff, uint32_t fstSz,
|
||||
uint32_t userOff, uint32_t userSz) -> bool
|
||||
{
|
||||
Header header = partIn->getHeader();
|
||||
header.m_dolOff = dolOff;
|
||||
header.m_fstOff = fstOff;
|
||||
header.m_fstSz = fstSz;
|
||||
header.m_fstMaxSz = fstSz;
|
||||
header.m_userPosition = userOff;
|
||||
header.m_userSz = userSz;
|
||||
header.write(ws);
|
||||
return true;
|
||||
},
|
||||
[this, partIn](IPartWriteStream& ws) -> bool
|
||||
{
|
||||
partIn->getBI2().write(ws);
|
||||
return true;
|
||||
},
|
||||
[this, partIn](IPartWriteStream& ws, size_t& xferSz) -> bool
|
||||
{
|
||||
std::unique_ptr<uint8_t[]> apploaderBuf = partIn->getApploaderBuf();
|
||||
size_t apploaderSz = partIn->getApploaderSize();
|
||||
|
@ -314,8 +409,7 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
EBuildResult DiscBuilderGCN::buildFromDirectory(const SystemChar* dirIn, const SystemChar* dolIn,
|
||||
const SystemChar* apploaderIn)
|
||||
EBuildResult DiscBuilderGCN::buildFromDirectory(const SystemChar* dirIn)
|
||||
{
|
||||
if (!m_fileIO->beginWriteStream())
|
||||
return EBuildResult::Failed;
|
||||
|
@ -332,12 +426,12 @@ EBuildResult DiscBuilderGCN::buildFromDirectory(const SystemChar* dirIn, const S
|
|||
ws->write("", 1);
|
||||
|
||||
PartitionBuilderGCN& pb = static_cast<PartitionBuilderGCN&>(*m_partitions[0]);
|
||||
return pb.buildFromDirectory(dirIn, dolIn, apploaderIn) ? EBuildResult::Success : EBuildResult::Failed;
|
||||
return pb.buildFromDirectory(dirIn) ? EBuildResult::Success : EBuildResult::Failed;
|
||||
}
|
||||
|
||||
uint64_t DiscBuilderGCN::CalculateTotalSizeRequired(const SystemChar* dirIn, const SystemChar* dolIn)
|
||||
uint64_t DiscBuilderGCN::CalculateTotalSizeRequired(const SystemChar* dirIn)
|
||||
{
|
||||
uint64_t sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dolIn, dirIn);
|
||||
uint64_t sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dirIn);
|
||||
if (sz == -1)
|
||||
return -1;
|
||||
sz += 0x30000;
|
||||
|
@ -349,12 +443,10 @@ uint64_t DiscBuilderGCN::CalculateTotalSizeRequired(const SystemChar* dirIn, con
|
|||
return sz;
|
||||
}
|
||||
|
||||
DiscBuilderGCN::DiscBuilderGCN(const SystemChar* outPath, const char gameID[6], const char* gameTitle,
|
||||
uint32_t fstMemoryAddr, FProgress progressCB)
|
||||
DiscBuilderGCN::DiscBuilderGCN(const SystemChar* outPath, FProgress progressCB)
|
||||
: DiscBuilderBase(outPath, 0x57058000, progressCB)
|
||||
{
|
||||
PartitionBuilderGCN* partBuilder = new PartitionBuilderGCN(*this, PartitionBuilderBase::Kind::Data,
|
||||
gameID, gameTitle, fstMemoryAddr);
|
||||
PartitionBuilderGCN* partBuilder = new PartitionBuilderGCN(*this);
|
||||
m_partitions.emplace_back(partBuilder);
|
||||
}
|
||||
|
||||
|
|
654
lib/DiscWii.cpp
654
lib/DiscWii.cpp
|
@ -5,6 +5,7 @@
|
|||
#include "nod/DiscWii.hpp"
|
||||
#include "nod/aes.hpp"
|
||||
#include "nod/sha1.h"
|
||||
#include "nod/nod.hpp"
|
||||
|
||||
namespace nod
|
||||
{
|
||||
|
@ -66,7 +67,7 @@ class PartitionWii : public DiscBase::IPartition
|
|||
uint32_t timeLimit;
|
||||
} timeLimits[8];
|
||||
|
||||
void read(IDiscIO::IReadStream& s)
|
||||
void read(IReadStream& s)
|
||||
{
|
||||
s.read(this, 676);
|
||||
sigType = SBig(sigType);
|
||||
|
@ -79,6 +80,21 @@ class PartitionWii : public DiscBase::IPartition
|
|||
timeLimits[t].timeLimit = SBig(timeLimits[t].timeLimit);
|
||||
}
|
||||
}
|
||||
|
||||
void write(IWriteStream& s) const
|
||||
{
|
||||
Ticket tik = *this;
|
||||
tik.sigType = SBig(tik.sigType);
|
||||
tik.ticketVersion = SBig(tik.ticketVersion);
|
||||
tik.permittedTitlesMask = SBig(tik.permittedTitlesMask);
|
||||
tik.permitMask = SBig(tik.permitMask);
|
||||
for (size_t t=0 ; t<8 ; ++t)
|
||||
{
|
||||
tik.timeLimits[t].enableTimeLimit = SBig(tik.timeLimits[t].enableTimeLimit);
|
||||
tik.timeLimits[t].timeLimit = SBig(tik.timeLimits[t].timeLimit);
|
||||
}
|
||||
s.write(&tik, 676);
|
||||
}
|
||||
} m_ticket;
|
||||
|
||||
struct TMD
|
||||
|
@ -112,7 +128,7 @@ class PartitionWii : public DiscBase::IPartition
|
|||
uint64_t size;
|
||||
char hash[20];
|
||||
|
||||
void read(IDiscIO::IReadStream& s)
|
||||
void read(IReadStream& s)
|
||||
{
|
||||
s.read(this, 36);
|
||||
id = SBig(id);
|
||||
|
@ -120,10 +136,20 @@ class PartitionWii : public DiscBase::IPartition
|
|||
type = SBig(type);
|
||||
size = SBig(size);
|
||||
}
|
||||
|
||||
void write(IWriteStream& s) const
|
||||
{
|
||||
Content c = *this;
|
||||
c.id = SBig(c.id);
|
||||
c.index = SBig(c.index);
|
||||
c.type = SBig(c.type);
|
||||
c.size = SBig(c.size);
|
||||
s.write(&c, 36);
|
||||
}
|
||||
};
|
||||
std::vector<Content> contents;
|
||||
|
||||
void read(IDiscIO::IReadStream& s)
|
||||
void read(IReadStream& s)
|
||||
{
|
||||
s.read(this, 484);
|
||||
sigType = SigType(SBig(uint32_t(sigType)));
|
||||
|
@ -144,6 +170,24 @@ class PartitionWii : public DiscBase::IPartition
|
|||
contents.back().read(s);
|
||||
}
|
||||
}
|
||||
|
||||
void write(IWriteStream& s) const
|
||||
{
|
||||
TMD tmd = *this;
|
||||
tmd.sigType = SigType(SBig(uint32_t(tmd.sigType)));
|
||||
tmd.iosIdMajor = SBig(tmd.iosIdMajor);
|
||||
tmd.iosIdMinor = SBig(tmd.iosIdMinor);
|
||||
tmd.titleIdMajor = SBig(tmd.titleIdMajor);
|
||||
tmd.titleType = SBig(tmd.titleType);
|
||||
tmd.groupId = SBig(tmd.groupId);
|
||||
tmd.accessFlags = SBig(tmd.accessFlags);
|
||||
tmd.titleVersion = SBig(tmd.titleVersion);
|
||||
tmd.numContents = SBig(tmd.numContents);
|
||||
tmd.bootIdx = SBig(tmd.bootIdx);
|
||||
s.write(&tmd, 484);
|
||||
for (uint16_t c=0 ; c<numContents ; ++c)
|
||||
tmd.contents.back().write(s);
|
||||
}
|
||||
} m_tmd;
|
||||
|
||||
struct Certificate
|
||||
|
@ -157,7 +201,7 @@ class PartitionWii : public DiscBase::IPartition
|
|||
uint32_t modulus;
|
||||
uint32_t pubExp;
|
||||
|
||||
void read(IDiscIO::IReadStream& s)
|
||||
void read(IReadStream& s)
|
||||
{
|
||||
s.read(&sigType, 4);
|
||||
sigType = SigType(SBig(uint32_t(sigType)));
|
||||
|
@ -184,11 +228,46 @@ class PartitionWii : public DiscBase::IPartition
|
|||
|
||||
s.seek(52, SEEK_CUR);
|
||||
}
|
||||
|
||||
void write(IWriteStream& s) const
|
||||
{
|
||||
Certificate c = *this;
|
||||
c.sigType = SigType(SBig(uint32_t(c.sigType)));
|
||||
s.write(&c.sigType, 4);
|
||||
if (sigType == SigType::RSA_4096)
|
||||
s.write(sig, 512);
|
||||
else if (sigType == SigType::RSA_2048)
|
||||
s.write(sig, 256);
|
||||
else if (sigType == SigType::ELIPTICAL_CURVE)
|
||||
s.write(sig, 64);
|
||||
|
||||
uint32_t zero = 0;
|
||||
for (int i=0 ; i<15 ; ++i)
|
||||
s.write(&zero, 4);
|
||||
|
||||
s.write(issuer, 64);
|
||||
c.keyType = KeyType(SBig(uint32_t(c.keyType)));
|
||||
s.write(&c.keyType, 4);
|
||||
s.write(subject, 64);
|
||||
if (keyType == KeyType::RSA_4096)
|
||||
s.write(key, 512);
|
||||
else if (keyType == KeyType::RSA_2048)
|
||||
s.write(key, 256);
|
||||
|
||||
c.modulus = SBig(c.modulus);
|
||||
c.pubExp = SBig(c.pubExp);
|
||||
s.write(&c.modulus, 8);
|
||||
|
||||
for (int i=0 ; i<13 ; ++i)
|
||||
s.write(&zero, 4);
|
||||
}
|
||||
};
|
||||
Certificate m_caCert;
|
||||
Certificate m_tmdCert;
|
||||
Certificate m_ticketCert;
|
||||
|
||||
std::unique_ptr<uint8_t[]> m_h3Data;
|
||||
|
||||
uint64_t m_dataOff;
|
||||
uint8_t m_decKey[16];
|
||||
|
||||
|
@ -196,7 +275,7 @@ public:
|
|||
PartitionWii(const DiscWii& parent, Kind kind, uint64_t offset, bool& err)
|
||||
: IPartition(parent, kind, offset)
|
||||
{
|
||||
std::unique_ptr<IDiscIO::IReadStream> s = parent.getDiscIO().beginReadStream(offset);
|
||||
std::unique_ptr<IReadStream> s = parent.getDiscIO().beginReadStream(offset);
|
||||
if (!s)
|
||||
{
|
||||
err = true;
|
||||
|
@ -239,25 +318,29 @@ public:
|
|||
m_tmdCert.read(*s);
|
||||
m_ticketCert.read(*s);
|
||||
|
||||
s->seek(globalHashTableOff);
|
||||
m_h3Data.reset(new uint8_t[0x18000]);
|
||||
s->read(m_h3Data.get(), 0x18000);
|
||||
|
||||
/* Decrypt title key */
|
||||
std::unique_ptr<IAES> aes = NewAES();
|
||||
uint8_t iv[16] = {};
|
||||
memcpy(iv, m_ticket.titleId, 8);
|
||||
memmove(iv, m_ticket.titleId, 8);
|
||||
aes->setKey(COMMON_KEYS[(int)m_ticket.commonKeyIdx]);
|
||||
aes->decrypt(iv, m_ticket.encKey, m_decKey, 16);
|
||||
|
||||
/* Wii-specific header reads (now using title key to decrypt) */
|
||||
std::unique_ptr<IPartReadStream> ds = beginReadStream(0x420);
|
||||
std::unique_ptr<IPartReadStream> ds = beginReadStream(0x0);
|
||||
if (!ds)
|
||||
{
|
||||
err = true;
|
||||
return;
|
||||
}
|
||||
|
||||
s->read(&m_bi2Header, sizeof(BI2Header));
|
||||
m_dolOff = SBig(m_bi2Header.dolOff) << 2;
|
||||
m_fstOff = SBig(m_bi2Header.fstOff) << 2;
|
||||
m_fstSz = SBig(m_bi2Header.fstSz) << 2;
|
||||
m_header.read(*ds);
|
||||
m_bi2Header.read(*ds);
|
||||
m_dolOff = m_header.m_dolOff << 2;
|
||||
m_fstOff = m_header.m_fstOff << 2;
|
||||
m_fstSz = m_header.m_fstSz << 2;
|
||||
ds->seek(0x2440 + 0x14);
|
||||
uint32_t vals[2];
|
||||
ds->read(vals, 8);
|
||||
|
@ -277,7 +360,7 @@ public:
|
|||
const PartitionWii& m_parent;
|
||||
uint64_t m_baseOffset;
|
||||
uint64_t m_offset;
|
||||
std::unique_ptr<IDiscIO::IReadStream> m_dio;
|
||||
std::unique_ptr<IReadStream> m_dio;
|
||||
|
||||
size_t m_curBlock = SIZE_MAX;
|
||||
uint8_t m_encBuf[0x8000];
|
||||
|
@ -340,7 +423,7 @@ public:
|
|||
if (cacheSize + cacheOffset > 0x7c00)
|
||||
cacheSize = 0x7c00 - cacheOffset;
|
||||
|
||||
memcpy(dst, m_decBuf + cacheOffset, cacheSize);
|
||||
memmove(dst, m_decBuf + cacheOffset, cacheSize);
|
||||
dst += cacheSize;
|
||||
rem -= cacheSize;
|
||||
cacheOffset = 0;
|
||||
|
@ -366,7 +449,7 @@ public:
|
|||
std::unique_ptr<uint8_t[]> readPartitionHeaderBuf(size_t& szOut) const
|
||||
{
|
||||
{
|
||||
std::unique_ptr<IDiscIO::IReadStream> rs = m_parent.getDiscIO().beginReadStream(m_offset + 0x2B4);
|
||||
std::unique_ptr<IReadStream> rs = m_parent.getDiscIO().beginReadStream(m_offset + 0x2B4);
|
||||
if (!rs)
|
||||
return {};
|
||||
|
||||
|
@ -380,7 +463,7 @@ public:
|
|||
szOut = uint64_t(h3) << 2;
|
||||
}
|
||||
|
||||
std::unique_ptr<IDiscIO::IReadStream> rs = m_parent.getDiscIO().beginReadStream(m_offset);
|
||||
std::unique_ptr<IReadStream> rs = m_parent.getDiscIO().beginReadStream(m_offset);
|
||||
if (!rs)
|
||||
return {};
|
||||
|
||||
|
@ -390,39 +473,58 @@ public:
|
|||
return buf;
|
||||
}
|
||||
|
||||
bool writeOutPartitionHeader(const SystemChar* pathOut) const
|
||||
bool extractCryptoFiles(const SystemString& path, const ExtractionContext& ctx) const
|
||||
{
|
||||
std::unique_ptr<IFileIO::IWriteStream> ws = NewFileIO(pathOut)->beginWriteStream();
|
||||
if (!ws)
|
||||
return false;
|
||||
uint64_t h3Off;
|
||||
{
|
||||
std::unique_ptr<IDiscIO::IReadStream> rs = m_parent.getDiscIO().beginReadStream(m_offset + 0x2B4);
|
||||
if (!rs)
|
||||
return false;
|
||||
Sstat theStat;
|
||||
|
||||
uint32_t h3;
|
||||
if (rs->read(&h3, 4) != 4)
|
||||
{
|
||||
LogModule.report(logvisor::Error, _S("unable to read H3 offset to %s"), pathOut);
|
||||
/* Extract Ticket */
|
||||
SystemString ticketPath = path + _S("/ticket.bin");
|
||||
if (ctx.force || Stat(ticketPath.c_str(), &theStat))
|
||||
{
|
||||
if (ctx.progressCB)
|
||||
ctx.progressCB("ticket.bin", 0.f);
|
||||
auto ws = NewFileIO(ticketPath)->beginWriteStream();
|
||||
if (!ws)
|
||||
return false;
|
||||
}
|
||||
h3 = SBig(h3);
|
||||
h3Off = uint64_t(h3) << 2;
|
||||
m_ticket.write(*ws);
|
||||
}
|
||||
|
||||
char buf[8192];
|
||||
size_t rem = h3Off;
|
||||
std::unique_ptr<IDiscIO::IReadStream> rs = m_parent.getDiscIO().beginReadStream(m_offset);
|
||||
if (!rs)
|
||||
return false;
|
||||
|
||||
while (rem)
|
||||
/* Extract TMD */
|
||||
SystemString tmdPath = path + _S("/tmd.bin");
|
||||
if (ctx.force || Stat(tmdPath.c_str(), &theStat))
|
||||
{
|
||||
size_t rdSz = nod::min(rem, size_t(8192ul));
|
||||
rs->read(buf, rdSz);
|
||||
ws->write(buf, rdSz);
|
||||
rem -= rdSz;
|
||||
if (ctx.progressCB)
|
||||
ctx.progressCB("tmd.bin", 0.f);
|
||||
auto ws = NewFileIO(tmdPath)->beginWriteStream();
|
||||
if (!ws)
|
||||
return false;
|
||||
m_tmd.write(*ws);
|
||||
}
|
||||
|
||||
/* Extract Certs */
|
||||
SystemString certPath = path + _S("/cert.bin");
|
||||
if (ctx.force || Stat(certPath.c_str(), &theStat))
|
||||
{
|
||||
if (ctx.progressCB)
|
||||
ctx.progressCB("cert.bin", 0.f);
|
||||
auto ws = NewFileIO(certPath)->beginWriteStream();
|
||||
if (!ws)
|
||||
return false;
|
||||
m_caCert.write(*ws);
|
||||
m_tmdCert.write(*ws);
|
||||
m_ticketCert.write(*ws);
|
||||
}
|
||||
|
||||
/* Extract H3 */
|
||||
SystemString h3Path = path + _S("/h3.bin");
|
||||
if (ctx.force || Stat(h3Path.c_str(), &theStat))
|
||||
{
|
||||
if (ctx.progressCB)
|
||||
ctx.progressCB("h3.bin", 0.f);
|
||||
auto ws = NewFileIO(h3Path)->beginWriteStream();
|
||||
if (!ws)
|
||||
return false;
|
||||
ws->write(m_h3Data.get(), 0x18000);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -447,7 +549,7 @@ DiscWii::DiscWii(std::unique_ptr<IDiscIO>&& dio, bool& err)
|
|||
} parts[4];
|
||||
PartInfo(IDiscIO& dio, bool& err)
|
||||
{
|
||||
std::unique_ptr<IDiscIO::IReadStream> s = dio.beginReadStream(0x40000);
|
||||
std::unique_ptr<IReadStream> s = dio.beginReadStream(0x40000);
|
||||
if (!s)
|
||||
{
|
||||
err = true;
|
||||
|
@ -496,20 +598,54 @@ DiscWii::DiscWii(std::unique_ptr<IDiscIO>&& dio, bool& err)
|
|||
|
||||
DiscBuilderWii DiscWii::makeMergeBuilder(const SystemChar* outPath, bool dualLayer, FProgress progressCB)
|
||||
{
|
||||
return DiscBuilderWii(outPath, m_header.m_gameID, m_header.m_gameTitle,
|
||||
dualLayer, progressCB);
|
||||
return DiscBuilderWii(outPath, dualLayer, progressCB);
|
||||
}
|
||||
|
||||
bool DiscWii::writeOutDataPartitionHeader(const SystemChar* pathOut) const
|
||||
bool DiscWii::extractDiscHeaderFiles(const SystemString& path, const ExtractionContext& ctx) const
|
||||
{
|
||||
for (const std::unique_ptr<IPartition>& part : m_partitions)
|
||||
if (Mkdir((path + _S("/DATA/disc")).c_str(), 0755) && errno != EEXIST)
|
||||
{
|
||||
if (part->getKind() == IPartition::Kind::Data)
|
||||
{
|
||||
return static_cast<PartitionWii&>(*part).writeOutPartitionHeader(pathOut);
|
||||
}
|
||||
LogModule.report(logvisor::Error, _S("unable to mkdir '%s/DATA/disc'"), path.c_str());
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
Sstat theStat;
|
||||
|
||||
/* Extract Header */
|
||||
SystemString headerPath = path + _S("/DATA/disc/header.bin");
|
||||
if (ctx.force || Stat(headerPath.c_str(), &theStat))
|
||||
{
|
||||
if (ctx.progressCB)
|
||||
ctx.progressCB("header.bin", 0.f);
|
||||
std::unique_ptr<IReadStream> rs = getDiscIO().beginReadStream(0x0);
|
||||
if (!rs)
|
||||
return false;
|
||||
Header header;
|
||||
header.read(*rs);
|
||||
auto ws = NewFileIO(headerPath)->beginWriteStream();
|
||||
if (!ws)
|
||||
return false;
|
||||
header.write(*ws);
|
||||
}
|
||||
|
||||
/* Extract Region info */
|
||||
SystemString regionPath = path + _S("/DATA/disc/region.bin");
|
||||
if (ctx.force || Stat(regionPath.c_str(), &theStat))
|
||||
{
|
||||
if (ctx.progressCB)
|
||||
ctx.progressCB("header.bin", 0.f);
|
||||
std::unique_ptr<IReadStream> rs = getDiscIO().beginReadStream(0x4E000);
|
||||
if (!rs)
|
||||
return false;
|
||||
std::unique_ptr<uint8_t[]> buf(new uint8_t[0x20]);
|
||||
rs->read(buf.get(), 0x20);
|
||||
auto ws = NewFileIO(regionPath)->beginWriteStream();
|
||||
if (!ws)
|
||||
return false;
|
||||
ws->write(buf.get(), 0x20);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const uint8_t ZEROIV[16] = {0};
|
||||
|
@ -557,32 +693,32 @@ public:
|
|||
{
|
||||
sha1_init(&sha);
|
||||
sha1_write(&sha, ptr0 + (j+1)*0x400, 0x400);
|
||||
memcpy(h0[j], sha1_result(&sha), 20);
|
||||
memmove(h0[j], sha1_result(&sha), 20);
|
||||
}
|
||||
|
||||
sha1_init(&sha);
|
||||
sha1_write(&sha, (char*)h0, 0x26C);
|
||||
memcpy(h1[c], sha1_result(&sha), 20);
|
||||
memmove(h1[c], sha1_result(&sha), 20);
|
||||
|
||||
memcpy(ptr0, h0, 0x26C);
|
||||
memmove(ptr0, h0, 0x26C);
|
||||
memset(ptr0+0x26C, 0, 0x014);
|
||||
}
|
||||
|
||||
sha1_init(&sha);
|
||||
sha1_write(&sha, (char*)h1, 0x0A0);
|
||||
memcpy(h2[s], sha1_result(&sha), 20);
|
||||
memmove(h2[s], sha1_result(&sha), 20);
|
||||
|
||||
for (int c=0 ; c<8 ; ++c)
|
||||
{
|
||||
char* ptr0 = ptr1 + c*0x8000;
|
||||
memcpy(ptr0+0x280, h1, 0x0A0);
|
||||
memmove(ptr0+0x280, h1, 0x0A0);
|
||||
memset(ptr0+0x320, 0, 0x020);
|
||||
}
|
||||
}
|
||||
|
||||
sha1_init(&sha);
|
||||
sha1_write(&sha, (char*)h2, 0x0A0);
|
||||
memcpy(h3Out, sha1_result(&sha), 20);
|
||||
memmove(h3Out, sha1_result(&sha), 20);
|
||||
|
||||
for (int s=0 ; s<8 ; ++s)
|
||||
{
|
||||
|
@ -590,7 +726,7 @@ public:
|
|||
for (int c=0 ; c<8 ; ++c)
|
||||
{
|
||||
char* ptr0 = ptr1 + c*0x8000;
|
||||
memcpy(ptr0+0x340, h2, 0x0A0);
|
||||
memmove(ptr0+0x340, h2, 0x0A0);
|
||||
memset(ptr0+0x3E0, 0, 0x020);
|
||||
m_parent.m_aes->encrypt(ZEROIV, (uint8_t*)ptr0, (uint8_t*)ptr0, 0x400);
|
||||
m_parent.m_aes->encrypt((uint8_t*)(ptr0+0x3D0), (uint8_t*)(ptr0+0x400), (uint8_t*)(ptr0+0x400), 0x7c00);
|
||||
|
@ -659,7 +795,7 @@ public:
|
|||
|
||||
if (src)
|
||||
{
|
||||
memcpy(m_buf + block * 0x8000 + 0x400 + cacheOffset, src, cacheSize);
|
||||
memmove(m_buf + block * 0x8000 + 0x400 + cacheOffset, src, cacheSize);
|
||||
src += cacheSize;
|
||||
}
|
||||
else
|
||||
|
@ -680,9 +816,8 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
PartitionBuilderWii(DiscBuilderBase& parent, Kind kind,
|
||||
const char gameID[6], const char* gameTitle, uint64_t baseOffset)
|
||||
: DiscBuilderBase::PartitionBuilderBase(parent, kind, gameID, gameTitle),
|
||||
PartitionBuilderWii(DiscBuilderBase& parent, Kind kind, uint64_t baseOffset)
|
||||
: DiscBuilderBase::PartitionBuilderBase(parent, kind),
|
||||
m_baseOffset(baseOffset), m_aes(NewAES()) {}
|
||||
|
||||
uint64_t getCurUserEnd() const {return m_curUser;}
|
||||
|
@ -723,101 +858,28 @@ public:
|
|||
return ret;
|
||||
}
|
||||
|
||||
uint64_t _build(const std::function<bool(IPartWriteStream&)>& contentFunc,
|
||||
uint64_t _build(const std::function<bool(IFileIO::IWriteStream&, uint32_t& h3Off, uint32_t& dataOff,
|
||||
uint8_t& ccIdx, uint8_t tkey[16], uint8_t tkeyiv[16],
|
||||
std::unique_ptr<uint8_t[]>& tmdData, size_t& tmdSz)>& cryptoFunc,
|
||||
const std::function<bool(IPartWriteStream&, uint32_t, uint32_t, uint32_t)>& headerFunc,
|
||||
const std::function<bool(IPartWriteStream&)>& bi2Func,
|
||||
const std::function<bool(IPartWriteStream&, size_t&)>& apploaderFunc,
|
||||
const uint8_t* phBuf, size_t phSz, size_t apploaderSz)
|
||||
const std::function<bool(IPartWriteStream&)>& contentFunc,
|
||||
size_t apploaderSz)
|
||||
{
|
||||
/* Read head and validate key members */
|
||||
uint8_t tkey[16];
|
||||
{
|
||||
if (0x1BF + 16 > phSz)
|
||||
{
|
||||
LogModule.report(logvisor::Error, _S("unable to read title key"));
|
||||
return -1;
|
||||
}
|
||||
memmove(tkey, phBuf + 0x1BF, 16);
|
||||
}
|
||||
|
||||
uint8_t tkeyiv[16] = {};
|
||||
{
|
||||
if (0x1DC + 8 > phSz)
|
||||
{
|
||||
LogModule.report(logvisor::Error, _S("unable to read title key IV"));
|
||||
return -1;
|
||||
}
|
||||
memmove(tkeyiv, phBuf + 0x1DC, 8);
|
||||
}
|
||||
|
||||
uint8_t ccIdx;
|
||||
{
|
||||
if (0x1F1 + 1 > phSz)
|
||||
{
|
||||
LogModule.report(logvisor::Error, _S("unable to read common key index"));
|
||||
return -1;
|
||||
}
|
||||
memmove(&ccIdx, phBuf + 0x1F1, 1);
|
||||
if (ccIdx > 1)
|
||||
{
|
||||
LogModule.report(logvisor::Error, _S("common key index may only be 0 or 1"));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t tmdSz;
|
||||
{
|
||||
if (0x2A4 + 4 > phSz)
|
||||
{
|
||||
LogModule.report(logvisor::Error, _S("unable to read TMD size"));
|
||||
return -1;
|
||||
}
|
||||
memmove(&tmdSz, phBuf + 0x2A4, 4);
|
||||
tmdSz = SBig(tmdSz);
|
||||
}
|
||||
|
||||
uint64_t h3Off;
|
||||
{
|
||||
uint32_t h3Ptr;
|
||||
if (0x2B4 + 4 > phSz)
|
||||
{
|
||||
LogModule.report(logvisor::Error, _S("unable to read H3 pointer"));
|
||||
return -1;
|
||||
}
|
||||
memmove(&h3Ptr, phBuf + 0x2B4, 4);
|
||||
h3Off = uint64_t(SBig(h3Ptr)) << 2;
|
||||
}
|
||||
|
||||
uint64_t dataOff;
|
||||
{
|
||||
uint32_t dataPtr;
|
||||
if (0x2B8 + 4 > phSz)
|
||||
{
|
||||
LogModule.report(logvisor::Error, _S("unable to read data pointer"));
|
||||
return -1;
|
||||
}
|
||||
memmove(&dataPtr, phBuf + 0x2B8, 4);
|
||||
dataOff = uint64_t(SBig(dataPtr)) << 2;
|
||||
}
|
||||
m_userOffset = dataOff;
|
||||
|
||||
std::unique_ptr<uint8_t[]> tmdData(new uint8_t[tmdSz]);
|
||||
{
|
||||
if (0x2C0 + tmdSz > phSz)
|
||||
{
|
||||
LogModule.report(logvisor::Error, _S("unable to read TMD"));
|
||||
return -1;
|
||||
}
|
||||
memmove(tmdData.get(), phBuf + 0x2C0, tmdSz);
|
||||
}
|
||||
|
||||
/* Copy partition head up to H3 table */
|
||||
/* Write partition head up to H3 table */
|
||||
std::unique_ptr<IFileIO::IWriteStream> ws = m_parent.getFileIO().beginWriteStream(m_baseOffset);
|
||||
if (!ws)
|
||||
return -1;
|
||||
size_t copySz = std::min(phSz, size_t(h3Off));
|
||||
ws->write(phBuf, copySz);
|
||||
size_t remCopy = (h3Off > phSz) ? (h3Off - copySz) : 0;
|
||||
for (size_t i=0 ; i<remCopy ; ++i)
|
||||
ws->write("", 1);
|
||||
uint32_t h3Off, dataOff;
|
||||
uint8_t tkey[16], tkeyiv[16];
|
||||
uint8_t ccIdx;
|
||||
std::unique_ptr<uint8_t[]> tmdData;
|
||||
size_t tmdSz;
|
||||
if (!cryptoFunc(*ws, h3Off, dataOff, ccIdx, tkey, tkeyiv, tmdData, tmdSz))
|
||||
return -1;
|
||||
|
||||
m_userOffset = dataOff;
|
||||
|
||||
/* Prepare crypto pass */
|
||||
m_aes->setKey(COMMON_KEYS[ccIdx]);
|
||||
|
@ -847,8 +909,6 @@ public:
|
|||
cws = beginWriteStream(0);
|
||||
if (!cws)
|
||||
return -1;
|
||||
Header header(m_gameID, m_gameTitle.c_str(), true, 0, 0, 0);
|
||||
header.write(*cws);
|
||||
|
||||
/* Compute boot table members and write */
|
||||
size_t fstOff = 0x2440 + ROUND_UP_32(apploaderSz);
|
||||
|
@ -863,13 +923,11 @@ public:
|
|||
return -1;
|
||||
}
|
||||
|
||||
cws->write(nullptr, 0x420 - sizeof(Header));
|
||||
uint32_t vals[4];
|
||||
vals[0] = SBig(uint32_t(m_dolOffset >> uint64_t(2)));
|
||||
vals[1] = SBig(uint32_t(fstOff >> uint64_t(2)));
|
||||
vals[2] = SBig(uint32_t(fstSz));
|
||||
vals[3] = SBig(uint32_t(fstSz));
|
||||
cws->write(vals, 16);
|
||||
if (!headerFunc(*cws, m_dolOffset, fstOff, fstSz))
|
||||
return -1;
|
||||
|
||||
if (!bi2Func(*cws))
|
||||
return -1;
|
||||
|
||||
size_t xferSz = 0;
|
||||
if (!apploaderFunc(*cws, xferSz))
|
||||
|
@ -958,42 +1016,151 @@ public:
|
|||
return m_baseOffset + dataOff + groupCount * 0x200000;
|
||||
}
|
||||
|
||||
uint64_t buildFromDirectory(const SystemChar* dirIn,
|
||||
const SystemChar* dolIn,
|
||||
const SystemChar* apploaderIn,
|
||||
const SystemChar* partHeadIn)
|
||||
uint64_t buildFromDirectory(const SystemChar* dirIn)
|
||||
{
|
||||
std::unique_ptr<IFileIO> ph = NewFileIO(partHeadIn);
|
||||
size_t phSz = ph->size();
|
||||
std::unique_ptr<uint8_t[]> phBuf(new uint8_t[phSz]);
|
||||
SystemString dirStr(dirIn);
|
||||
|
||||
/* Check Ticket */
|
||||
SystemString ticketIn = dirStr + _S("/ticket.bin");
|
||||
Sstat theStat;
|
||||
if (Stat(ticketIn.c_str(), &theStat))
|
||||
{
|
||||
auto rs = ph->beginReadStream();
|
||||
if (!rs)
|
||||
return -1;
|
||||
rs->read(phBuf.get(), phSz);
|
||||
LogModule.report(logvisor::Error, _S("unable to stat %s"), ticketIn.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get Apploader Size */
|
||||
Sstat theStat;
|
||||
if (Stat(apploaderIn, &theStat))
|
||||
/* Check TMD */
|
||||
SystemString tmdIn = dirStr + _S("/tmd.bin");
|
||||
Sstat tmdStat;
|
||||
if (Stat(tmdIn.c_str(), &tmdStat))
|
||||
{
|
||||
LogModule.report(logvisor::Error, _S("unable to stat %s"), apploaderIn);
|
||||
LogModule.report(logvisor::Error, _S("unable to stat %s"), tmdIn.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check Cert */
|
||||
SystemString certIn = dirStr + _S("/cert.bin");
|
||||
Sstat certStat;
|
||||
if (Stat(certIn.c_str(), &certStat))
|
||||
{
|
||||
LogModule.report(logvisor::Error, _S("unable to stat %s"), certIn.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check Apploader */
|
||||
SystemString apploaderIn = dirStr + _S("/DATA/sys/apploader.img");
|
||||
Sstat apploaderStat;
|
||||
if (Stat(apploaderIn.c_str(), &apploaderStat))
|
||||
{
|
||||
LogModule.report(logvisor::Error, _S("unable to stat %s"), apploaderIn.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check Boot */
|
||||
SystemString bootIn = dirStr + _S("/DATA/sys/boot.bin");
|
||||
Sstat bootStat;
|
||||
if (Stat(bootIn.c_str(), &bootStat))
|
||||
{
|
||||
LogModule.report(logvisor::Error, _S("unable to stat %s"), bootIn.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check BI2 */
|
||||
SystemString bi2In = dirStr + _S("/DATA/sys/bi2.bin");
|
||||
Sstat bi2Stat;
|
||||
if (Stat(bi2In.c_str(), &bi2Stat))
|
||||
{
|
||||
LogModule.report(logvisor::Error, _S("unable to stat %s"), bi2In.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
return _build(
|
||||
[this, dirIn, dolIn, apploaderIn](IPartWriteStream& cws) -> bool
|
||||
[&](IFileIO::IWriteStream& ws, uint32_t& h3OffOut, uint32_t& dataOffOut,
|
||||
uint8_t& ccIdx, uint8_t tkey[16], uint8_t tkeyiv[16],
|
||||
std::unique_ptr<uint8_t[]>& tmdData, size_t& tmdSzOut) -> bool
|
||||
{
|
||||
return DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(cws, dirIn, dolIn, apploaderIn);
|
||||
h3OffOut = 0x8000;
|
||||
dataOffOut = 0x20000;
|
||||
|
||||
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(ticketIn.c_str())->beginReadStream();
|
||||
if (!rs)
|
||||
return false;
|
||||
uint8_t buf[0x2A4];
|
||||
memset(buf, 0, 0x2A4);
|
||||
rs->read(buf, 0x2A4);
|
||||
ws.write(buf, 0x2A4);
|
||||
|
||||
ccIdx = buf[0x1F1];
|
||||
memmove(tkey, buf + 0x1BF, 16);
|
||||
memmove(tkeyiv, buf + 0x1DC, 8);
|
||||
memset(tkeyiv + 8, 0, 8);
|
||||
|
||||
uint32_t curOff = 0x2C0;
|
||||
uint32_t tmdSz = SBig(uint32_t(tmdStat.st_size));
|
||||
ws.write(&tmdSz, 4);
|
||||
uint32_t tmdOff = SBig(curOff >> 2);
|
||||
ws.write(&tmdOff, 4);
|
||||
curOff += ROUND_UP_32(tmdStat.st_size);
|
||||
|
||||
uint32_t certSz = SBig(uint32_t(certStat.st_size));
|
||||
ws.write(&certSz, 4);
|
||||
uint32_t certOff = SBig(curOff >> 2);
|
||||
ws.write(&certOff, 4);
|
||||
curOff += ROUND_UP_32(certStat.st_size);
|
||||
|
||||
uint32_t h3Off = SBig(0x8000 >> 2);
|
||||
ws.write(&h3Off, 4);
|
||||
uint32_t dataOff = SBig(0x20000 >> 2);
|
||||
ws.write(&dataOff, 4);
|
||||
uint32_t dataSz = 0;
|
||||
ws.write(&dataSz, 4);
|
||||
|
||||
rs = NewFileIO(tmdIn.c_str())->beginReadStream();
|
||||
tmdData.reset(new uint8_t[tmdStat.st_size]);
|
||||
tmdSzOut = tmdStat.st_size;
|
||||
rs->read(tmdData.get(), tmdStat.st_size);
|
||||
ws.write(tmdData.get(), tmdStat.st_size);
|
||||
uint32_t tmdPadding = ROUND_UP_32(tmdStat.st_size) - tmdStat.st_size;
|
||||
for (int i=0 ; i<tmdPadding ; ++i)
|
||||
ws.write("", 1);
|
||||
|
||||
rs = NewFileIO(certIn.c_str())->beginReadStream();
|
||||
std::unique_ptr<uint8_t[]> certBuf(new uint8_t[certStat.st_size]);
|
||||
rs->read(certBuf.get(), certStat.st_size);
|
||||
ws.write(certBuf.get(), certStat.st_size);
|
||||
|
||||
return true;
|
||||
},
|
||||
[this, apploaderIn](IPartWriteStream& cws, size_t& xferSz) -> bool
|
||||
[this, &bootIn](IPartWriteStream& cws, uint32_t dolOff, uint32_t fstOff, uint32_t fstSz) -> bool
|
||||
{
|
||||
cws.write(nullptr, 0x2440 - 0x430);
|
||||
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(apploaderIn)->beginReadStream();
|
||||
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(bootIn.c_str())->beginReadStream();
|
||||
if (!rs)
|
||||
return false;
|
||||
Header header;
|
||||
header.read(*rs);
|
||||
header.m_dolOff = uint32_t(dolOff >> 2);
|
||||
header.m_fstOff = uint32_t(fstOff >> 2);
|
||||
header.m_fstSz = fstSz;
|
||||
header.m_fstMaxSz = fstSz;
|
||||
header.write(cws);
|
||||
return true;
|
||||
},
|
||||
[this, &bi2In](IPartWriteStream& cws) -> bool
|
||||
{
|
||||
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(bi2In.c_str())->beginReadStream();
|
||||
if (!rs)
|
||||
return false;
|
||||
BI2Header bi2;
|
||||
bi2.read(*rs);
|
||||
bi2.write(cws);
|
||||
return true;
|
||||
},
|
||||
[this, &apploaderIn](IPartWriteStream& cws, size_t& xferSz) -> bool
|
||||
{
|
||||
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(apploaderIn.c_str())->beginReadStream();
|
||||
if (!rs)
|
||||
return false;
|
||||
char buf[8192];
|
||||
SystemString apploaderName(apploaderIn);
|
||||
while (true)
|
||||
{
|
||||
size_t rdSz = rs->read(buf, 8192);
|
||||
|
@ -1007,11 +1174,15 @@ public:
|
|||
"apploader flows into user area (one or the other is too big)");
|
||||
return false;
|
||||
}
|
||||
m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderName, xferSz);
|
||||
m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderIn, xferSz);
|
||||
}
|
||||
++m_parent.m_progressIdx;
|
||||
return true;
|
||||
}, phBuf.get(), phSz, theStat.st_size);
|
||||
},
|
||||
[this, dirIn](IPartWriteStream& cws) -> bool
|
||||
{
|
||||
return DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(cws, dirIn);
|
||||
}, apploaderStat.st_size);
|
||||
}
|
||||
|
||||
bool mergeFromDirectory(const PartitionWii* partIn, const SystemChar* dirIn)
|
||||
|
@ -1020,13 +1191,44 @@ public:
|
|||
std::unique_ptr<uint8_t[]> phBuf = partIn->readPartitionHeaderBuf(phSz);
|
||||
|
||||
return _build(
|
||||
[this, partIn, dirIn](IPartWriteStream& cws) -> bool
|
||||
[&](IFileIO::IWriteStream& ws, uint32_t& h3OffOut, uint32_t& dataOffOut,
|
||||
uint8_t& ccIdx, uint8_t tkey[16], uint8_t tkeyiv[16],
|
||||
std::unique_ptr<uint8_t[]>& tmdData, size_t& tmdSz) -> bool
|
||||
{
|
||||
return DiscBuilderBase::PartitionBuilderBase::mergeFromDirectory(cws, partIn, dirIn);
|
||||
h3OffOut = SBig(*reinterpret_cast<uint32_t*>(&phBuf[0x2B4])) << 2;
|
||||
dataOffOut = SBig(*reinterpret_cast<uint32_t*>(&phBuf[0x2B8])) << 2;
|
||||
|
||||
ccIdx = phBuf[0x1F1];
|
||||
memmove(tkey, phBuf.get() + 0x1BF, 16);
|
||||
memmove(tkeyiv, phBuf.get() + 0x1DC, 8);
|
||||
memset(tkeyiv + 8, 0, 8);
|
||||
|
||||
tmdSz = SBig(*reinterpret_cast<uint32_t*>(&phBuf[0x2A4]));
|
||||
tmdData.reset(new uint8_t[tmdSz]);
|
||||
memmove(tmdData.get(), phBuf.get() + 0x2C0, tmdSz);
|
||||
|
||||
size_t copySz = std::min(phSz, size_t(h3OffOut));
|
||||
ws.write(phBuf.get(), copySz);
|
||||
|
||||
return true;
|
||||
},
|
||||
[this, partIn](IPartWriteStream& cws, uint32_t dolOff, uint32_t fstOff, uint32_t fstSz) -> bool
|
||||
{
|
||||
Header header = partIn->getHeader();
|
||||
header.m_dolOff = uint32_t(dolOff >> uint64_t(2));
|
||||
header.m_fstOff = uint32_t(fstOff >> uint64_t(2));
|
||||
header.m_fstSz = fstSz;
|
||||
header.m_fstMaxSz = fstSz;
|
||||
header.write(cws);
|
||||
return true;
|
||||
},
|
||||
[this, partIn](IPartWriteStream& cws) -> bool
|
||||
{
|
||||
partIn->getBI2().write(cws);
|
||||
return true;
|
||||
},
|
||||
[this, partIn](IPartWriteStream& cws, size_t& xferSz) -> bool
|
||||
{
|
||||
cws.write(nullptr, 0x2440 - 0x430);
|
||||
std::unique_ptr<uint8_t[]> apploaderBuf = partIn->getApploaderBuf();
|
||||
size_t apploaderSz = partIn->getApploaderSize();
|
||||
SystemString apploaderName(_S("<apploader>"));
|
||||
|
@ -1041,13 +1243,18 @@ public:
|
|||
m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderName, xferSz);
|
||||
++m_parent.m_progressIdx;
|
||||
return true;
|
||||
}, phBuf.get(), phSz, partIn->getApploaderSize());
|
||||
},
|
||||
[this, partIn, dirIn](IPartWriteStream& cws) -> bool
|
||||
{
|
||||
return DiscBuilderBase::PartitionBuilderBase::mergeFromDirectory(cws, partIn, dirIn);
|
||||
}, partIn->getApploaderSize());
|
||||
}
|
||||
};
|
||||
|
||||
EBuildResult DiscBuilderWii::buildFromDirectory(const SystemChar* dirIn, const SystemChar* dolIn,
|
||||
const SystemChar* apploaderIn, const SystemChar* partHeadIn)
|
||||
EBuildResult DiscBuilderWii::buildFromDirectory(const SystemChar* dirIn)
|
||||
{
|
||||
SystemString dirStr(dirIn);
|
||||
|
||||
PartitionBuilderWii& pb = static_cast<PartitionBuilderWii&>(*m_partitions[0]);
|
||||
uint64_t filledSz = pb.m_baseOffset;
|
||||
if (!m_fileIO->beginWriteStream())
|
||||
|
@ -1066,7 +1273,7 @@ EBuildResult DiscBuilderWii::buildFromDirectory(const SystemChar* dirIn, const S
|
|||
ws->write("", 1);
|
||||
|
||||
/* Assemble image */
|
||||
filledSz = pb.buildFromDirectory(dirIn, dolIn, apploaderIn, partHeadIn);
|
||||
filledSz = pb.buildFromDirectory(dirIn);
|
||||
if (filledSz == -1)
|
||||
return EBuildResult::Failed;
|
||||
else if (filledSz >= uint64_t(m_discCapacity))
|
||||
|
@ -1082,7 +1289,12 @@ EBuildResult DiscBuilderWii::buildFromDirectory(const SystemChar* dirIn, const S
|
|||
ws = m_fileIO->beginWriteStream(0);
|
||||
if (!ws)
|
||||
return EBuildResult::Failed;
|
||||
Header header(pb.getGameID(), pb.getGameTitle().c_str(), true, 0, 0, 0);
|
||||
SystemString headerPath = dirStr + _S("/DATA/disc/header.bin");
|
||||
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(headerPath.c_str())->beginReadStream();
|
||||
if (!rs)
|
||||
return EBuildResult::Failed;
|
||||
Header header;
|
||||
header.read(*rs);
|
||||
header.write(*ws);
|
||||
|
||||
/* Populate partition info */
|
||||
|
@ -1099,24 +1311,16 @@ EBuildResult DiscBuilderWii::buildFromDirectory(const SystemChar* dirIn, const S
|
|||
ws->write(vals, 4);
|
||||
|
||||
/* Populate region info */
|
||||
SystemString regionPath = dirStr + _S("/DATA/disc/region.bin");
|
||||
rs = NewFileIO(regionPath.c_str())->beginReadStream();
|
||||
if (!rs)
|
||||
return EBuildResult::Failed;
|
||||
uint8_t regionBuf[0x20];
|
||||
rs->read(regionBuf, 0x20);
|
||||
ws = m_fileIO->beginWriteStream(0x4E000);
|
||||
if (!ws)
|
||||
return EBuildResult::Failed;
|
||||
const char* gameID = pb.getGameID();
|
||||
if (gameID[3] == 'P')
|
||||
vals[0] = SBig(uint32_t(2));
|
||||
else if (gameID[3] == 'J')
|
||||
vals[0] = SBig(uint32_t(0));
|
||||
else
|
||||
vals[0] = SBig(uint32_t(1));
|
||||
ws->write(vals, 4);
|
||||
|
||||
/* Make disc unrated */
|
||||
ws = m_fileIO->beginWriteStream(0x4E010);
|
||||
if (!ws)
|
||||
return EBuildResult::Failed;
|
||||
for (int i=0 ; i<16 ; ++i)
|
||||
ws->write("\x80", 1);
|
||||
ws->write(regionBuf, 0x20);
|
||||
|
||||
/* Fill image to end */
|
||||
ws = m_fileIO->beginWriteStream(filledSz);
|
||||
|
@ -1139,10 +1343,9 @@ EBuildResult DiscBuilderWii::buildFromDirectory(const SystemChar* dirIn, const S
|
|||
return EBuildResult::Success;
|
||||
}
|
||||
|
||||
uint64_t DiscBuilderWii::CalculateTotalSizeRequired(const SystemChar* dirIn, const SystemChar* dolIn,
|
||||
bool& dualLayer)
|
||||
uint64_t DiscBuilderWii::CalculateTotalSizeRequired(const SystemChar* dirIn, bool& dualLayer)
|
||||
{
|
||||
uint64_t sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dolIn, dirIn);
|
||||
uint64_t sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dirIn);
|
||||
if (sz == -1)
|
||||
return -1;
|
||||
auto szDiv = std::lldiv(sz, 0x1F0000);
|
||||
|
@ -1158,12 +1361,10 @@ uint64_t DiscBuilderWii::CalculateTotalSizeRequired(const SystemChar* dirIn, con
|
|||
return sz;
|
||||
}
|
||||
|
||||
DiscBuilderWii::DiscBuilderWii(const SystemChar* outPath, const char gameID[6], const char* gameTitle,
|
||||
bool dualLayer, FProgress progressCB)
|
||||
DiscBuilderWii::DiscBuilderWii(const SystemChar* outPath, bool dualLayer, FProgress progressCB)
|
||||
: DiscBuilderBase(outPath, dualLayer ? 0x1FB4E0000 : 0x118240000, progressCB), m_dualLayer(dualLayer)
|
||||
{
|
||||
PartitionBuilderWii* partBuilder = new PartitionBuilderWii(*this, PartitionBuilderBase::Kind::Data,
|
||||
gameID, gameTitle, 0x200000);
|
||||
PartitionBuilderWii* partBuilder = new PartitionBuilderWii(*this, PartitionBuilderBase::Kind::Data, 0x200000);
|
||||
m_partitions.emplace_back(partBuilder);
|
||||
}
|
||||
|
||||
|
@ -1224,24 +1425,15 @@ EBuildResult DiscMergerWii::mergeFromDirectory(const SystemChar* dirIn)
|
|||
ws->write(vals, 4);
|
||||
|
||||
/* Populate region info */
|
||||
std::unique_ptr<IReadStream> rs = m_sourceDisc.getDiscIO().beginReadStream(0x4E000);
|
||||
if (!rs)
|
||||
return EBuildResult::Failed;
|
||||
uint8_t regionBuf[0x20];
|
||||
rs->read(regionBuf, 0x20);
|
||||
ws = m_builder.m_fileIO->beginWriteStream(0x4E000);
|
||||
if (!ws)
|
||||
return EBuildResult::Failed;
|
||||
const char* gameID = pb.getGameID();
|
||||
if (gameID[3] == 'P')
|
||||
vals[0] = SBig(uint32_t(2));
|
||||
else if (gameID[3] == 'J')
|
||||
vals[0] = SBig(uint32_t(0));
|
||||
else
|
||||
vals[0] = SBig(uint32_t(1));
|
||||
ws->write(vals, 4);
|
||||
|
||||
/* Make disc unrated */
|
||||
ws = m_builder.m_fileIO->beginWriteStream(0x4E010);
|
||||
if (!ws)
|
||||
return EBuildResult::Failed;
|
||||
for (int i=0 ; i<16 ; ++i)
|
||||
ws->write("\x80", 1);
|
||||
ws->write(regionBuf, 0x20);
|
||||
|
||||
/* Fill image to end */
|
||||
ws = m_builder.m_fileIO->beginWriteStream(filledSz);
|
||||
|
|
|
@ -128,7 +128,7 @@ public:
|
|||
{
|
||||
FSeek(fp, offset, whence);
|
||||
}
|
||||
int64_t position()
|
||||
uint64_t position() const
|
||||
{
|
||||
return FTell(fp);
|
||||
}
|
||||
|
|
|
@ -145,7 +145,7 @@ public:
|
|||
li.QuadPart = offset;
|
||||
SetFilePointerEx(fp, li, nullptr, whence);
|
||||
}
|
||||
int64_t position()
|
||||
uint64_t position() const
|
||||
{
|
||||
LARGE_INTEGER li = {};
|
||||
LARGE_INTEGER res;
|
||||
|
|
Loading…
Reference in New Issue