2018-10-07 03:38:44 +00:00
|
|
|
#pragma once
|
2018-04-15 06:04:08 +00:00
|
|
|
|
|
|
|
#if HECL_HAS_NOD
|
|
|
|
|
|
|
|
#include <vector>
|
|
|
|
#include <string>
|
|
|
|
#include "ToolBase.hpp"
|
|
|
|
#include <cstdio>
|
|
|
|
#include "nod/DiscGCN.hpp"
|
|
|
|
#include "nod/DiscWii.hpp"
|
|
|
|
#include "athena/FileReader.hpp"
|
|
|
|
|
2018-12-08 05:18:42 +00:00
|
|
|
class ToolImage final : public ToolBase {
|
|
|
|
std::unique_ptr<hecl::Database::Project> m_fallbackProj;
|
|
|
|
hecl::Database::Project* m_useProj;
|
2018-04-15 06:04:08 +00:00
|
|
|
|
|
|
|
public:
|
2019-08-20 02:49:24 +00:00
|
|
|
explicit ToolImage(const ToolPassInfo& info) : ToolBase(info), m_useProj(info.project) {
|
2018-12-08 05:18:42 +00:00
|
|
|
if (!info.project)
|
2020-04-11 22:48:11 +00:00
|
|
|
LogModule.report(logvisor::Fatal, FMT_STRING("hecl image must be ran within a project directory"));
|
2018-12-08 05:18:42 +00:00
|
|
|
|
|
|
|
/* Scan args */
|
|
|
|
if (info.args.size()) {
|
|
|
|
/* See if project path is supplied via args and use that over the getcwd one */
|
2021-06-30 18:20:45 +00:00
|
|
|
for (const std::string& arg : info.args) {
|
2018-12-08 05:18:42 +00:00
|
|
|
if (arg.empty())
|
|
|
|
continue;
|
|
|
|
|
2021-06-30 18:20:45 +00:00
|
|
|
std::string subPath;
|
2018-12-08 05:18:42 +00:00
|
|
|
hecl::ProjectRootPath root = hecl::SearchForProject(MakePathArgAbsolute(arg, info.cwd), subPath);
|
|
|
|
|
|
|
|
if (root) {
|
|
|
|
if (!m_fallbackProj) {
|
|
|
|
m_fallbackProj.reset(new hecl::Database::Project(root));
|
|
|
|
m_useProj = m_fallbackProj.get();
|
|
|
|
break;
|
|
|
|
}
|
2018-04-15 06:04:08 +00:00
|
|
|
}
|
2018-12-08 05:18:42 +00:00
|
|
|
}
|
2018-04-15 06:04:08 +00:00
|
|
|
}
|
2018-12-08 05:18:42 +00:00
|
|
|
if (!m_useProj)
|
|
|
|
LogModule.report(logvisor::Fatal,
|
2020-04-11 22:48:11 +00:00
|
|
|
FMT_STRING("hecl image must be ran within a project directory or "
|
2019-07-20 04:22:58 +00:00
|
|
|
"provided a path within a project"));
|
2018-12-08 05:18:42 +00:00
|
|
|
}
|
|
|
|
|
2019-08-20 02:34:12 +00:00
|
|
|
~ToolImage() override = default;
|
2018-12-08 05:18:42 +00:00
|
|
|
|
|
|
|
static void Help(HelpOutput& help) {
|
2021-06-30 18:20:45 +00:00
|
|
|
help.secHead("NAME");
|
2018-12-08 05:18:42 +00:00
|
|
|
help.beginWrap();
|
2021-06-30 18:20:45 +00:00
|
|
|
help.wrap("hecl-image - Generate GameCube/Wii disc image from packaged files\n");
|
2018-12-08 05:18:42 +00:00
|
|
|
help.endWrap();
|
|
|
|
|
2021-06-30 18:20:45 +00:00
|
|
|
help.secHead("SYNOPSIS");
|
2018-12-08 05:18:42 +00:00
|
|
|
help.beginWrap();
|
2021-06-30 18:20:45 +00:00
|
|
|
help.wrap("hecl image [<input-dir>]\n");
|
2018-12-08 05:18:42 +00:00
|
|
|
help.endWrap();
|
|
|
|
|
2021-06-30 18:20:45 +00:00
|
|
|
help.secHead("DESCRIPTION");
|
2018-12-08 05:18:42 +00:00
|
|
|
help.beginWrap();
|
2021-06-30 18:20:45 +00:00
|
|
|
help.wrap("This command uses the current contents of `out` to generate a GameCube or "
|
|
|
|
"Wii disc image. `hecl package` must have been run previously to be effective.\n");
|
2018-12-08 05:18:42 +00:00
|
|
|
help.endWrap();
|
2018-04-15 06:04:08 +00:00
|
|
|
|
2021-06-30 18:20:45 +00:00
|
|
|
help.secHead("OPTIONS");
|
|
|
|
help.optionHead("<input-dir>", "input directory");
|
2018-12-08 05:18:42 +00:00
|
|
|
help.beginWrap();
|
2021-06-30 18:20:45 +00:00
|
|
|
help.wrap("Specifies a project subdirectory to root the resulting image from. "
|
|
|
|
"Project must contain an out/sys and out/files directory to succeed.\n");
|
2018-12-08 05:18:42 +00:00
|
|
|
help.endWrap();
|
|
|
|
}
|
|
|
|
|
2021-06-30 18:20:45 +00:00
|
|
|
std::string_view toolName() const override { return "image"sv; }
|
2018-12-08 05:18:42 +00:00
|
|
|
|
2019-08-20 02:34:12 +00:00
|
|
|
int run() override {
|
2018-12-08 05:18:42 +00:00
|
|
|
if (XTERM_COLOR)
|
2021-06-30 18:20:45 +00:00
|
|
|
fmt::print(FMT_STRING("" GREEN BOLD "ABOUT TO IMAGE:" NORMAL "\n"));
|
2018-12-08 05:18:42 +00:00
|
|
|
else
|
2021-06-30 18:20:45 +00:00
|
|
|
fmt::print(FMT_STRING("ABOUT TO IMAGE:\n"));
|
2018-12-08 05:18:42 +00:00
|
|
|
|
2021-06-30 18:20:45 +00:00
|
|
|
fmt::print(FMT_STRING(" {}\n"), m_useProj->getProjectRootPath().getAbsolutePath());
|
2018-12-08 05:18:42 +00:00
|
|
|
fflush(stdout);
|
|
|
|
|
|
|
|
if (continuePrompt()) {
|
2021-06-30 18:20:45 +00:00
|
|
|
hecl::ProjectPath outPath(m_useProj->getProjectWorkingPath(), "out");
|
2018-12-08 05:18:42 +00:00
|
|
|
if (!outPath.isDirectory()) {
|
2021-06-30 18:20:45 +00:00
|
|
|
LogModule.report(logvisor::Error, FMT_STRING("{} is not a directory"), outPath.getAbsolutePath());
|
2018-12-08 05:18:42 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-06-30 18:20:45 +00:00
|
|
|
hecl::ProjectPath bootBinPath(outPath, "sys/boot.bin");
|
2018-12-08 05:18:42 +00:00
|
|
|
if (!bootBinPath.isFile()) {
|
2021-06-30 18:20:45 +00:00
|
|
|
LogModule.report(logvisor::Error, FMT_STRING("{} is not a file"), bootBinPath.getAbsolutePath());
|
2018-12-08 05:18:42 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
athena::io::FileReader r(bootBinPath.getAbsolutePath());
|
|
|
|
if (r.hasError()) {
|
2021-06-30 18:20:45 +00:00
|
|
|
LogModule.report(logvisor::Error, FMT_STRING("unable to open {}"), bootBinPath.getAbsolutePath());
|
2018-12-08 05:18:42 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
std::string id = r.readString(6);
|
|
|
|
r.close();
|
|
|
|
|
2021-06-30 18:20:45 +00:00
|
|
|
std::string fileOut = std::string(outPath.getAbsolutePath()) + '/' + id;
|
2018-12-08 05:18:42 +00:00
|
|
|
hecl::MultiProgressPrinter printer(true);
|
2021-06-30 18:20:45 +00:00
|
|
|
auto progFunc = [&printer](float totalProg, std::string_view fileName, size_t fileBytesXfered) {
|
2021-06-28 21:01:04 +00:00
|
|
|
printer.print(fileName, std::nullopt, totalProg);
|
2018-12-08 05:18:42 +00:00
|
|
|
};
|
|
|
|
if (id[0] == 'G') {
|
2021-06-30 18:20:45 +00:00
|
|
|
fileOut += ".gcm";
|
2019-06-12 02:01:19 +00:00
|
|
|
if (nod::DiscBuilderGCN::CalculateTotalSizeRequired(outPath.getAbsolutePath()) == UINT64_MAX)
|
2018-12-08 05:18:42 +00:00
|
|
|
return 1;
|
2021-06-30 18:20:45 +00:00
|
|
|
LogModule.report(logvisor::Info, FMT_STRING("Generating {} as GameCube image"), fileOut);
|
2018-12-08 05:18:42 +00:00
|
|
|
nod::DiscBuilderGCN db(fileOut, progFunc);
|
|
|
|
if (db.buildFromDirectory(outPath.getAbsolutePath()) != nod::EBuildResult::Success)
|
|
|
|
return 1;
|
|
|
|
} else {
|
2021-06-30 18:20:45 +00:00
|
|
|
fileOut += ".iso";
|
2018-12-08 05:18:42 +00:00
|
|
|
bool dualLayer;
|
2019-06-12 02:01:19 +00:00
|
|
|
if (nod::DiscBuilderWii::CalculateTotalSizeRequired(outPath.getAbsolutePath(), dualLayer) == UINT64_MAX)
|
2018-12-08 05:18:42 +00:00
|
|
|
return 1;
|
2021-06-30 18:20:45 +00:00
|
|
|
LogModule.report(logvisor::Info, FMT_STRING("Generating {} as {}-layer Wii image"), fileOut,
|
|
|
|
dualLayer ? "dual" : "single");
|
2018-12-08 05:18:42 +00:00
|
|
|
nod::DiscBuilderWii db(fileOut, dualLayer, progFunc);
|
|
|
|
if (db.buildFromDirectory(outPath.getAbsolutePath()) != nod::EBuildResult::Success)
|
|
|
|
return 1;
|
|
|
|
}
|
2018-04-15 06:04:08 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:18:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2018-04-15 06:04:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#endif
|