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 */
|
/* Making a GCN image */
|
||||||
nod::DiscBuilderGCN b(isoOutPath, gameID, gameTitle, dolLoadAddress, progFunc);
|
nod::DiscBuilderGCN b(isoOutPath, progFunc);
|
||||||
ret = b.buildFromDirectory(fsRootDirPath, bootDolPath, apploaderPath);
|
ret = b.buildFromDirectory(fsRootDirPath);
|
||||||
|
|
||||||
/* Making a Wii image */
|
/* Making a Wii image */
|
||||||
nod::DiscBuilderWii b(isoOutPath, gameID, gameTitle, dualLayer, progFunc);
|
nod::DiscBuilderWii b(isoOutPath, dualLayer, progFunc);
|
||||||
ret = b.buildFromDirectory(fsRootDirPath, bootDolPath, apploaderPath, partitionHeadPath);
|
ret = b.buildFromDirectory(fsRootDirPath);
|
||||||
```
|
```
|
||||||
|
|
||||||
Wii images are fakesigned using a commonly-applied [signing bug](http://wiibrew.org/wiki/Signing_bug).
|
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>
|
>$ cd <dir-out>
|
||||||
|
|
||||||
# Then one of:
|
# Then one of:
|
||||||
>$ nodtool makegcn <gameid> <game-title> fsroot boot.dol apploader.bin [<image-out>]
|
>$ nodtool makegcn fsroot [<image-out>]
|
||||||
>$ nodtool makewiisl <gameid> <game-title> fsroot boot.dol apploader.bin partition_head.bin [<image-out>]
|
>$ nodtool makewii fsroot [<image-out>]
|
||||||
>$ nodtool makewiidl <gameid> <game-title> fsroot boot.dol apploader.bin partition_head.bin [<image-out>]
|
|
||||||
```
|
```
|
||||||
|
|
124
driver/main.cpp
124
driver/main.cpp
|
@ -7,8 +7,8 @@ static void printHelp()
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Usage:\n"
|
fprintf(stderr, "Usage:\n"
|
||||||
" nodtool extract [-f] <image-in> [<dir-out>]\n"
|
" nodtool extract [-f] <image-in> [<dir-out>]\n"
|
||||||
" nodtool makegcn <gameid> <game-title> <fsroot-in> <dol-in> <apploader-in> [<image-out>]\n"
|
" nodtool makegcn <fsroot-in> [<image-out>]\n"
|
||||||
" nodtool makewii <gameid> <game-title> <fsroot-in> <dol-in> <apploader-in> <parthead-in> [<image-out>]\n"
|
" nodtool makewii <fsroot-in> [<image-out>]\n"
|
||||||
" nodtool mergegcn <fsroot-in> <image-in> [<image-out>]\n"
|
" nodtool mergegcn <fsroot-in> <image-in> [<image-out>]\n"
|
||||||
" nodtool mergewii <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
|
#endif
|
||||||
{
|
{
|
||||||
if (argc < 3 ||
|
if (argc < 3 ||
|
||||||
(!strcasecmp(argv[1], _S("makegcn")) && argc < 7) ||
|
(!strcasecmp(argv[1], _S("makegcn")) && argc < 3) ||
|
||||||
(!strcasecmp(argv[1], _S("makewii")) && argc < 8) ||
|
(!strcasecmp(argv[1], _S("makewii")) && argc < 3) ||
|
||||||
(!strcasecmp(argv[1], _S("mergegcn")) && argc < 4) ||
|
(!strcasecmp(argv[1], _S("mergegcn")) && argc < 4) ||
|
||||||
(!strcasecmp(argv[1], _S("mergewii")) && argc < 4))
|
(!strcasecmp(argv[1], _S("mergewii")) && argc < 4))
|
||||||
{
|
{
|
||||||
|
@ -39,9 +39,12 @@ int main(int argc, char* argv[])
|
||||||
logvisor::RegisterStandardExceptions();
|
logvisor::RegisterStandardExceptions();
|
||||||
logvisor::RegisterConsoleLogger();
|
logvisor::RegisterConsoleLogger();
|
||||||
|
|
||||||
nod::ExtractionContext ctx = { true, true, [&](const std::string& str, float c){
|
bool verbose = false;
|
||||||
fprintf(stderr, "Current node: %s, Extraction %g%% Complete\n", str.c_str(), c * 100.f);
|
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* inDir = nullptr;
|
||||||
const nod::SystemChar* outDir = _S(".");
|
const nod::SystemChar* outDir = _S(".");
|
||||||
|
|
||||||
|
@ -50,7 +53,7 @@ int main(int argc, char* argv[])
|
||||||
if (argv[a][0] == '-' && argv[a][1] == 'f')
|
if (argv[a][0] == '-' && argv[a][1] == 'f')
|
||||||
ctx.force = true;
|
ctx.force = true;
|
||||||
else if (argv[a][0] == '-' && argv[a][1] == 'v')
|
else if (argv[a][0] == '-' && argv[a][1] == 'v')
|
||||||
ctx.verbose = true;
|
verbose = true;
|
||||||
|
|
||||||
else if (!inDir)
|
else if (!inDir)
|
||||||
inDir = argv[a];
|
inDir = argv[a];
|
||||||
|
@ -77,10 +80,6 @@ int main(int argc, char* argv[])
|
||||||
|
|
||||||
nod::Mkdir(outDir, 0755);
|
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();
|
nod::Partition* dataPart = disc->getDataPartition();
|
||||||
if (!dataPart)
|
if (!dataPart)
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -90,59 +89,30 @@ int main(int argc, char* argv[])
|
||||||
}
|
}
|
||||||
else if (!strcasecmp(argv[1], _S("makegcn")))
|
else if (!strcasecmp(argv[1], _S("makegcn")))
|
||||||
{
|
{
|
||||||
#if NOD_UCS2
|
/* Pre-validate path */
|
||||||
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 */
|
|
||||||
nod::Sstat theStat;
|
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]);
|
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as directory"), argv[2]);
|
||||||
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]);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
nod::SystemString gameIdSys(argv[2]);
|
if (nod::DiscBuilderGCN::CalculateTotalSizeRequired(argv[2]) == -1)
|
||||||
nod::SystemUTF8View gameId(gameIdSys);
|
|
||||||
nod::SystemString gameTitleSys(argv[3]);
|
|
||||||
nod::SystemUTF8View gameTitle(gameTitleSys);
|
|
||||||
|
|
||||||
if (nod::DiscBuilderGCN::CalculateTotalSizeRequired(argv[4], argv[5]) == -1)
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
nod::EBuildResult ret;
|
nod::EBuildResult ret;
|
||||||
|
|
||||||
if (argc < 8)
|
if (argc < 4)
|
||||||
{
|
{
|
||||||
nod::SystemString outPath(argv[4]);
|
nod::SystemString outPath(argv[2]);
|
||||||
outPath.append(_S(".iso"));
|
outPath.append(_S(".iso"));
|
||||||
nod::DiscBuilderGCN b(outPath.c_str(), gameId.utf8_str().c_str(), gameTitle.utf8_str().c_str(), 0x0003EB60, progFunc);
|
nod::DiscBuilderGCN b(outPath.c_str(), progFunc);
|
||||||
ret = b.buildFromDirectory(argv[4], argv[5], argv[6]);
|
ret = b.buildFromDirectory(argv[2]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
nod::DiscBuilderGCN b(argv[7], gameId.utf8_str().c_str(), gameTitle.utf8_str().c_str(), 0x0003EB60, progFunc);
|
nod::DiscBuilderGCN b(argv[3], progFunc);
|
||||||
ret = b.buildFromDirectory(argv[4], argv[5], argv[6]);
|
ret = b.buildFromDirectory(argv[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
@ -151,65 +121,31 @@ int main(int argc, char* argv[])
|
||||||
}
|
}
|
||||||
else if (!strcasecmp(argv[1], _S("makewii")))
|
else if (!strcasecmp(argv[1], _S("makewii")))
|
||||||
{
|
{
|
||||||
#if NOD_UCS2
|
/* Pre-validate path */
|
||||||
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 */
|
|
||||||
nod::Sstat theStat;
|
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]);
|
nod::LogModule.report(logvisor::Error, _S("unable to stat %s as directory"), argv[4]);
|
||||||
return 1;
|
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;
|
bool dual = false;
|
||||||
if (nod::DiscBuilderWii::CalculateTotalSizeRequired(argv[4], argv[5], dual) == -1)
|
if (nod::DiscBuilderWii::CalculateTotalSizeRequired(argv[2], dual) == -1)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
nod::EBuildResult ret;
|
nod::EBuildResult ret;
|
||||||
|
|
||||||
if (argc < 9)
|
if (argc < 4)
|
||||||
{
|
{
|
||||||
nod::SystemString outPath(argv[4]);
|
nod::SystemString outPath(argv[2]);
|
||||||
outPath.append(_S(".iso"));
|
outPath.append(_S(".iso"));
|
||||||
nod::DiscBuilderWii b(outPath.c_str(), gameId.utf8_str().c_str(), gameTitle.utf8_str().c_str(), dual, progFunc);
|
nod::DiscBuilderWii b(outPath.c_str(), dual, progFunc);
|
||||||
ret = b.buildFromDirectory(argv[4], argv[5], argv[6], argv[7]);
|
ret = b.buildFromDirectory(argv[2]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
nod::DiscBuilderWii b(argv[8], gameId.utf8_str().c_str(), gameTitle.utf8_str().c_str(), dual, progFunc);
|
nod::DiscBuilderWii b(argv[3], dual, progFunc);
|
||||||
ret = b.buildFromDirectory(argv[4], argv[5], argv[6], argv[7]);
|
ret = b.buildFromDirectory(argv[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
|
@ -67,60 +67,126 @@ struct Header
|
||||||
uint32_t m_debugMonOff;
|
uint32_t m_debugMonOff;
|
||||||
uint32_t m_debugLoadAddr;
|
uint32_t m_debugLoadAddr;
|
||||||
char m_unk3[0x18];
|
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)
|
Header(IDiscIO& dio, bool& err)
|
||||||
{
|
{
|
||||||
std::unique_ptr<IDiscIO::IReadStream> s = dio.beginReadStream(0);
|
auto rs = dio.beginReadStream();
|
||||||
if (!s)
|
if (!rs)
|
||||||
{
|
{
|
||||||
err = true;
|
err = true;
|
||||||
return;
|
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_wiiMagic = SBig(m_wiiMagic);
|
||||||
m_gcnMagic = SBig(m_gcnMagic);
|
m_gcnMagic = SBig(m_gcnMagic);
|
||||||
m_debugMonOff = SBig(m_debugMonOff);
|
m_debugMonOff = SBig(m_debugMonOff);
|
||||||
m_debugLoadAddr = SBig(m_debugLoadAddr);
|
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,
|
void write(IWriteStream& ws) const
|
||||||
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
|
|
||||||
{
|
{
|
||||||
Header hs(*this);
|
Header hs(*this);
|
||||||
hs.m_wiiMagic = SBig(hs.m_wiiMagic);
|
hs.m_wiiMagic = SBig(hs.m_wiiMagic);
|
||||||
hs.m_gcnMagic = SBig(hs.m_gcnMagic);
|
hs.m_gcnMagic = SBig(hs.m_gcnMagic);
|
||||||
hs.m_debugMonOff = SBig(hs.m_debugMonOff);
|
hs.m_debugMonOff = SBig(hs.m_debugMonOff);
|
||||||
hs.m_debugLoadAddr = SBig(hs.m_debugLoadAddr);
|
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));
|
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;
|
struct ExtractionContext;
|
||||||
class DiscBase
|
class DiscBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~DiscBase() {}
|
virtual ~DiscBase() = default;
|
||||||
|
|
||||||
class IPartition
|
class IPartition
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~IPartition() {}
|
virtual ~IPartition() = default;
|
||||||
enum class Kind : uint32_t
|
enum class Kind : uint32_t
|
||||||
{
|
{
|
||||||
Data,
|
Data,
|
||||||
|
@ -140,32 +206,6 @@ public:
|
||||||
uint32_t entryPoint;
|
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
|
class Node
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -259,11 +299,11 @@ public:
|
||||||
bool extractToDirectory(const SystemString& basePath, const ExtractionContext& ctx) const;
|
bool extractToDirectory(const SystemString& basePath, const ExtractionContext& ctx) const;
|
||||||
};
|
};
|
||||||
protected:
|
protected:
|
||||||
|
Header m_header;
|
||||||
BI2Header m_bi2Header;
|
BI2Header m_bi2Header;
|
||||||
uint64_t m_dolOff;
|
uint64_t m_dolOff;
|
||||||
uint64_t m_fstOff;
|
uint64_t m_fstOff;
|
||||||
uint64_t m_fstSz;
|
uint64_t m_fstSz;
|
||||||
uint64_t m_fstMemoryAddr;
|
|
||||||
uint64_t m_apploaderSz;
|
uint64_t m_apploaderSz;
|
||||||
std::vector<Node> m_nodes;
|
std::vector<Node> m_nodes;
|
||||||
void parseFST(IPartReadStream& s);
|
void parseFST(IPartReadStream& s);
|
||||||
|
@ -325,8 +365,6 @@ public:
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint64_t getFSTMemoryAddr() const {return m_fstMemoryAddr;}
|
|
||||||
|
|
||||||
inline uint64_t getApploaderSize() const {return m_apploaderSz;}
|
inline uint64_t getApploaderSize() const {return m_apploaderSz;}
|
||||||
inline std::unique_ptr<uint8_t[]> getApploaderBuf() const
|
inline std::unique_ptr<uint8_t[]> getApploaderBuf() const
|
||||||
{
|
{
|
||||||
|
@ -336,8 +374,9 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
inline size_t getNodeCount() const { return m_nodes.size(); }
|
inline size_t getNodeCount() const { return m_nodes.size(); }
|
||||||
inline const Header& getHeader() const { return m_parent.getHeader(); }
|
inline const Header& getHeader() const { return m_header; }
|
||||||
inline const uint8_t* getBI2Buf() const { return reinterpret_cast<const uint8_t*>(&m_bi2Header); }
|
inline const BI2Header& getBI2() const { return m_bi2Header; }
|
||||||
|
virtual bool extractCryptoFiles(const SystemString& path, const ExtractionContext& ctx) const { return true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -364,6 +403,7 @@ public:
|
||||||
return part.get();
|
return part.get();
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline IPartition* getUpdatePartition()
|
inline IPartition* getUpdatePartition()
|
||||||
{
|
{
|
||||||
for (const std::unique_ptr<IPartition>& part : m_partitions)
|
for (const std::unique_ptr<IPartition>& part : m_partitions)
|
||||||
|
@ -371,12 +411,14 @@ public:
|
||||||
return part.get();
|
return part.get();
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void extractToDirectory(const SystemString& path, const ExtractionContext& ctx)
|
inline void extractToDirectory(const SystemString& path, const ExtractionContext& ctx)
|
||||||
{
|
{
|
||||||
for (std::unique_ptr<IPartition>& part : m_partitions)
|
for (std::unique_ptr<IPartition>& part : m_partitions)
|
||||||
part->extractToDirectory(path, ctx);
|
part->extractToDirectory(path, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual bool extractDiscHeaderFiles(const SystemString& path, const ExtractionContext& ctx) const=0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DiscBuilderBase
|
class DiscBuilderBase
|
||||||
|
@ -386,7 +428,7 @@ public:
|
||||||
class PartitionBuilderBase
|
class PartitionBuilderBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~PartitionBuilderBase() {}
|
virtual ~PartitionBuilderBase() = default;
|
||||||
enum class Kind : uint32_t
|
enum class Kind : uint32_t
|
||||||
{
|
{
|
||||||
Data,
|
Data,
|
||||||
|
@ -401,10 +443,10 @@ public:
|
||||||
virtual uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws)=0;
|
virtual uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws)=0;
|
||||||
virtual uint32_t packOffset(uint64_t offset) const=0;
|
virtual uint32_t packOffset(uint64_t offset) const=0;
|
||||||
|
|
||||||
void recursiveBuildNodesPre(const SystemChar* dirIn, uint64_t dolInode);
|
void recursiveBuildNodesPre(const SystemChar* dirIn);
|
||||||
bool recursiveBuildNodes(IPartWriteStream& ws, bool system, const SystemChar* dirIn, uint64_t dolInode);
|
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);
|
std::function<void(void)> incParents);
|
||||||
|
|
||||||
void recursiveMergeNodesPre(const DiscBase::IPartition::Node* nodeIn, const SystemChar* dirIn);
|
void recursiveMergeNodesPre(const DiscBase::IPartition::Node* nodeIn, const SystemChar* dirIn);
|
||||||
|
@ -428,32 +470,21 @@ public:
|
||||||
|
|
||||||
DiscBuilderBase& m_parent;
|
DiscBuilderBase& m_parent;
|
||||||
Kind m_kind;
|
Kind m_kind;
|
||||||
|
|
||||||
char m_gameID[6];
|
|
||||||
std::string m_gameTitle;
|
|
||||||
uint64_t m_dolOffset = 0;
|
uint64_t m_dolOffset = 0;
|
||||||
uint64_t m_dolSize = 0;
|
uint64_t m_dolSize = 0;
|
||||||
public:
|
public:
|
||||||
PartitionBuilderBase(DiscBuilderBase& parent, Kind kind,
|
PartitionBuilderBase(DiscBuilderBase& parent, Kind kind)
|
||||||
const char gameID[6], const char* gameTitle)
|
: m_parent(parent), m_kind(kind)
|
||||||
: m_parent(parent), m_kind(kind), m_gameTitle(gameTitle)
|
{}
|
||||||
{
|
|
||||||
memcpy(m_gameID, gameID, 6);
|
|
||||||
}
|
|
||||||
virtual std::unique_ptr<IPartWriteStream> beginWriteStream(uint64_t offset)=0;
|
virtual std::unique_ptr<IPartWriteStream> beginWriteStream(uint64_t offset)=0;
|
||||||
bool buildFromDirectory(IPartWriteStream& ws,
|
bool buildFromDirectory(IPartWriteStream& ws,
|
||||||
const SystemChar* dirIn, const SystemChar* dolIn,
|
const SystemChar* dirIn);
|
||||||
const SystemChar* apploaderIn);
|
static uint64_t CalculateTotalSizeBuild(const SystemChar* dirIn);
|
||||||
static uint64_t CalculateTotalSizeBuild(const SystemChar* dolIn,
|
|
||||||
const SystemChar* dirIn);
|
|
||||||
bool mergeFromDirectory(IPartWriteStream& ws,
|
bool mergeFromDirectory(IPartWriteStream& ws,
|
||||||
const DiscBase::IPartition* partIn,
|
const DiscBase::IPartition* partIn,
|
||||||
const SystemChar* dirIn);
|
const SystemChar* dirIn);
|
||||||
static uint64_t CalculateTotalSizeMerge(const DiscBase::IPartition* partIn,
|
static uint64_t CalculateTotalSizeMerge(const DiscBase::IPartition* partIn,
|
||||||
const SystemChar* dirIn);
|
const SystemChar* dirIn);
|
||||||
|
|
||||||
const char* getGameID() const {return m_gameID;}
|
|
||||||
const std::string& getGameTitle() const {return m_gameTitle;}
|
|
||||||
};
|
};
|
||||||
protected:
|
protected:
|
||||||
SystemString m_outPath;
|
SystemString m_outPath;
|
||||||
|
@ -480,13 +511,14 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~DiscBuilderBase() = default;
|
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_outPath(outPath), m_fileIO(NewFileIO(outPath, discCapacity)),
|
||||||
m_discCapacity(discCapacity), m_progressCB(progressCB) {}
|
m_discCapacity(discCapacity), m_progressCB(progressCB) {}
|
||||||
DiscBuilderBase(DiscBuilderBase&&) = default;
|
DiscBuilderBase(DiscBuilderBase&&) = default;
|
||||||
DiscBuilderBase& operator=(DiscBuilderBase&&) = default;
|
DiscBuilderBase& operator=(DiscBuilderBase&&) = default;
|
||||||
|
|
||||||
IFileIO& getFileIO() {return *m_fileIO;}
|
IFileIO& getFileIO() { return *m_fileIO; }
|
||||||
};
|
};
|
||||||
|
|
||||||
using Partition = DiscBase::IPartition;
|
using Partition = DiscBase::IPartition;
|
||||||
|
|
|
@ -13,17 +13,16 @@ class DiscGCN : public DiscBase
|
||||||
DiscBuilderGCN makeMergeBuilder(const SystemChar* outPath, FProgress progressCB);
|
DiscBuilderGCN makeMergeBuilder(const SystemChar* outPath, FProgress progressCB);
|
||||||
public:
|
public:
|
||||||
DiscGCN(std::unique_ptr<IDiscIO>&& dio, bool& err);
|
DiscGCN(std::unique_ptr<IDiscIO>&& dio, bool& err);
|
||||||
|
bool extractDiscHeaderFiles(const SystemString& path, const ExtractionContext& ctx) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DiscBuilderGCN : public DiscBuilderBase
|
class DiscBuilderGCN : public DiscBuilderBase
|
||||||
{
|
{
|
||||||
friend class DiscMergerGCN;
|
friend class DiscMergerGCN;
|
||||||
public:
|
public:
|
||||||
DiscBuilderGCN(const SystemChar* outPath, const char gameID[6], const char* gameTitle,
|
DiscBuilderGCN(const SystemChar* outPath, FProgress progressCB);
|
||||||
uint32_t fstMemoryAddr, FProgress progressCB);
|
EBuildResult buildFromDirectory(const SystemChar* dirIn);
|
||||||
EBuildResult buildFromDirectory(const SystemChar* dirIn, const SystemChar* dolIn,
|
static uint64_t CalculateTotalSizeRequired(const SystemChar* dirIn);
|
||||||
const SystemChar* apploaderIn);
|
|
||||||
static uint64_t CalculateTotalSizeRequired(const SystemChar* dirIn, const SystemChar* dolIn);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class DiscMergerGCN
|
class DiscMergerGCN
|
||||||
|
|
|
@ -12,21 +12,16 @@ class DiscWii : public DiscBase
|
||||||
public:
|
public:
|
||||||
DiscWii(std::unique_ptr<IDiscIO>&& dio, bool& err);
|
DiscWii(std::unique_ptr<IDiscIO>&& dio, bool& err);
|
||||||
DiscBuilderWii makeMergeBuilder(const SystemChar* outPath, bool dualLayer, FProgress progressCB);
|
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
|
class DiscBuilderWii : public DiscBuilderBase
|
||||||
{
|
{
|
||||||
bool m_dualLayer;
|
bool m_dualLayer;
|
||||||
public:
|
public:
|
||||||
DiscBuilderWii(const SystemChar* outPath, const char gameID[6], const char* gameTitle,
|
DiscBuilderWii(const SystemChar* outPath, bool dualLayer, FProgress progressCB);
|
||||||
bool dualLayer, FProgress progressCB);
|
EBuildResult buildFromDirectory(const SystemChar* dirIn);
|
||||||
EBuildResult buildFromDirectory(const SystemChar* dirIn,
|
static uint64_t CalculateTotalSizeRequired(const SystemChar* dirIn, bool& dualLayer);
|
||||||
const SystemChar* dolIn,
|
|
||||||
const SystemChar* apploaderIn,
|
|
||||||
const SystemChar* partHeadIn);
|
|
||||||
static uint64_t CalculateTotalSizeRequired(const SystemChar* dirIn, const SystemChar* dolIn,
|
|
||||||
bool& dualLayer);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class DiscMergerWii
|
class DiscMergerWii
|
||||||
|
|
|
@ -14,42 +14,38 @@
|
||||||
namespace nod
|
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
|
class IDiscIO
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~IDiscIO() {}
|
virtual ~IDiscIO() = default;
|
||||||
|
|
||||||
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 std::unique_ptr<IReadStream> beginReadStream(uint64_t offset=0) const=0;
|
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;
|
virtual std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset=0) const=0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct IPartReadStream
|
struct IPartReadStream : IReadStream
|
||||||
{
|
{
|
||||||
virtual ~IPartReadStream() {}
|
virtual ~IPartReadStream() = default;
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct IPartWriteStream
|
struct IPartWriteStream : IWriteStream
|
||||||
{
|
{
|
||||||
virtual ~IPartWriteStream() {}
|
virtual ~IPartWriteStream() = default;
|
||||||
virtual void close()=0;
|
virtual void close()=0;
|
||||||
virtual uint64_t position() const=0;
|
virtual uint64_t position() const=0;
|
||||||
virtual uint64_t write(const void* buf, uint64_t length)=0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#if NOD_ATHENA
|
#if NOD_ATHENA
|
||||||
|
|
|
@ -13,15 +13,12 @@ namespace nod
|
||||||
class IFileIO
|
class IFileIO
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~IFileIO() {}
|
virtual ~IFileIO() = default;
|
||||||
virtual bool exists()=0;
|
virtual bool exists()=0;
|
||||||
virtual uint64_t size()=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 copyFromDisc(IPartReadStream& discio, uint64_t length)
|
||||||
{
|
{
|
||||||
uint64_t read = 0;
|
uint64_t read = 0;
|
||||||
|
@ -74,12 +71,8 @@ public:
|
||||||
virtual std::unique_ptr<IWriteStream> beginWriteStream() const=0;
|
virtual std::unique_ptr<IWriteStream> beginWriteStream() const=0;
|
||||||
virtual std::unique_ptr<IWriteStream> beginWriteStream(uint64_t offset) 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 uint64_t copyToDisc(struct IPartWriteStream& discio, uint64_t length)=0;
|
||||||
};
|
};
|
||||||
virtual std::unique_ptr<IReadStream> beginReadStream() const=0;
|
virtual std::unique_ptr<IReadStream> beginReadStream() const=0;
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace nod
|
||||||
class IAES
|
class IAES
|
||||||
{
|
{
|
||||||
public:
|
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 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 decrypt(const uint8_t* iv, const uint8_t* inbuf, uint8_t* outbuf, size_t len)=0;
|
||||||
virtual void setKey(const uint8_t* key)=0;
|
virtual void setKey(const uint8_t* key)=0;
|
||||||
|
|
|
@ -13,7 +13,6 @@ class DiscBase;
|
||||||
|
|
||||||
struct ExtractionContext final
|
struct ExtractionContext final
|
||||||
{
|
{
|
||||||
bool verbose : 1;
|
|
||||||
bool force : 1;
|
bool force : 1;
|
||||||
std::function<void(const std::string&, float)> progressCB;
|
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)
|
if (m_kind == Kind::Directory)
|
||||||
{
|
{
|
||||||
++m_parent.m_curNodeIdx;
|
++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()));
|
ctx.progressCB(getName(), m_parent.m_curNodeIdx / float(m_parent.getNodeCount()));
|
||||||
if (Mkdir(path.c_str(), 0755) && errno != EEXIST)
|
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)
|
else if (m_kind == Kind::File)
|
||||||
{
|
{
|
||||||
Sstat theStat;
|
Sstat theStat;
|
||||||
if (ctx.verbose && ctx.progressCB)
|
if (ctx.progressCB)
|
||||||
ctx.progressCB(getName(), m_parent.m_curNodeIdx / float(m_parent.getNodeCount()));
|
ctx.progressCB(getName(), m_parent.m_curNodeIdx / float(m_parent.getNodeCount()));
|
||||||
|
|
||||||
if (ctx.force || Stat(path.c_str(), &theStat))
|
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,
|
ws->copyFromDisc(*rs, m_discLength,
|
||||||
[&](float prog)
|
[&](float prog)
|
||||||
{
|
{
|
||||||
if (ctx.verbose && ctx.progressCB)
|
if (ctx.progressCB)
|
||||||
ctx.progressCB(getName(), (m_parent.m_curNodeIdx + prog) / float(m_parent.getNodeCount()));
|
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;
|
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;
|
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 */
|
/* 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.force || Stat(apploaderPath.c_str(), &theStat))
|
||||||
{
|
{
|
||||||
if (ctx.verbose && ctx.progressCB)
|
if (ctx.progressCB)
|
||||||
ctx.progressCB("apploader.bin", 0.f);
|
ctx.progressCB("apploader.bin", 0.f);
|
||||||
std::unique_ptr<uint8_t[]> buf = getApploaderBuf();
|
std::unique_ptr<uint8_t[]> buf = getApploaderBuf();
|
||||||
auto ws = NewFileIO(apploaderPath)->beginWriteStream();
|
auto ws = NewFileIO(apploaderPath)->beginWriteStream();
|
||||||
|
@ -164,10 +178,10 @@ bool DiscBase::IPartition::extractToDirectory(const SystemString& path,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Extract Dol */
|
/* 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.force || Stat(dolPath.c_str(), &theStat))
|
||||||
{
|
{
|
||||||
if (ctx.verbose && ctx.progressCB)
|
if (ctx.progressCB)
|
||||||
ctx.progressCB("main.dol", 0.f);
|
ctx.progressCB("main.dol", 0.f);
|
||||||
std::unique_ptr<uint8_t[]> buf = getDOLBuf();
|
std::unique_ptr<uint8_t[]> buf = getDOLBuf();
|
||||||
auto ws = NewFileIO(dolPath)->beginWriteStream();
|
auto ws = NewFileIO(dolPath)->beginWriteStream();
|
||||||
|
@ -177,32 +191,32 @@ bool DiscBase::IPartition::extractToDirectory(const SystemString& path,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Extract Boot info */
|
/* 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.force || Stat(bootPath.c_str(), &theStat))
|
||||||
{
|
{
|
||||||
if (ctx.verbose && ctx.progressCB)
|
if (ctx.progressCB)
|
||||||
ctx.progressCB("boot.bin", 0.f);
|
ctx.progressCB("boot.bin", 0.f);
|
||||||
auto ws = NewFileIO(bootPath)->beginWriteStream();
|
auto ws = NewFileIO(bootPath)->beginWriteStream();
|
||||||
if (!ws)
|
if (!ws)
|
||||||
return false;
|
return false;
|
||||||
getHeader().write(*ws.get());
|
m_header.write(*ws.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Extract BI2 info */
|
/* 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.force || Stat(bi2Path.c_str(), &theStat))
|
||||||
{
|
{
|
||||||
if (ctx.verbose && ctx.progressCB)
|
if (ctx.progressCB)
|
||||||
ctx.progressCB("bi2.bin", 0.f);
|
ctx.progressCB("bi2.bin", 0.f);
|
||||||
|
|
||||||
const uint8_t* buf = getBI2Buf();
|
|
||||||
auto ws = NewFileIO(bi2Path)->beginWriteStream();
|
auto ws = NewFileIO(bi2Path)->beginWriteStream();
|
||||||
if (!ws)
|
if (!ws)
|
||||||
return false;
|
return false;
|
||||||
ws->write(buf, sizeof(BI2Header));
|
m_bi2Header.write(*ws);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Extract Filesystem */
|
/* Extract Filesystem */
|
||||||
SystemString fsPath = path + _S("/files");
|
SystemString fsPath = path + _S("/DATA/files");
|
||||||
if (Mkdir(fsPath.c_str(), 0755) && errno != EEXIST)
|
if (Mkdir(fsPath.c_str(), 0755) && errno != EEXIST)
|
||||||
{
|
{
|
||||||
LogModule.report(logvisor::Error, _S("unable to mkdir '%s'"), fsPath.c_str());
|
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);
|
return out.write(buf.get(), sz);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodesPre(const SystemChar* dirIn,
|
void DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodesPre(const SystemChar* filesIn)
|
||||||
uint64_t dolInode)
|
|
||||||
{
|
{
|
||||||
DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
|
DirectoryEnumerator dEnum(filesIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
|
||||||
for (const DirectoryEnumerator::Entry& e : dEnum)
|
for (const DirectoryEnumerator::Entry& e : dEnum)
|
||||||
{
|
{
|
||||||
if (e.m_isDir)
|
if (e.m_isDir)
|
||||||
{
|
recursiveBuildNodesPre(e.m_path.c_str());
|
||||||
recursiveBuildNodesPre(e.m_path.c_str(), dolInode);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
if (dolInode == GetInode(e.m_path.c_str()))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
++m_parent.m_progressTotal;
|
++m_parent.m_progressTotal;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodes(IPartWriteStream& ws,
|
bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodes(IPartWriteStream& ws,
|
||||||
bool system,
|
bool system,
|
||||||
const SystemChar* dirIn,
|
const SystemChar* filesIn)
|
||||||
uint64_t dolInode)
|
|
||||||
{
|
{
|
||||||
DirectoryEnumerator dEnum(dirIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
|
DirectoryEnumerator dEnum(filesIn, DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
|
||||||
for (const DirectoryEnumerator::Entry& e : dEnum)
|
for (const DirectoryEnumerator::Entry& e : dEnum)
|
||||||
{
|
{
|
||||||
if (e.m_isDir)
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -339,9 +344,6 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodes(IPartWriteStream
|
||||||
if (system ^ isSys)
|
if (system ^ isSys)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (dolInode == GetInode(e.m_path.c_str()))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
size_t fileSz = ROUND_UP_32(e.m_fileSz);
|
size_t fileSz = ROUND_UP_32(e.m_fileSz);
|
||||||
uint64_t fileOff = userAllocate(fileSz, ws);
|
uint64_t fileOff = userAllocate(fileSz, ws);
|
||||||
if (fileOff == -1)
|
if (fileOff == -1)
|
||||||
|
@ -380,10 +382,10 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildNodes(IPartWriteStream
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildFST(const SystemChar* dirIn, uint64_t dolInode,
|
bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildFST(const SystemChar* filesIn,
|
||||||
std::function<void(void)> incParents)
|
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)
|
for (const DirectoryEnumerator::Entry& e : dEnum)
|
||||||
{
|
{
|
||||||
if (e.m_isDir)
|
if (e.m_isDir)
|
||||||
|
@ -392,19 +394,11 @@ bool DiscBuilderBase::PartitionBuilderBase::recursiveBuildFST(const SystemChar*
|
||||||
m_buildNodes.emplace_back(true, m_buildNameOff, 0, dirNodeIdx+1);
|
m_buildNodes.emplace_back(true, m_buildNameOff, 0, dirNodeIdx+1);
|
||||||
addBuildName(e.m_name);
|
addBuildName(e.m_name);
|
||||||
incParents();
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
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);
|
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);
|
m_buildNodes.emplace_back(false, m_buildNameOff, packOffset(fileOffSz.first), fileOffSz.second);
|
||||||
addBuildName(e.m_name);
|
addBuildName(e.m_name);
|
||||||
|
@ -796,20 +790,21 @@ bool DiscBuilderBase::PartitionBuilderBase::RecursiveCalculateTotalSize(
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(IPartWriteStream& ws,
|
bool DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(IPartWriteStream& ws,
|
||||||
const SystemChar* dirIn,
|
const SystemChar* dirIn)
|
||||||
const SystemChar* dolIn,
|
|
||||||
const SystemChar* apploaderIn)
|
|
||||||
{
|
{
|
||||||
if (!dirIn || !dolIn || !apploaderIn)
|
if (!dirIn)
|
||||||
{
|
{
|
||||||
LogModule.report(logvisor::Error, _S("all arguments must be supplied to buildFromDirectory()"));
|
LogModule.report(logvisor::Error, _S("all arguments must be supplied to buildFromDirectory()"));
|
||||||
return false;
|
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 */
|
/* 1st pass - Tally up total progress steps */
|
||||||
uint64_t dolInode = GetInode(dolIn);
|
|
||||||
m_parent.m_progressTotal += 2; /* Prep and DOL */
|
m_parent.m_progressTotal += 2; /* Prep and DOL */
|
||||||
recursiveBuildNodesPre(dirIn, dolInode);
|
recursiveBuildNodesPre(filesIn.c_str());
|
||||||
|
|
||||||
/* Clear file */
|
/* Clear file */
|
||||||
m_parent.m_progressCB(m_parent.getProgressFactor(), _S("Preparing output image"), -1);
|
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) */
|
/* Write Boot DOL first (first thing seeked to after Apploader) */
|
||||||
{
|
{
|
||||||
Sstat dolStat;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
size_t fileSz = ROUND_UP_32(dolStat.st_size);
|
size_t fileSz = ROUND_UP_32(dolStat.st_size);
|
||||||
|
@ -833,12 +828,12 @@ bool DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(IPartWriteStream&
|
||||||
return false;
|
return false;
|
||||||
m_dolOffset = fileOff;
|
m_dolOffset = fileOff;
|
||||||
m_dolSize = fileSz;
|
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)
|
if (!rs)
|
||||||
return false;
|
return false;
|
||||||
bool patched;
|
bool patched;
|
||||||
size_t xferSz = PatchDOL(*rs, ws, dolStat.st_size, 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);
|
(patched ? _S(" [PATCHED]") : _S("")), xferSz);
|
||||||
++m_parent.m_progressIdx;
|
++m_parent.m_progressIdx;
|
||||||
for (size_t i=0 ; i<fileSz-xferSz ; ++i)
|
for (size_t i=0 ; i<fileSz-xferSz ; ++i)
|
||||||
|
@ -846,27 +841,30 @@ bool DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(IPartWriteStream&
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Gather files in root directory */
|
/* Gather files in root directory */
|
||||||
if (!recursiveBuildNodes(ws, true, dirIn, dolInode))
|
if (!recursiveBuildNodes(ws, true, filesIn.c_str()))
|
||||||
return false;
|
return false;
|
||||||
if (!recursiveBuildNodes(ws, false, dirIn, dolInode))
|
if (!recursiveBuildNodes(ws, false, filesIn.c_str()))
|
||||||
return false;
|
return false;
|
||||||
if (!recursiveBuildFST(dirIn, dolInode, [&](){m_buildNodes[0].incrementLength();}))
|
if (!recursiveBuildFST(filesIn.c_str(), [&](){m_buildNodes[0].incrementLength();}))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(const SystemChar* dolIn,
|
uint64_t DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(const SystemChar* dirIn)
|
||||||
const SystemChar* dirIn)
|
|
||||||
{
|
{
|
||||||
|
SystemString dirStr(dirIn);
|
||||||
|
SystemString dolIn = dirStr + _S("/DATA/sys/main.dol");
|
||||||
|
SystemString filesIn = dirStr + _S("/DATA/files");
|
||||||
|
|
||||||
Sstat dolStat;
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
uint64_t totalSz = ROUND_UP_32(dolStat.st_size);
|
uint64_t totalSz = ROUND_UP_32(dolStat.st_size);
|
||||||
if (!RecursiveCalculateTotalSize(totalSz, nullptr, dirIn))
|
if (!RecursiveCalculateTotalSize(totalSz, nullptr, filesIn.c_str()))
|
||||||
return -1;
|
return -1;
|
||||||
return totalSz;
|
return totalSz;
|
||||||
}
|
}
|
||||||
|
@ -881,9 +879,12 @@ bool DiscBuilderBase::PartitionBuilderBase::mergeFromDirectory(IPartWriteStream&
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SystemString dirStr(dirIn);
|
||||||
|
SystemString filesIn = dirStr + _S("/DATA/files");
|
||||||
|
|
||||||
/* 1st pass - Tally up total progress steps */
|
/* 1st pass - Tally up total progress steps */
|
||||||
m_parent.m_progressTotal += 2; /* Prep and DOL */
|
m_parent.m_progressTotal += 2; /* Prep and DOL */
|
||||||
recursiveMergeNodesPre(&partIn->getFSTRoot(), dirIn);
|
recursiveMergeNodesPre(&partIn->getFSTRoot(), filesIn.c_str());
|
||||||
|
|
||||||
/* Clear file */
|
/* Clear file */
|
||||||
m_parent.m_progressCB(m_parent.getProgressFactor(), _S("Preparing output image"), -1);
|
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 */
|
/* Gather files in root directory */
|
||||||
SystemString keyPath;
|
SystemString keyPath;
|
||||||
if (!recursiveMergeNodes(ws, true, &partIn->getFSTRoot(), dirIn, keyPath))
|
if (!recursiveMergeNodes(ws, true, &partIn->getFSTRoot(), filesIn.c_str(), keyPath))
|
||||||
return false;
|
return false;
|
||||||
if (!recursiveMergeNodes(ws, false, &partIn->getFSTRoot(), dirIn, keyPath))
|
if (!recursiveMergeNodes(ws, false, &partIn->getFSTRoot(), filesIn.c_str(), keyPath))
|
||||||
return false;
|
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 false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -928,8 +929,11 @@ bool DiscBuilderBase::PartitionBuilderBase::mergeFromDirectory(IPartWriteStream&
|
||||||
uint64_t DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge(const DiscBase::IPartition* partIn,
|
uint64_t DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeMerge(const DiscBase::IPartition* partIn,
|
||||||
const SystemChar* dirIn)
|
const SystemChar* dirIn)
|
||||||
{
|
{
|
||||||
|
SystemString dirStr(dirIn);
|
||||||
|
SystemString filesIn = dirStr + _S("/DATA/files");
|
||||||
|
|
||||||
uint64_t totalSz = ROUND_UP_32(partIn->getDOLSize());
|
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 -1;
|
||||||
return totalSz;
|
return totalSz;
|
||||||
}
|
}
|
||||||
|
|
190
lib/DiscGCN.cpp
190
lib/DiscGCN.cpp
|
@ -1,4 +1,5 @@
|
||||||
#include "nod/DiscGCN.hpp"
|
#include "nod/DiscGCN.hpp"
|
||||||
|
#include "nod/nod.hpp"
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#define BUFFER_SZ 0x8000
|
#define BUFFER_SZ 0x8000
|
||||||
|
|
||||||
|
@ -12,18 +13,17 @@ public:
|
||||||
: IPartition(parent, kind, offset)
|
: IPartition(parent, kind, offset)
|
||||||
{
|
{
|
||||||
/* GCN-specific header reads */
|
/* GCN-specific header reads */
|
||||||
std::unique_ptr<IPartReadStream> s = beginReadStream(0x420);
|
std::unique_ptr<IPartReadStream> s = beginReadStream(0x0);
|
||||||
if (!s)
|
if (!s)
|
||||||
{
|
{
|
||||||
err = true;
|
err = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
m_header.read(*s);
|
||||||
s->read(&m_bi2Header, sizeof(BI2Header));
|
m_bi2Header.read(*s);
|
||||||
m_dolOff = SBig(m_bi2Header.dolOff);
|
m_dolOff = m_header.m_dolOff;
|
||||||
m_fstOff = SBig(m_bi2Header.fstOff);
|
m_fstOff = m_header.m_fstOff;
|
||||||
m_fstSz = SBig(m_bi2Header.fstSz);
|
m_fstSz = m_header.m_fstSz;
|
||||||
m_fstMemoryAddr = SBig(m_bi2Header.fstMemoryAddress);
|
|
||||||
uint32_t vals[2];
|
uint32_t vals[2];
|
||||||
s->seek(0x2440 + 0x14);
|
s->seek(0x2440 + 0x14);
|
||||||
s->read(vals, 8);
|
s->read(vals, 8);
|
||||||
|
@ -40,7 +40,7 @@ public:
|
||||||
class PartReadStream : public IPartReadStream
|
class PartReadStream : public IPartReadStream
|
||||||
{
|
{
|
||||||
const PartitionGCN& m_parent;
|
const PartitionGCN& m_parent;
|
||||||
std::unique_ptr<IDiscIO::IReadStream> m_dio;
|
std::unique_ptr<IReadStream> m_dio;
|
||||||
|
|
||||||
uint64_t m_offset;
|
uint64_t m_offset;
|
||||||
size_t m_curBlock = SIZE_MAX;
|
size_t m_curBlock = SIZE_MAX;
|
||||||
|
@ -97,7 +97,7 @@ public:
|
||||||
if (cacheSize + cacheOffset > BUFFER_SZ)
|
if (cacheSize + cacheOffset > BUFFER_SZ)
|
||||||
cacheSize = BUFFER_SZ - cacheOffset;
|
cacheSize = BUFFER_SZ - cacheOffset;
|
||||||
|
|
||||||
memcpy(dst, m_buf + cacheOffset, cacheSize);
|
memmove(dst, m_buf + cacheOffset, cacheSize);
|
||||||
dst += cacheSize;
|
dst += cacheSize;
|
||||||
rem -= cacheSize;
|
rem -= cacheSize;
|
||||||
cacheOffset = 0;
|
cacheOffset = 0;
|
||||||
|
@ -131,15 +131,42 @@ DiscGCN::DiscGCN(std::unique_ptr<IDiscIO>&& dio, bool& err)
|
||||||
|
|
||||||
DiscBuilderGCN DiscGCN::makeMergeBuilder(const SystemChar* outPath, FProgress progressCB)
|
DiscBuilderGCN DiscGCN::makeMergeBuilder(const SystemChar* outPath, FProgress progressCB)
|
||||||
{
|
{
|
||||||
IPartition* dataPart = getDataPartition();
|
return DiscBuilderGCN(outPath, progressCB);
|
||||||
return DiscBuilderGCN(outPath, m_header.m_gameID, m_header.m_gameTitle,
|
}
|
||||||
dataPart->getFSTMemoryAddr(), 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
|
class PartitionBuilderGCN : public DiscBuilderBase::PartitionBuilderBase
|
||||||
{
|
{
|
||||||
uint64_t m_curUser = 0x57058000;
|
uint64_t m_curUser = 0x57058000;
|
||||||
uint32_t m_fstMemoryAddr;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
class PartWriteStream : public IPartWriteStream
|
class PartWriteStream : public IPartWriteStream
|
||||||
|
@ -171,9 +198,8 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
PartitionBuilderGCN(DiscBuilderBase& parent, Kind kind,
|
PartitionBuilderGCN(DiscBuilderBase& parent)
|
||||||
const char gameID[6], const char* gameTitle, uint32_t fstMemoryAddr)
|
: DiscBuilderBase::PartitionBuilderBase(parent, Kind::Data) {}
|
||||||
: DiscBuilderBase::PartitionBuilderBase(parent, kind, gameID, gameTitle), m_fstMemoryAddr(fstMemoryAddr) {}
|
|
||||||
|
|
||||||
uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws)
|
uint64_t userAllocate(uint64_t reqSz, IPartWriteStream& ws)
|
||||||
{
|
{
|
||||||
|
@ -202,17 +228,16 @@ public:
|
||||||
return ret;
|
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)
|
if (!ws)
|
||||||
return false;
|
return false;
|
||||||
Header header(m_gameID, m_gameTitle.c_str(), false);
|
|
||||||
header.write(*ws);
|
|
||||||
|
|
||||||
ws = beginWriteStream(0x2440);
|
|
||||||
size_t xferSz = 0;
|
size_t xferSz = 0;
|
||||||
if (!func(*ws, xferSz))
|
if (!apploaderFunc(*ws, xferSz))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
size_t fstOff = ROUND_UP_32(xferSz);
|
size_t fstOff = ROUND_UP_32(xferSz);
|
||||||
|
@ -233,38 +258,89 @@ public:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ws = beginWriteStream(0x420);
|
ws = beginWriteStream(0);
|
||||||
if (!ws)
|
if (!ws)
|
||||||
return false;
|
return false;
|
||||||
uint32_t vals[7];
|
if (!headerFunc(*ws, m_dolOffset, fstOff, fstSz, m_curUser, 0x57058000 - m_curUser))
|
||||||
vals[0] = SBig(uint32_t(m_dolOffset));
|
return false;
|
||||||
vals[1] = SBig(uint32_t(fstOff));
|
if (!bi2Func(*ws))
|
||||||
vals[2] = SBig(uint32_t(fstSz));
|
return false;
|
||||||
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));
|
|
||||||
|
|
||||||
return true;
|
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);
|
std::unique_ptr<IPartWriteStream> ws = beginWriteStream(0);
|
||||||
if (!ws)
|
if (!ws)
|
||||||
return false;
|
return false;
|
||||||
bool result = DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(*ws, dirIn, dolIn, apploaderIn);
|
bool result = DiscBuilderBase::PartitionBuilderBase::buildFromDirectory(*ws, dirIn);
|
||||||
if (!result)
|
if (!result)
|
||||||
return false;
|
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)
|
if (!rs)
|
||||||
return false;
|
return false;
|
||||||
char buf[8192];
|
char buf[8192];
|
||||||
SystemString apploaderName(apploaderIn);
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
size_t rdSz = rs->read(buf, 8192);
|
size_t rdSz = rs->read(buf, 8192);
|
||||||
|
@ -278,7 +354,7 @@ public:
|
||||||
"apploader flows into user area (one or the other is too big)");
|
"apploader flows into user area (one or the other is too big)");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderName, xferSz);
|
m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderIn, xferSz);
|
||||||
}
|
}
|
||||||
++m_parent.m_progressIdx;
|
++m_parent.m_progressIdx;
|
||||||
return true;
|
return true;
|
||||||
|
@ -294,7 +370,26 @@ public:
|
||||||
if (!result)
|
if (!result)
|
||||||
return false;
|
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();
|
std::unique_ptr<uint8_t[]> apploaderBuf = partIn->getApploaderBuf();
|
||||||
size_t apploaderSz = partIn->getApploaderSize();
|
size_t apploaderSz = partIn->getApploaderSize();
|
||||||
|
@ -314,8 +409,7 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
EBuildResult DiscBuilderGCN::buildFromDirectory(const SystemChar* dirIn, const SystemChar* dolIn,
|
EBuildResult DiscBuilderGCN::buildFromDirectory(const SystemChar* dirIn)
|
||||||
const SystemChar* apploaderIn)
|
|
||||||
{
|
{
|
||||||
if (!m_fileIO->beginWriteStream())
|
if (!m_fileIO->beginWriteStream())
|
||||||
return EBuildResult::Failed;
|
return EBuildResult::Failed;
|
||||||
|
@ -332,12 +426,12 @@ EBuildResult DiscBuilderGCN::buildFromDirectory(const SystemChar* dirIn, const S
|
||||||
ws->write("", 1);
|
ws->write("", 1);
|
||||||
|
|
||||||
PartitionBuilderGCN& pb = static_cast<PartitionBuilderGCN&>(*m_partitions[0]);
|
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)
|
if (sz == -1)
|
||||||
return -1;
|
return -1;
|
||||||
sz += 0x30000;
|
sz += 0x30000;
|
||||||
|
@ -349,12 +443,10 @@ uint64_t DiscBuilderGCN::CalculateTotalSizeRequired(const SystemChar* dirIn, con
|
||||||
return sz;
|
return sz;
|
||||||
}
|
}
|
||||||
|
|
||||||
DiscBuilderGCN::DiscBuilderGCN(const SystemChar* outPath, const char gameID[6], const char* gameTitle,
|
DiscBuilderGCN::DiscBuilderGCN(const SystemChar* outPath, FProgress progressCB)
|
||||||
uint32_t fstMemoryAddr, FProgress progressCB)
|
|
||||||
: DiscBuilderBase(outPath, 0x57058000, progressCB)
|
: DiscBuilderBase(outPath, 0x57058000, progressCB)
|
||||||
{
|
{
|
||||||
PartitionBuilderGCN* partBuilder = new PartitionBuilderGCN(*this, PartitionBuilderBase::Kind::Data,
|
PartitionBuilderGCN* partBuilder = new PartitionBuilderGCN(*this);
|
||||||
gameID, gameTitle, fstMemoryAddr);
|
|
||||||
m_partitions.emplace_back(partBuilder);
|
m_partitions.emplace_back(partBuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
654
lib/DiscWii.cpp
654
lib/DiscWii.cpp
|
@ -5,6 +5,7 @@
|
||||||
#include "nod/DiscWii.hpp"
|
#include "nod/DiscWii.hpp"
|
||||||
#include "nod/aes.hpp"
|
#include "nod/aes.hpp"
|
||||||
#include "nod/sha1.h"
|
#include "nod/sha1.h"
|
||||||
|
#include "nod/nod.hpp"
|
||||||
|
|
||||||
namespace nod
|
namespace nod
|
||||||
{
|
{
|
||||||
|
@ -66,7 +67,7 @@ class PartitionWii : public DiscBase::IPartition
|
||||||
uint32_t timeLimit;
|
uint32_t timeLimit;
|
||||||
} timeLimits[8];
|
} timeLimits[8];
|
||||||
|
|
||||||
void read(IDiscIO::IReadStream& s)
|
void read(IReadStream& s)
|
||||||
{
|
{
|
||||||
s.read(this, 676);
|
s.read(this, 676);
|
||||||
sigType = SBig(sigType);
|
sigType = SBig(sigType);
|
||||||
|
@ -79,6 +80,21 @@ class PartitionWii : public DiscBase::IPartition
|
||||||
timeLimits[t].timeLimit = SBig(timeLimits[t].timeLimit);
|
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;
|
} m_ticket;
|
||||||
|
|
||||||
struct TMD
|
struct TMD
|
||||||
|
@ -112,7 +128,7 @@ class PartitionWii : public DiscBase::IPartition
|
||||||
uint64_t size;
|
uint64_t size;
|
||||||
char hash[20];
|
char hash[20];
|
||||||
|
|
||||||
void read(IDiscIO::IReadStream& s)
|
void read(IReadStream& s)
|
||||||
{
|
{
|
||||||
s.read(this, 36);
|
s.read(this, 36);
|
||||||
id = SBig(id);
|
id = SBig(id);
|
||||||
|
@ -120,10 +136,20 @@ class PartitionWii : public DiscBase::IPartition
|
||||||
type = SBig(type);
|
type = SBig(type);
|
||||||
size = SBig(size);
|
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;
|
std::vector<Content> contents;
|
||||||
|
|
||||||
void read(IDiscIO::IReadStream& s)
|
void read(IReadStream& s)
|
||||||
{
|
{
|
||||||
s.read(this, 484);
|
s.read(this, 484);
|
||||||
sigType = SigType(SBig(uint32_t(sigType)));
|
sigType = SigType(SBig(uint32_t(sigType)));
|
||||||
|
@ -144,6 +170,24 @@ class PartitionWii : public DiscBase::IPartition
|
||||||
contents.back().read(s);
|
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;
|
} m_tmd;
|
||||||
|
|
||||||
struct Certificate
|
struct Certificate
|
||||||
|
@ -157,7 +201,7 @@ class PartitionWii : public DiscBase::IPartition
|
||||||
uint32_t modulus;
|
uint32_t modulus;
|
||||||
uint32_t pubExp;
|
uint32_t pubExp;
|
||||||
|
|
||||||
void read(IDiscIO::IReadStream& s)
|
void read(IReadStream& s)
|
||||||
{
|
{
|
||||||
s.read(&sigType, 4);
|
s.read(&sigType, 4);
|
||||||
sigType = SigType(SBig(uint32_t(sigType)));
|
sigType = SigType(SBig(uint32_t(sigType)));
|
||||||
|
@ -184,11 +228,46 @@ class PartitionWii : public DiscBase::IPartition
|
||||||
|
|
||||||
s.seek(52, SEEK_CUR);
|
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_caCert;
|
||||||
Certificate m_tmdCert;
|
Certificate m_tmdCert;
|
||||||
Certificate m_ticketCert;
|
Certificate m_ticketCert;
|
||||||
|
|
||||||
|
std::unique_ptr<uint8_t[]> m_h3Data;
|
||||||
|
|
||||||
uint64_t m_dataOff;
|
uint64_t m_dataOff;
|
||||||
uint8_t m_decKey[16];
|
uint8_t m_decKey[16];
|
||||||
|
|
||||||
|
@ -196,7 +275,7 @@ public:
|
||||||
PartitionWii(const DiscWii& parent, Kind kind, uint64_t offset, bool& err)
|
PartitionWii(const DiscWii& parent, Kind kind, uint64_t offset, bool& err)
|
||||||
: IPartition(parent, kind, offset)
|
: 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)
|
if (!s)
|
||||||
{
|
{
|
||||||
err = true;
|
err = true;
|
||||||
|
@ -239,25 +318,29 @@ public:
|
||||||
m_tmdCert.read(*s);
|
m_tmdCert.read(*s);
|
||||||
m_ticketCert.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 */
|
/* Decrypt title key */
|
||||||
std::unique_ptr<IAES> aes = NewAES();
|
std::unique_ptr<IAES> aes = NewAES();
|
||||||
uint8_t iv[16] = {};
|
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->setKey(COMMON_KEYS[(int)m_ticket.commonKeyIdx]);
|
||||||
aes->decrypt(iv, m_ticket.encKey, m_decKey, 16);
|
aes->decrypt(iv, m_ticket.encKey, m_decKey, 16);
|
||||||
|
|
||||||
/* Wii-specific header reads (now using title key to decrypt) */
|
/* 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)
|
if (!ds)
|
||||||
{
|
{
|
||||||
err = true;
|
err = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
m_header.read(*ds);
|
||||||
s->read(&m_bi2Header, sizeof(BI2Header));
|
m_bi2Header.read(*ds);
|
||||||
m_dolOff = SBig(m_bi2Header.dolOff) << 2;
|
m_dolOff = m_header.m_dolOff << 2;
|
||||||
m_fstOff = SBig(m_bi2Header.fstOff) << 2;
|
m_fstOff = m_header.m_fstOff << 2;
|
||||||
m_fstSz = SBig(m_bi2Header.fstSz) << 2;
|
m_fstSz = m_header.m_fstSz << 2;
|
||||||
ds->seek(0x2440 + 0x14);
|
ds->seek(0x2440 + 0x14);
|
||||||
uint32_t vals[2];
|
uint32_t vals[2];
|
||||||
ds->read(vals, 8);
|
ds->read(vals, 8);
|
||||||
|
@ -277,7 +360,7 @@ public:
|
||||||
const PartitionWii& m_parent;
|
const PartitionWii& m_parent;
|
||||||
uint64_t m_baseOffset;
|
uint64_t m_baseOffset;
|
||||||
uint64_t m_offset;
|
uint64_t m_offset;
|
||||||
std::unique_ptr<IDiscIO::IReadStream> m_dio;
|
std::unique_ptr<IReadStream> m_dio;
|
||||||
|
|
||||||
size_t m_curBlock = SIZE_MAX;
|
size_t m_curBlock = SIZE_MAX;
|
||||||
uint8_t m_encBuf[0x8000];
|
uint8_t m_encBuf[0x8000];
|
||||||
|
@ -340,7 +423,7 @@ public:
|
||||||
if (cacheSize + cacheOffset > 0x7c00)
|
if (cacheSize + cacheOffset > 0x7c00)
|
||||||
cacheSize = 0x7c00 - cacheOffset;
|
cacheSize = 0x7c00 - cacheOffset;
|
||||||
|
|
||||||
memcpy(dst, m_decBuf + cacheOffset, cacheSize);
|
memmove(dst, m_decBuf + cacheOffset, cacheSize);
|
||||||
dst += cacheSize;
|
dst += cacheSize;
|
||||||
rem -= cacheSize;
|
rem -= cacheSize;
|
||||||
cacheOffset = 0;
|
cacheOffset = 0;
|
||||||
|
@ -366,7 +449,7 @@ public:
|
||||||
std::unique_ptr<uint8_t[]> readPartitionHeaderBuf(size_t& szOut) const
|
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)
|
if (!rs)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
@ -380,7 +463,7 @@ public:
|
||||||
szOut = uint64_t(h3) << 2;
|
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)
|
if (!rs)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
@ -390,39 +473,58 @@ public:
|
||||||
return buf;
|
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();
|
Sstat theStat;
|
||||||
if (!ws)
|
|
||||||
return false;
|
|
||||||
uint64_t h3Off;
|
|
||||||
{
|
|
||||||
std::unique_ptr<IDiscIO::IReadStream> rs = m_parent.getDiscIO().beginReadStream(m_offset + 0x2B4);
|
|
||||||
if (!rs)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
uint32_t h3;
|
/* Extract Ticket */
|
||||||
if (rs->read(&h3, 4) != 4)
|
SystemString ticketPath = path + _S("/ticket.bin");
|
||||||
{
|
if (ctx.force || Stat(ticketPath.c_str(), &theStat))
|
||||||
LogModule.report(logvisor::Error, _S("unable to read H3 offset to %s"), pathOut);
|
{
|
||||||
|
if (ctx.progressCB)
|
||||||
|
ctx.progressCB("ticket.bin", 0.f);
|
||||||
|
auto ws = NewFileIO(ticketPath)->beginWriteStream();
|
||||||
|
if (!ws)
|
||||||
return false;
|
return false;
|
||||||
}
|
m_ticket.write(*ws);
|
||||||
h3 = SBig(h3);
|
|
||||||
h3Off = uint64_t(h3) << 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char buf[8192];
|
/* Extract TMD */
|
||||||
size_t rem = h3Off;
|
SystemString tmdPath = path + _S("/tmd.bin");
|
||||||
std::unique_ptr<IDiscIO::IReadStream> rs = m_parent.getDiscIO().beginReadStream(m_offset);
|
if (ctx.force || Stat(tmdPath.c_str(), &theStat))
|
||||||
if (!rs)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
while (rem)
|
|
||||||
{
|
{
|
||||||
size_t rdSz = nod::min(rem, size_t(8192ul));
|
if (ctx.progressCB)
|
||||||
rs->read(buf, rdSz);
|
ctx.progressCB("tmd.bin", 0.f);
|
||||||
ws->write(buf, rdSz);
|
auto ws = NewFileIO(tmdPath)->beginWriteStream();
|
||||||
rem -= rdSz;
|
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;
|
return true;
|
||||||
|
@ -447,7 +549,7 @@ DiscWii::DiscWii(std::unique_ptr<IDiscIO>&& dio, bool& err)
|
||||||
} parts[4];
|
} parts[4];
|
||||||
PartInfo(IDiscIO& dio, bool& err)
|
PartInfo(IDiscIO& dio, bool& err)
|
||||||
{
|
{
|
||||||
std::unique_ptr<IDiscIO::IReadStream> s = dio.beginReadStream(0x40000);
|
std::unique_ptr<IReadStream> s = dio.beginReadStream(0x40000);
|
||||||
if (!s)
|
if (!s)
|
||||||
{
|
{
|
||||||
err = true;
|
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)
|
DiscBuilderWii DiscWii::makeMergeBuilder(const SystemChar* outPath, bool dualLayer, FProgress progressCB)
|
||||||
{
|
{
|
||||||
return DiscBuilderWii(outPath, m_header.m_gameID, m_header.m_gameTitle,
|
return DiscBuilderWii(outPath, dualLayer, progressCB);
|
||||||
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)
|
LogModule.report(logvisor::Error, _S("unable to mkdir '%s/DATA/disc'"), path.c_str());
|
||||||
{
|
return false;
|
||||||
return static_cast<PartitionWii&>(*part).writeOutPartitionHeader(pathOut);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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};
|
static const uint8_t ZEROIV[16] = {0};
|
||||||
|
@ -557,32 +693,32 @@ public:
|
||||||
{
|
{
|
||||||
sha1_init(&sha);
|
sha1_init(&sha);
|
||||||
sha1_write(&sha, ptr0 + (j+1)*0x400, 0x400);
|
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_init(&sha);
|
||||||
sha1_write(&sha, (char*)h0, 0x26C);
|
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);
|
memset(ptr0+0x26C, 0, 0x014);
|
||||||
}
|
}
|
||||||
|
|
||||||
sha1_init(&sha);
|
sha1_init(&sha);
|
||||||
sha1_write(&sha, (char*)h1, 0x0A0);
|
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)
|
for (int c=0 ; c<8 ; ++c)
|
||||||
{
|
{
|
||||||
char* ptr0 = ptr1 + c*0x8000;
|
char* ptr0 = ptr1 + c*0x8000;
|
||||||
memcpy(ptr0+0x280, h1, 0x0A0);
|
memmove(ptr0+0x280, h1, 0x0A0);
|
||||||
memset(ptr0+0x320, 0, 0x020);
|
memset(ptr0+0x320, 0, 0x020);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sha1_init(&sha);
|
sha1_init(&sha);
|
||||||
sha1_write(&sha, (char*)h2, 0x0A0);
|
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)
|
for (int s=0 ; s<8 ; ++s)
|
||||||
{
|
{
|
||||||
|
@ -590,7 +726,7 @@ public:
|
||||||
for (int c=0 ; c<8 ; ++c)
|
for (int c=0 ; c<8 ; ++c)
|
||||||
{
|
{
|
||||||
char* ptr0 = ptr1 + c*0x8000;
|
char* ptr0 = ptr1 + c*0x8000;
|
||||||
memcpy(ptr0+0x340, h2, 0x0A0);
|
memmove(ptr0+0x340, h2, 0x0A0);
|
||||||
memset(ptr0+0x3E0, 0, 0x020);
|
memset(ptr0+0x3E0, 0, 0x020);
|
||||||
m_parent.m_aes->encrypt(ZEROIV, (uint8_t*)ptr0, (uint8_t*)ptr0, 0x400);
|
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);
|
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)
|
if (src)
|
||||||
{
|
{
|
||||||
memcpy(m_buf + block * 0x8000 + 0x400 + cacheOffset, src, cacheSize);
|
memmove(m_buf + block * 0x8000 + 0x400 + cacheOffset, src, cacheSize);
|
||||||
src += cacheSize;
|
src += cacheSize;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -680,9 +816,8 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
PartitionBuilderWii(DiscBuilderBase& parent, Kind kind,
|
PartitionBuilderWii(DiscBuilderBase& parent, Kind kind, uint64_t baseOffset)
|
||||||
const char gameID[6], const char* gameTitle, uint64_t baseOffset)
|
: DiscBuilderBase::PartitionBuilderBase(parent, kind),
|
||||||
: DiscBuilderBase::PartitionBuilderBase(parent, kind, gameID, gameTitle),
|
|
||||||
m_baseOffset(baseOffset), m_aes(NewAES()) {}
|
m_baseOffset(baseOffset), m_aes(NewAES()) {}
|
||||||
|
|
||||||
uint64_t getCurUserEnd() const {return m_curUser;}
|
uint64_t getCurUserEnd() const {return m_curUser;}
|
||||||
|
@ -723,101 +858,28 @@ public:
|
||||||
return ret;
|
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 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 */
|
/* Write partition head up to H3 table */
|
||||||
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 */
|
|
||||||
std::unique_ptr<IFileIO::IWriteStream> ws = m_parent.getFileIO().beginWriteStream(m_baseOffset);
|
std::unique_ptr<IFileIO::IWriteStream> ws = m_parent.getFileIO().beginWriteStream(m_baseOffset);
|
||||||
if (!ws)
|
if (!ws)
|
||||||
return -1;
|
return -1;
|
||||||
size_t copySz = std::min(phSz, size_t(h3Off));
|
uint32_t h3Off, dataOff;
|
||||||
ws->write(phBuf, copySz);
|
uint8_t tkey[16], tkeyiv[16];
|
||||||
size_t remCopy = (h3Off > phSz) ? (h3Off - copySz) : 0;
|
uint8_t ccIdx;
|
||||||
for (size_t i=0 ; i<remCopy ; ++i)
|
std::unique_ptr<uint8_t[]> tmdData;
|
||||||
ws->write("", 1);
|
size_t tmdSz;
|
||||||
|
if (!cryptoFunc(*ws, h3Off, dataOff, ccIdx, tkey, tkeyiv, tmdData, tmdSz))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
m_userOffset = dataOff;
|
||||||
|
|
||||||
/* Prepare crypto pass */
|
/* Prepare crypto pass */
|
||||||
m_aes->setKey(COMMON_KEYS[ccIdx]);
|
m_aes->setKey(COMMON_KEYS[ccIdx]);
|
||||||
|
@ -847,8 +909,6 @@ public:
|
||||||
cws = beginWriteStream(0);
|
cws = beginWriteStream(0);
|
||||||
if (!cws)
|
if (!cws)
|
||||||
return -1;
|
return -1;
|
||||||
Header header(m_gameID, m_gameTitle.c_str(), true, 0, 0, 0);
|
|
||||||
header.write(*cws);
|
|
||||||
|
|
||||||
/* Compute boot table members and write */
|
/* Compute boot table members and write */
|
||||||
size_t fstOff = 0x2440 + ROUND_UP_32(apploaderSz);
|
size_t fstOff = 0x2440 + ROUND_UP_32(apploaderSz);
|
||||||
|
@ -863,13 +923,11 @@ public:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
cws->write(nullptr, 0x420 - sizeof(Header));
|
if (!headerFunc(*cws, m_dolOffset, fstOff, fstSz))
|
||||||
uint32_t vals[4];
|
return -1;
|
||||||
vals[0] = SBig(uint32_t(m_dolOffset >> uint64_t(2)));
|
|
||||||
vals[1] = SBig(uint32_t(fstOff >> uint64_t(2)));
|
if (!bi2Func(*cws))
|
||||||
vals[2] = SBig(uint32_t(fstSz));
|
return -1;
|
||||||
vals[3] = SBig(uint32_t(fstSz));
|
|
||||||
cws->write(vals, 16);
|
|
||||||
|
|
||||||
size_t xferSz = 0;
|
size_t xferSz = 0;
|
||||||
if (!apploaderFunc(*cws, xferSz))
|
if (!apploaderFunc(*cws, xferSz))
|
||||||
|
@ -958,42 +1016,151 @@ public:
|
||||||
return m_baseOffset + dataOff + groupCount * 0x200000;
|
return m_baseOffset + dataOff + groupCount * 0x200000;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t buildFromDirectory(const SystemChar* dirIn,
|
uint64_t buildFromDirectory(const SystemChar* dirIn)
|
||||||
const SystemChar* dolIn,
|
|
||||||
const SystemChar* apploaderIn,
|
|
||||||
const SystemChar* partHeadIn)
|
|
||||||
{
|
{
|
||||||
std::unique_ptr<IFileIO> ph = NewFileIO(partHeadIn);
|
SystemString dirStr(dirIn);
|
||||||
size_t phSz = ph->size();
|
|
||||||
std::unique_ptr<uint8_t[]> phBuf(new uint8_t[phSz]);
|
/* Check Ticket */
|
||||||
|
SystemString ticketIn = dirStr + _S("/ticket.bin");
|
||||||
|
Sstat theStat;
|
||||||
|
if (Stat(ticketIn.c_str(), &theStat))
|
||||||
{
|
{
|
||||||
auto rs = ph->beginReadStream();
|
LogModule.report(logvisor::Error, _S("unable to stat %s"), ticketIn.c_str());
|
||||||
if (!rs)
|
return -1;
|
||||||
return -1;
|
|
||||||
rs->read(phBuf.get(), phSz);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get Apploader Size */
|
/* Check TMD */
|
||||||
Sstat theStat;
|
SystemString tmdIn = dirStr + _S("/tmd.bin");
|
||||||
if (Stat(apploaderIn, &theStat))
|
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 -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _build(
|
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(bootIn.c_str())->beginReadStream();
|
||||||
std::unique_ptr<IFileIO::IReadStream> rs = NewFileIO(apploaderIn)->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)
|
if (!rs)
|
||||||
return false;
|
return false;
|
||||||
char buf[8192];
|
char buf[8192];
|
||||||
SystemString apploaderName(apploaderIn);
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
size_t rdSz = rs->read(buf, 8192);
|
size_t rdSz = rs->read(buf, 8192);
|
||||||
|
@ -1007,11 +1174,15 @@ public:
|
||||||
"apploader flows into user area (one or the other is too big)");
|
"apploader flows into user area (one or the other is too big)");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderName, xferSz);
|
m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderIn, xferSz);
|
||||||
}
|
}
|
||||||
++m_parent.m_progressIdx;
|
++m_parent.m_progressIdx;
|
||||||
return true;
|
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)
|
bool mergeFromDirectory(const PartitionWii* partIn, const SystemChar* dirIn)
|
||||||
|
@ -1020,13 +1191,44 @@ public:
|
||||||
std::unique_ptr<uint8_t[]> phBuf = partIn->readPartitionHeaderBuf(phSz);
|
std::unique_ptr<uint8_t[]> phBuf = partIn->readPartitionHeaderBuf(phSz);
|
||||||
|
|
||||||
return _build(
|
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
|
[this, partIn](IPartWriteStream& cws, size_t& xferSz) -> bool
|
||||||
{
|
{
|
||||||
cws.write(nullptr, 0x2440 - 0x430);
|
|
||||||
std::unique_ptr<uint8_t[]> apploaderBuf = partIn->getApploaderBuf();
|
std::unique_ptr<uint8_t[]> apploaderBuf = partIn->getApploaderBuf();
|
||||||
size_t apploaderSz = partIn->getApploaderSize();
|
size_t apploaderSz = partIn->getApploaderSize();
|
||||||
SystemString apploaderName(_S("<apploader>"));
|
SystemString apploaderName(_S("<apploader>"));
|
||||||
|
@ -1041,13 +1243,18 @@ public:
|
||||||
m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderName, xferSz);
|
m_parent.m_progressCB(m_parent.getProgressFactor(), apploaderName, xferSz);
|
||||||
++m_parent.m_progressIdx;
|
++m_parent.m_progressIdx;
|
||||||
return true;
|
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,
|
EBuildResult DiscBuilderWii::buildFromDirectory(const SystemChar* dirIn)
|
||||||
const SystemChar* apploaderIn, const SystemChar* partHeadIn)
|
|
||||||
{
|
{
|
||||||
|
SystemString dirStr(dirIn);
|
||||||
|
|
||||||
PartitionBuilderWii& pb = static_cast<PartitionBuilderWii&>(*m_partitions[0]);
|
PartitionBuilderWii& pb = static_cast<PartitionBuilderWii&>(*m_partitions[0]);
|
||||||
uint64_t filledSz = pb.m_baseOffset;
|
uint64_t filledSz = pb.m_baseOffset;
|
||||||
if (!m_fileIO->beginWriteStream())
|
if (!m_fileIO->beginWriteStream())
|
||||||
|
@ -1066,7 +1273,7 @@ EBuildResult DiscBuilderWii::buildFromDirectory(const SystemChar* dirIn, const S
|
||||||
ws->write("", 1);
|
ws->write("", 1);
|
||||||
|
|
||||||
/* Assemble image */
|
/* Assemble image */
|
||||||
filledSz = pb.buildFromDirectory(dirIn, dolIn, apploaderIn, partHeadIn);
|
filledSz = pb.buildFromDirectory(dirIn);
|
||||||
if (filledSz == -1)
|
if (filledSz == -1)
|
||||||
return EBuildResult::Failed;
|
return EBuildResult::Failed;
|
||||||
else if (filledSz >= uint64_t(m_discCapacity))
|
else if (filledSz >= uint64_t(m_discCapacity))
|
||||||
|
@ -1082,7 +1289,12 @@ EBuildResult DiscBuilderWii::buildFromDirectory(const SystemChar* dirIn, const S
|
||||||
ws = m_fileIO->beginWriteStream(0);
|
ws = m_fileIO->beginWriteStream(0);
|
||||||
if (!ws)
|
if (!ws)
|
||||||
return EBuildResult::Failed;
|
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);
|
header.write(*ws);
|
||||||
|
|
||||||
/* Populate partition info */
|
/* Populate partition info */
|
||||||
|
@ -1099,24 +1311,16 @@ EBuildResult DiscBuilderWii::buildFromDirectory(const SystemChar* dirIn, const S
|
||||||
ws->write(vals, 4);
|
ws->write(vals, 4);
|
||||||
|
|
||||||
/* Populate region info */
|
/* 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);
|
ws = m_fileIO->beginWriteStream(0x4E000);
|
||||||
if (!ws)
|
if (!ws)
|
||||||
return EBuildResult::Failed;
|
return EBuildResult::Failed;
|
||||||
const char* gameID = pb.getGameID();
|
ws->write(regionBuf, 0x20);
|
||||||
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);
|
|
||||||
|
|
||||||
/* Fill image to end */
|
/* Fill image to end */
|
||||||
ws = m_fileIO->beginWriteStream(filledSz);
|
ws = m_fileIO->beginWriteStream(filledSz);
|
||||||
|
@ -1139,10 +1343,9 @@ EBuildResult DiscBuilderWii::buildFromDirectory(const SystemChar* dirIn, const S
|
||||||
return EBuildResult::Success;
|
return EBuildResult::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t DiscBuilderWii::CalculateTotalSizeRequired(const SystemChar* dirIn, const SystemChar* dolIn,
|
uint64_t DiscBuilderWii::CalculateTotalSizeRequired(const SystemChar* dirIn, bool& dualLayer)
|
||||||
bool& dualLayer)
|
|
||||||
{
|
{
|
||||||
uint64_t sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dolIn, dirIn);
|
uint64_t sz = DiscBuilderBase::PartitionBuilderBase::CalculateTotalSizeBuild(dirIn);
|
||||||
if (sz == -1)
|
if (sz == -1)
|
||||||
return -1;
|
return -1;
|
||||||
auto szDiv = std::lldiv(sz, 0x1F0000);
|
auto szDiv = std::lldiv(sz, 0x1F0000);
|
||||||
|
@ -1158,12 +1361,10 @@ uint64_t DiscBuilderWii::CalculateTotalSizeRequired(const SystemChar* dirIn, con
|
||||||
return sz;
|
return sz;
|
||||||
}
|
}
|
||||||
|
|
||||||
DiscBuilderWii::DiscBuilderWii(const SystemChar* outPath, const char gameID[6], const char* gameTitle,
|
DiscBuilderWii::DiscBuilderWii(const SystemChar* outPath, bool dualLayer, FProgress progressCB)
|
||||||
bool dualLayer, FProgress progressCB)
|
|
||||||
: DiscBuilderBase(outPath, dualLayer ? 0x1FB4E0000 : 0x118240000, progressCB), m_dualLayer(dualLayer)
|
: DiscBuilderBase(outPath, dualLayer ? 0x1FB4E0000 : 0x118240000, progressCB), m_dualLayer(dualLayer)
|
||||||
{
|
{
|
||||||
PartitionBuilderWii* partBuilder = new PartitionBuilderWii(*this, PartitionBuilderBase::Kind::Data,
|
PartitionBuilderWii* partBuilder = new PartitionBuilderWii(*this, PartitionBuilderBase::Kind::Data, 0x200000);
|
||||||
gameID, gameTitle, 0x200000);
|
|
||||||
m_partitions.emplace_back(partBuilder);
|
m_partitions.emplace_back(partBuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1224,24 +1425,15 @@ EBuildResult DiscMergerWii::mergeFromDirectory(const SystemChar* dirIn)
|
||||||
ws->write(vals, 4);
|
ws->write(vals, 4);
|
||||||
|
|
||||||
/* Populate region info */
|
/* 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);
|
ws = m_builder.m_fileIO->beginWriteStream(0x4E000);
|
||||||
if (!ws)
|
if (!ws)
|
||||||
return EBuildResult::Failed;
|
return EBuildResult::Failed;
|
||||||
const char* gameID = pb.getGameID();
|
ws->write(regionBuf, 0x20);
|
||||||
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);
|
|
||||||
|
|
||||||
/* Fill image to end */
|
/* Fill image to end */
|
||||||
ws = m_builder.m_fileIO->beginWriteStream(filledSz);
|
ws = m_builder.m_fileIO->beginWriteStream(filledSz);
|
||||||
|
|
|
@ -128,7 +128,7 @@ public:
|
||||||
{
|
{
|
||||||
FSeek(fp, offset, whence);
|
FSeek(fp, offset, whence);
|
||||||
}
|
}
|
||||||
int64_t position()
|
uint64_t position() const
|
||||||
{
|
{
|
||||||
return FTell(fp);
|
return FTell(fp);
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,7 +145,7 @@ public:
|
||||||
li.QuadPart = offset;
|
li.QuadPart = offset;
|
||||||
SetFilePointerEx(fp, li, nullptr, whence);
|
SetFilePointerEx(fp, li, nullptr, whence);
|
||||||
}
|
}
|
||||||
int64_t position()
|
uint64_t position() const
|
||||||
{
|
{
|
||||||
LARGE_INTEGER li = {};
|
LARGE_INTEGER li = {};
|
||||||
LARGE_INTEGER res;
|
LARGE_INTEGER res;
|
||||||
|
|
Loading…
Reference in New Issue