mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2024-11-25 10:52:19 -05:00
91bd693ff6
* Bump LUS version * Removes the "now" boolean from the LUS resource bridge functions. * Bump LUS * More LUS bump * Update soh/soh/resource/importer/AudioSampleFactory.cpp --------- Co-authored-by: briaguya <70942617+briaguya-ai@users.noreply.github.com>
595 lines
16 KiB
C++
595 lines
16 KiB
C++
#include "Globals.h"
|
|
#include "Utils/Directory.h"
|
|
#include <Utils/DiskFile.h>
|
|
#include "Utils/Path.h"
|
|
#include "WarningHandler.h"
|
|
|
|
#include "ZAnimation.h"
|
|
ZNormalAnimation nAnim(nullptr);
|
|
ZCurveAnimation cAnim(nullptr);
|
|
ZLinkAnimation lAnim(nullptr);
|
|
ZLegacyAnimation lAnim2(nullptr);
|
|
|
|
#include "ZArray.h"
|
|
ZArray arr(nullptr);
|
|
|
|
#include "ZAudio.h"
|
|
ZAudio audio(nullptr);
|
|
|
|
#include "ZBackground.h"
|
|
ZBackground back(nullptr);
|
|
|
|
#include "ZBlob.h"
|
|
ZBlob blob(nullptr);
|
|
|
|
#include "ZCollision.h"
|
|
ZCollisionHeader colHeader(nullptr);
|
|
|
|
#include "ZCutscene.h"
|
|
ZCutscene cs(nullptr);
|
|
|
|
#include "ZLimb.h"
|
|
ZLimb limb(nullptr);
|
|
|
|
#include "ZMtx.h"
|
|
ZMtx mtx(nullptr);
|
|
|
|
#include "ZPath.h"
|
|
ZPath path(nullptr);
|
|
|
|
#include "ZPlayerAnimationData.h"
|
|
ZPlayerAnimationData pAnimData(nullptr);
|
|
|
|
#include "ZScalar.h"
|
|
ZScalar scalar(nullptr);
|
|
|
|
#include "ZSkeleton.h"
|
|
ZLimbTable limbTbl(nullptr);
|
|
ZSkeleton skel(nullptr);
|
|
|
|
#include "ZString.h"
|
|
ZString str(nullptr);
|
|
|
|
#include "ZSymbol.h"
|
|
ZSymbol sym(nullptr);
|
|
|
|
#include "ZText.h"
|
|
ZText txt(nullptr);
|
|
|
|
#include "ZTexture.h"
|
|
ZTexture tex(nullptr);
|
|
|
|
#include "ZVector.h"
|
|
ZVector vec(nullptr);
|
|
|
|
#include "ZVtx.h"
|
|
ZVtx vtx(nullptr);
|
|
|
|
#include "ZRoom/ZRoom.h"
|
|
ZRoom room(nullptr);
|
|
|
|
#include "ZFile.h"
|
|
#include "ZTexture.h"
|
|
|
|
#include "CrashHandler.h"
|
|
|
|
#include <string>
|
|
#include <string_view>
|
|
#include "tinyxml2.h"
|
|
#include <ctpl_stl.h>
|
|
|
|
//extern const char gBuildHash[];
|
|
const char gBuildHash[] = "";
|
|
|
|
bool Parse(const fs::path& xmlFilePath, const fs::path& basePath, const fs::path& outPath,
|
|
ZFileMode fileMode, int workerID);
|
|
|
|
void BuildAssetTexture(const fs::path& pngFilePath, TextureType texType, const fs::path& outPath);
|
|
void BuildAssetBackground(const fs::path& imageFilePath, const fs::path& outPath);
|
|
void BuildAssetBlob(const fs::path& blobFilePath, const fs::path& outPath);
|
|
int ExtractFunc(int workerID, int fileListSize, std::string fileListItem, ZFileMode fileMode);
|
|
|
|
volatile int numWorkersLeft = 0;
|
|
|
|
extern void ImportExporters();
|
|
|
|
extern "C" int zapd_main(int argc, char* argv[])
|
|
{
|
|
// Syntax: ZAPD.out [mode (btex/bovl/e)] (Arbritrary Number of Arguments)
|
|
|
|
if (argc < 2)
|
|
{
|
|
printf("ZAPD.out (%s) [mode (btex/bovl/bsf/bblb/bmdlintr/bamnintr/e)] ...\n", gBuildHash);
|
|
return 1;
|
|
}
|
|
|
|
Globals* g = new Globals();
|
|
WarningHandler::Init(argc, argv);
|
|
|
|
for (int i = 1; i < argc; i++)
|
|
{
|
|
if (!strcmp(argv[i], "--version"))
|
|
{
|
|
printf("ZAPD.out %s\n", gBuildHash);
|
|
return 0;
|
|
}
|
|
else if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-h"))
|
|
{
|
|
printf("Congratulations!\n");
|
|
printf("You just found the (unimplemented and undocumented) ZAPD's help message.\n");
|
|
printf("Feel free to implement it if you want :D\n");
|
|
|
|
WarningHandler::PrintHelp();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Parse other "commands"
|
|
for (int32_t i = 2; i < argc; i++)
|
|
{
|
|
std::string arg = argv[i];
|
|
|
|
if (arg == "-o" || arg == "--outputpath") // Set output path
|
|
{
|
|
Globals::Instance->outputPath = argv[++i];
|
|
|
|
if (Globals::Instance->sourceOutputPath == "")
|
|
Globals::Instance->sourceOutputPath = Globals::Instance->outputPath;
|
|
}
|
|
else if (arg == "-i" || arg == "--inputpath") // Set input path
|
|
{
|
|
Globals::Instance->inputPath = argv[++i];
|
|
}
|
|
else if (arg == "-b" || arg == "--baserompath") // Set baserom path
|
|
{
|
|
Globals::Instance->baseRomPath = argv[++i];
|
|
}
|
|
else if (arg == "-osf") // Set source output path
|
|
{
|
|
Globals::Instance->sourceOutputPath = argv[++i];
|
|
}
|
|
else if (arg == "-gsf") // Generate source file during extraction
|
|
{
|
|
Globals::Instance->genSourceFile = std::string_view(argv[++i]) == "1";
|
|
}
|
|
else if (arg == "-tm") // Test Mode (enables certain experimental features)
|
|
{
|
|
Globals::Instance->testMode = std::string_view(argv[++i]) == "1";
|
|
}
|
|
else if (arg == "-crc" ||
|
|
arg == "--output-crc") // Outputs a CRC file for each extracted texture.
|
|
{
|
|
Globals::Instance->testMode = std::string_view(argv[++i]) == "1";
|
|
}
|
|
else if (arg == "-ulzdl") // Use Legacy ZDisplay List
|
|
{
|
|
Globals::Instance->useLegacyZDList = std::string_view(argv[++i]) == "1";
|
|
}
|
|
else if (arg == "-profile") // Enable profiling
|
|
{
|
|
Globals::Instance->profile = std::string_view(argv[++i]) == "1";
|
|
}
|
|
else if (arg ==
|
|
"-uer") // Split resources into their individual components (enabled by default)
|
|
// TODO: We may wish to make this a part of the config file...
|
|
{
|
|
Globals::Instance->useExternalResources = std::string_view(argv[++i]) == "1";
|
|
}
|
|
else if (arg == "-tt") // Set texture type
|
|
{
|
|
Globals::Instance->texType = ZTexture::GetTextureTypeFromString(argv[++i]);
|
|
}
|
|
else if (arg == "-fl") // Set baserom filelist path
|
|
{
|
|
Globals::Instance->fileListPath = argv[++i];
|
|
}
|
|
else if (arg == "-rconf") // Read Config File
|
|
{
|
|
Globals::Instance->cfg.ReadConfigFile(argv[++i]);
|
|
}
|
|
else if (arg == "-eh") // Enable Error Handler
|
|
{
|
|
CrashHandler_Init();
|
|
}
|
|
else if (arg == "-v") // Verbose
|
|
{
|
|
Globals::Instance->verbosity = static_cast<VerbosityLevel>(strtol(argv[++i], NULL, 16));
|
|
}
|
|
else if (arg == "-vu" || arg == "--verbose-unaccounted") // Verbose unaccounted
|
|
{
|
|
Globals::Instance->verboseUnaccounted = true;
|
|
}
|
|
else if (arg == "-se" || arg == "--set-exporter") // Set Current Exporter
|
|
{
|
|
ImportExporters();
|
|
Globals::Instance->currentExporter = argv[++i];
|
|
}
|
|
else if (arg == "--gcc-compat") // GCC compatibility
|
|
{
|
|
Globals::Instance->gccCompat = true;
|
|
}
|
|
else if (arg == "-s" || arg == "--static")
|
|
{
|
|
Globals::Instance->forceStatic = true;
|
|
}
|
|
else if (arg == "-us" || arg == "--unaccounted-static")
|
|
{
|
|
Globals::Instance->forceUnaccountedStatic = true;
|
|
}
|
|
else if (arg == "-brt" || arg == "--build-raw-texture")
|
|
{
|
|
Globals::Instance->buildRawTexture = true;
|
|
}
|
|
else if (arg == "--norom")
|
|
{
|
|
Globals::Instance->onlyGenSohOtr = true;
|
|
}
|
|
}
|
|
|
|
|
|
// Parse File Mode
|
|
ExporterSet* exporterSet = Globals::Instance->GetExporterSet();
|
|
|
|
if(Globals::Instance->onlyGenSohOtr) {
|
|
exporterSet->endProgramFunc();
|
|
|
|
delete g;
|
|
return 0;
|
|
}
|
|
|
|
std::string buildMode = argv[1];
|
|
ZFileMode fileMode = ZFileMode::Invalid;
|
|
|
|
if (buildMode == "btex")
|
|
fileMode = ZFileMode::BuildTexture;
|
|
else if (buildMode == "bren")
|
|
fileMode = ZFileMode::BuildBackground;
|
|
else if (buildMode == "bsf")
|
|
fileMode = ZFileMode::BuildSourceFile;
|
|
else if (buildMode == "bblb")
|
|
fileMode = ZFileMode::BuildBlob;
|
|
else if (buildMode == "e")
|
|
fileMode = ZFileMode::Extract;
|
|
else if (buildMode == "ed")
|
|
fileMode = ZFileMode::ExtractDirectory;
|
|
else if (exporterSet != nullptr && exporterSet->parseFileModeFunc != nullptr)
|
|
exporterSet->parseFileModeFunc(buildMode, fileMode);
|
|
|
|
if (fileMode == ZFileMode::Invalid)
|
|
{
|
|
printf("Error: Invalid file mode '%s'\n", buildMode.c_str());
|
|
return 1;
|
|
}
|
|
|
|
Globals::Instance->fileMode = fileMode;
|
|
|
|
if (fileMode == ZFileMode::ExtractDirectory)
|
|
Globals::Instance->rom = new ZRom(Globals::Instance->baseRomPath.string());
|
|
|
|
// We've parsed through our commands once. If an exporter exists, it's been set by now.
|
|
// Now we'll parse through them again but pass them on to our exporter if one is available.
|
|
|
|
if (exporterSet != nullptr && exporterSet->parseArgsFunc != nullptr)
|
|
{
|
|
for (int32_t i = 2; i < argc; i++)
|
|
exporterSet->parseArgsFunc(argc, argv, i);
|
|
}
|
|
|
|
if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_INFO)
|
|
printf("ZAPD: Zelda Asset Processor For Decomp: %s\n", gBuildHash);
|
|
|
|
if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_DEBUG)
|
|
{
|
|
WarningHandler::PrintWarningsDebugInfo();
|
|
}
|
|
|
|
// TODO: switch
|
|
if (fileMode == ZFileMode::Extract || fileMode == ZFileMode::BuildSourceFile || fileMode == ZFileMode::ExtractDirectory)
|
|
{
|
|
bool procFileModeSuccess = false;
|
|
|
|
if (exporterSet != nullptr && exporterSet->processFileModeFunc != nullptr)
|
|
procFileModeSuccess = exporterSet->processFileModeFunc(fileMode);
|
|
|
|
if (!procFileModeSuccess)
|
|
{
|
|
if (fileMode == ZFileMode::ExtractDirectory)
|
|
{
|
|
std::vector<std::string> fileList =
|
|
Directory::ListFiles(Globals::Instance->inputPath.string());
|
|
|
|
const int num_threads = std::thread::hardware_concurrency();
|
|
ctpl::thread_pool pool(num_threads > 1 ? num_threads / 2 : 1);
|
|
|
|
bool parseSuccessful;
|
|
|
|
auto start = std::chrono::steady_clock::now();
|
|
int fileListSize = fileList.size();
|
|
Globals::Instance->singleThreaded = false;
|
|
|
|
for (int i = 0; i < fileListSize; i++)
|
|
Globals::Instance->workerData[i] = new FileWorker();
|
|
|
|
numWorkersLeft = fileListSize;
|
|
|
|
for (int i = 0; i < fileListSize; i++)
|
|
{
|
|
if (Globals::Instance->singleThreaded)
|
|
{
|
|
ExtractFunc(i, fileList.size(), fileList[i], fileMode);
|
|
}
|
|
else
|
|
{
|
|
std::string fileListItem = fileList[i];
|
|
pool.push([i, fileListSize, fileListItem, fileMode](int) {
|
|
ExtractFunc(i, fileListSize, fileListItem, fileMode);
|
|
});
|
|
}
|
|
}
|
|
|
|
if (!Globals::Instance->singleThreaded)
|
|
{
|
|
while (true)
|
|
{
|
|
if (numWorkersLeft <= 0)
|
|
break;
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
|
}
|
|
}
|
|
|
|
auto end = std::chrono::steady_clock::now();
|
|
auto diff =
|
|
std::chrono::duration_cast<std::chrono::seconds>(end - start).count();
|
|
|
|
printf("Generated OTR File Data in %i seconds\n", diff);
|
|
}
|
|
else
|
|
{
|
|
bool parseSuccessful;
|
|
|
|
for (auto& extFile : Globals::Instance->cfg.externalFiles)
|
|
{
|
|
fs::path externalXmlFilePath =
|
|
Globals::Instance->cfg.externalXmlFolder / extFile.xmlPath;
|
|
|
|
if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_INFO)
|
|
{
|
|
printf("Parsing external file from config: '%s'\n",
|
|
externalXmlFilePath.c_str());
|
|
}
|
|
|
|
parseSuccessful = Parse(externalXmlFilePath, Globals::Instance->baseRomPath,
|
|
extFile.outPath, ZFileMode::ExternalFile, 0);
|
|
|
|
if (!parseSuccessful)
|
|
return 1;
|
|
}
|
|
|
|
parseSuccessful =
|
|
Parse(Globals::Instance->inputPath, Globals::Instance->baseRomPath,
|
|
Globals::Instance->outputPath, fileMode, 0);
|
|
if (!parseSuccessful)
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
else if (fileMode == ZFileMode::BuildTexture)
|
|
{
|
|
TextureType texType = Globals::Instance->texType;
|
|
BuildAssetTexture(Globals::Instance->inputPath, texType, Globals::Instance->outputPath);
|
|
}
|
|
else if (fileMode == ZFileMode::BuildBackground)
|
|
{
|
|
BuildAssetBackground(Globals::Instance->inputPath, Globals::Instance->outputPath);
|
|
}
|
|
else if (fileMode == ZFileMode::BuildBlob)
|
|
{
|
|
BuildAssetBlob(Globals::Instance->inputPath, Globals::Instance->outputPath);
|
|
}
|
|
|
|
if (exporterSet != nullptr && exporterSet->endProgramFunc != nullptr)
|
|
exporterSet->endProgramFunc();
|
|
|
|
end:
|
|
delete exporterSet;
|
|
|
|
//Globals::Instance->GetExporterSet() = nullptr; //TODO NULL this out. Compiler complains about lvalue assignment.
|
|
|
|
delete g;
|
|
return 0;
|
|
}
|
|
|
|
int ExtractFunc(int workerID, int fileListSize, std::string fileListItem, ZFileMode fileMode)
|
|
{
|
|
bool parseSuccessful;
|
|
|
|
printf("(%i / %i): %s\n", (workerID + 1), fileListSize, fileListItem.c_str());
|
|
|
|
for (auto& extFile : Globals::Instance->cfg.externalFiles)
|
|
{
|
|
fs::path externalXmlFilePath = Globals::Instance->cfg.externalXmlFolder / extFile.xmlPath;
|
|
|
|
if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_INFO)
|
|
{
|
|
printf("Parsing external file from config: '%s'\n", externalXmlFilePath.c_str());
|
|
}
|
|
|
|
parseSuccessful = Parse(externalXmlFilePath, Globals::Instance->baseRomPath,
|
|
extFile.outPath, ZFileMode::ExternalFile, workerID);
|
|
|
|
if (!parseSuccessful)
|
|
return 1;
|
|
}
|
|
|
|
parseSuccessful = Parse(fileListItem, Globals::Instance->baseRomPath,
|
|
Globals::Instance->outputPath, fileMode, workerID);
|
|
|
|
if (!parseSuccessful)
|
|
return 1;
|
|
|
|
if (Globals::Instance->singleThreaded)
|
|
{
|
|
for (int i = 0; i < Globals::Instance->files.size(); i++)
|
|
{
|
|
delete Globals::Instance->files[i];
|
|
Globals::Instance->files.erase(Globals::Instance->files.begin() + i);
|
|
i--;
|
|
}
|
|
|
|
Globals::Instance->externalFiles.clear();
|
|
Globals::Instance->segments.clear();
|
|
Globals::Instance->cfg.segmentRefFiles.clear();
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < Globals::Instance->workerData[workerID]->files.size(); i++)
|
|
{
|
|
delete Globals::Instance->workerData[workerID]->files[i];
|
|
Globals::Instance->workerData[workerID]->files.erase(
|
|
Globals::Instance->workerData[workerID]->files.begin() +
|
|
i);
|
|
i--;
|
|
}
|
|
|
|
Globals::Instance->workerData[workerID]->externalFiles.clear();
|
|
Globals::Instance->workerData[workerID]->segments.clear();
|
|
Globals::Instance->workerData[workerID]->segmentRefFiles.clear();
|
|
|
|
numWorkersLeft--;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool Parse(const fs::path& xmlFilePath, const fs::path& basePath, const fs::path& outPath,
|
|
ZFileMode fileMode, int workerID)
|
|
{
|
|
tinyxml2::XMLDocument doc;
|
|
tinyxml2::XMLError eResult = doc.LoadFile(xmlFilePath.string().c_str());
|
|
|
|
if (eResult != tinyxml2::XML_SUCCESS)
|
|
{
|
|
// TODO: use XMLDocument::ErrorIDToName to get more specific error messages here
|
|
HANDLE_ERROR(WarningType::InvalidXML,
|
|
StringHelper::Sprintf("invalid XML file: '%s'", xmlFilePath.c_str()), "");
|
|
return false;
|
|
}
|
|
|
|
tinyxml2::XMLNode* root = doc.FirstChild();
|
|
|
|
if (root == nullptr)
|
|
{
|
|
HANDLE_WARNING(
|
|
WarningType::InvalidXML,
|
|
StringHelper::Sprintf("missing Root tag in xml file: '%s'", xmlFilePath.c_str()), "");
|
|
return false;
|
|
}
|
|
|
|
for (tinyxml2::XMLElement* child = root->FirstChildElement(); child != NULL;
|
|
child = child->NextSiblingElement())
|
|
{
|
|
if (std::string_view(child->Name()) == "File")
|
|
{
|
|
ZFile* file = new ZFile(fileMode, child, basePath, outPath, "", xmlFilePath, workerID);
|
|
Globals::Instance->AddFile(file, workerID);
|
|
if (fileMode == ZFileMode::ExternalFile)
|
|
{
|
|
Globals::Instance->AddExternalFile(file, workerID);
|
|
file->isExternalFile = true;
|
|
}
|
|
}
|
|
else if (std::string(child->Name()) == "ExternalFile")
|
|
{
|
|
const char* xmlPathValue = child->Attribute("XmlPath");
|
|
if (xmlPathValue == nullptr)
|
|
{
|
|
throw std::runtime_error(StringHelper::Sprintf(
|
|
"Parse: Fatal error in '%s'.\n"
|
|
"\t Missing 'XmlPath' attribute in `ExternalFile` element.\n",
|
|
xmlFilePath.c_str()));
|
|
}
|
|
const char* outPathValue = child->Attribute("OutPath");
|
|
if (outPathValue == nullptr)
|
|
{
|
|
throw std::runtime_error(StringHelper::Sprintf(
|
|
"Parse: Fatal error in '%s'.\n"
|
|
"\t Missing 'OutPath' attribute in `ExternalFile` element.\n",
|
|
xmlFilePath.c_str()));
|
|
}
|
|
|
|
fs::path externalXmlFilePath =
|
|
Globals::Instance->cfg.externalXmlFolder / fs::path(xmlPathValue);
|
|
fs::path externalOutFilePath = fs::path(outPathValue);
|
|
|
|
if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_INFO)
|
|
{
|
|
printf("Parsing external file: '%s'\n", externalXmlFilePath.c_str());
|
|
}
|
|
|
|
// Recursion. What can go wrong?
|
|
Parse(externalXmlFilePath, basePath, externalOutFilePath, ZFileMode::ExternalFile, workerID);
|
|
}
|
|
else
|
|
{
|
|
std::string errorHeader =
|
|
StringHelper::Sprintf("when parsing file '%s'", xmlFilePath.c_str());
|
|
std::string errorBody = StringHelper::Sprintf(
|
|
"Found a resource outside a File element: '%s'", child->Name());
|
|
HANDLE_ERROR(WarningType::InvalidXML, errorHeader, errorBody);
|
|
}
|
|
}
|
|
|
|
if (fileMode != ZFileMode::ExternalFile)
|
|
{
|
|
ExporterSet* exporterSet = Globals::Instance->GetExporterSet();
|
|
|
|
if (exporterSet != nullptr && exporterSet->beginXMLFunc != nullptr)
|
|
exporterSet->beginXMLFunc();
|
|
|
|
std::vector<ZFile*> files;
|
|
|
|
if (Globals::Instance->singleThreaded)
|
|
files = Globals::Instance->files;
|
|
else
|
|
files = Globals::Instance->workerData[workerID]->files;
|
|
|
|
for (ZFile* file : files)
|
|
{
|
|
if (fileMode == ZFileMode::BuildSourceFile)
|
|
file->BuildSourceFile();
|
|
else
|
|
file->ExtractResources();
|
|
}
|
|
|
|
if (exporterSet != nullptr && exporterSet->endXMLFunc != nullptr)
|
|
exporterSet->endXMLFunc();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void BuildAssetTexture(const fs::path& pngFilePath, TextureType texType, const fs::path& outPath)
|
|
{
|
|
return Globals::Instance->BuildAssetTexture(pngFilePath, texType, outPath);
|
|
}
|
|
|
|
void BuildAssetBackground(const fs::path& imageFilePath, const fs::path& outPath)
|
|
{
|
|
ZBackground background(nullptr);
|
|
background.ParseBinaryFile(imageFilePath.string(), false);
|
|
|
|
DiskFile::WriteAllText(outPath.string(), background.GetBodySourceCode());
|
|
}
|
|
|
|
void BuildAssetBlob(const fs::path& blobFilePath, const fs::path& outPath)
|
|
{
|
|
ZBlob* blob = ZBlob::FromFile(blobFilePath.string());
|
|
std::string name = outPath.stem().string(); // filename without extension
|
|
|
|
std::string src = blob->GetBodySourceCode();
|
|
|
|
DiskFile::WriteAllText(outPath.string(), src);
|
|
|
|
delete blob;
|
|
}
|