Zonebuilder improvements

This commit is contained in:
Louvenarde 2024-01-28 02:23:52 +01:00
parent f1ec16983a
commit 8fdde66bdc
7 changed files with 284 additions and 188 deletions

View File

@ -242,54 +242,14 @@ namespace Components
{
auto str = Game::SL_FindString("j_spinelower");
int interestingPart = 0;
for (size_t i = 0; i < anim->boneCount[Game::XAnimPartType::PART_TYPE_ALL]; i++)
{
if (anim->names[i] == str)
{
interestingPart = i;
//anim->names[i] = Game::SL_FindString("torso_stabilizer");
}
}
// Something interesting to do there
//for (size_t frameIndex = 0; frameIndex < anim->numframes; frameIndex++)
//{
// const auto delta = &anim->deltaPart[frameIndex];
// const auto frameCount = anim->numframes;
// if (delta->trans->size)
// {
// const auto test = delta->trans->u.frames;
// if (frameCount > 0xFF)
// {
// delta->trans->u.frames.indices._1
// buffer->saveArray(delta->trans->u.frames.indices._2, delta->trans->size + 1);
// }
// else
// {
// buffer->saveArray(delta->trans->u.frames.indices._1, delta->trans->size + 1);
// }
// if (delta->trans->u.frames.frames._1)
// {
// if (delta->trans->smallTrans)
// {
// buffer->save(delta->trans->u.frames.frames._1, 3, delta->trans->size + 1);
// }
// else
// {
// buffer->align(Utils::Stream::ALIGN_4);
// buffer->save(delta->trans->u.frames.frames._1, 6, delta->trans->size + 1);
// }
// }
// }
// else
// {
// buffer->save(delta->trans->u.frame0, 12);
// }
//}
}
}
@ -323,65 +283,14 @@ namespace Components
// Unlikely candidate
return;
}
//
static std::vector<std::string> names{};
names.clear();
static Utils::Memory::Allocator allocator{};
for (auto i = 0; i < model->numBones; i++)
{
const auto bone = model->boneNames[i];
const auto boneName = Game::SL_ConvertToString(bone);
const auto str = std::format("{} => q({} {} {} {}) t({} {} {})",
boneName,
model->baseMat[i - 1].quat[0],
model->baseMat[i - 1].quat[1],
model->baseMat[i - 1].quat[2],
model->baseMat[i - 1].quat[3],
model->baseMat[i - 1].trans[0],
model->baseMat[i - 1].trans[1],
model->baseMat[i - 1].trans[2]
);
const auto duplicated = allocator.duplicateString(str);
names.push_back(
duplicated
);
}
//
Assets::IXModel::ConvertPlayerModelFromSingleplayerToMultiplayer(model, allocator);
printf("");
//
names.clear();
for (auto i = 1; i < model->numBones; i++)
//if (name == "body_urban_civ_female_a"s)
{
const auto bone = model->boneNames[i];
const auto boneName = Game::SL_ConvertToString(bone);
const auto m1 = i - 1;
const auto fmt = std::format("{} => q({} {} {} {}) t({} {} {})",
boneName,
model->quats[m1 * 4 + 0],
model->quats[m1 * 4 + 1],
model->quats[m1 * 4 + 2],
model->quats[m1 * 4 + 3],
model->trans[m1 * 3 + 0],
model->trans[m1 * 3 + 1],
model->trans[m1 * 3 + 2]
);
names.push_back(
fmt
);
Assets::IXModel::ConvertPlayerModelFromSingleplayerToMultiplayer(model, allocator);
printf("");
}
printf("");
//
}
if (type == Game::ASSET_TYPE_MATERIAL && (name == "gfx_distortion_knife_trail" || name == "gfx_distortion_heat_far" || name == "gfx_distortion_ring_light" || name == "gfx_distortion_heat") && asset.material->info.sortKey >= 43)

View File

@ -296,6 +296,7 @@ namespace Assets
std::string IXModel::GetParentOfBone(Game::XModel* model, uint8_t index)
{
assert(index > 0);
const auto parentIndex = GetParentIndexOfBone(model, index);
const auto boneName = Game::SL_ConvertToString(model->boneNames[parentIndex]);
return boneName;
@ -313,17 +314,17 @@ namespace Assets
auto vertsBlendOffset = 0;
int rebuiltPartBits[6]{};
int rebuiltPartBits[LENGTH]{};
std::unordered_set<uint8_t> affectingBones{};
const auto registerBoneAffectingSurface = [&](unsigned int offset) {
uint8_t index = static_cast<uint8_t>(surface->vertInfo.vertsBlend[offset] / sizeof(Game::DObjSkelMat));
highestBoneIndex = std::max(highestBoneIndex, index);
};
};
// 1 bone weight
for (auto vertIndex = 0; vertIndex < surface->vertInfo.vertCount[0]; vertIndex++)
for (unsigned int vertIndex = 0; vertIndex < surface->vertInfo.vertCount[0]; vertIndex++)
{
registerBoneAffectingSurface(vertsBlendOffset + 0);
@ -331,7 +332,7 @@ namespace Assets
}
// 2 bone weights
for (auto vertIndex = 0; vertIndex < surface->vertInfo.vertCount[1]; vertIndex++)
for (unsigned int vertIndex = 0; vertIndex < surface->vertInfo.vertCount[1]; vertIndex++)
{
registerBoneAffectingSurface(vertsBlendOffset + 0);
registerBoneAffectingSurface(vertsBlendOffset + 1);
@ -340,7 +341,7 @@ namespace Assets
}
// 3 bone weights
for (auto vertIndex = 0; vertIndex < surface->vertInfo.vertCount[2]; vertIndex++)
for (unsigned int vertIndex = 0; vertIndex < surface->vertInfo.vertCount[2]; vertIndex++)
{
registerBoneAffectingSurface(vertsBlendOffset + 0);
registerBoneAffectingSurface(vertsBlendOffset + 1);
@ -350,7 +351,7 @@ namespace Assets
}
// 4 bone weights
for (auto vertIndex = 0; vertIndex < surface->vertInfo.vertCount[3]; vertIndex++)
for (unsigned int vertIndex = 0; vertIndex < surface->vertInfo.vertCount[3]; vertIndex++)
{
registerBoneAffectingSurface(vertsBlendOffset + 0);
registerBoneAffectingSurface(vertsBlendOffset + 1);
@ -360,7 +361,7 @@ namespace Assets
vertsBlendOffset += 7;
}
for (auto vertListIndex = 0; vertListIndex < surface->vertListCount; vertListIndex++)
for (unsigned int vertListIndex = 0; vertListIndex < surface->vertListCount; vertListIndex++)
{
highestBoneIndex = std::max(highestBoneIndex, static_cast<uint8_t>(surface->vertList[vertListIndex].boneOffset / sizeof(Game::DObjSkelMat)));
}
@ -395,7 +396,7 @@ namespace Assets
assert(index < model->numBones);
affectingBones.emplace(index);
};
};
// 1 bone weight
@ -438,7 +439,7 @@ namespace Assets
for (auto vertListIndex = 0; vertListIndex < surface->vertListCount; vertListIndex++)
{
affectingBones.emplace(surface->vertList[vertListIndex].boneOffset / sizeof(Game::DObjSkelMat));
affectingBones.emplace(static_cast<uint8_t>(surface->vertList[vertListIndex].boneOffset / sizeof(Game::DObjSkelMat)));
}
// Actually rebuilding
@ -497,7 +498,7 @@ namespace Assets
const uint8_t newBoneIndex = atPosition;
const uint8_t newBoneIndexMinusRoot = atPosition - model->numRootBones;
Components::Logger::Print("Inserting bone {} at position {} (between {} and {})\n", boneName, atPosition, Game::SL_ConvertToString(model->boneNames[atPosition-1]), Game::SL_ConvertToString(model->boneNames[atPosition+1]));
Components::Logger::Print("Inserting bone {} at position {} (between {} and {})\n", boneName, atPosition, Game::SL_ConvertToString(model->boneNames[atPosition - 1]), Game::SL_ConvertToString(model->boneNames[atPosition + 1]));
// Reallocate
const auto newBoneNames = allocator.allocateArray<uint16_t>(newBoneCount);
@ -549,7 +550,7 @@ namespace Assets
// It's RELATIVE !
uint16_t quat[4]{};
quat[3] = 32767; // 0 0 0 32767
quat[3] = SHRT_MAX; // 0 0 0 1
float trans[3]{};
@ -570,7 +571,7 @@ namespace Assets
{
std::memcpy(&newBoneNames[atPosition + 1], &model->boneNames[atPosition], sizeof(uint16_t) * lengthOfSecondPart);
std::memcpy(&newMats[atPosition + 1], &model->baseMat[atPosition], sizeof(Game::DObjAnimMat) * lengthOfSecondPart);
std::memcpy(&newPartsClassification[atPosition+1], &model->partClassification[atPosition], lengthOfSecondPart);
std::memcpy(&newPartsClassification[atPosition + 1], &model->partClassification[atPosition], lengthOfSecondPart);
std::memcpy(&newBoneInfo[atPosition + 1], &model->boneInfo[atPosition], sizeof(Game::XBoneInfo) * lengthOfSecondPart);
std::memcpy(&newQuats[(atPositionM1 + 1) * 4], &model->quats[atPositionM1 * 4], sizeof(uint16_t) * 4 * lengthOfSecondPartM1);
std::memcpy(&newTrans[(atPositionM1 + 1) * 3], &model->trans[atPositionM1 * 3], sizeof(float) * 3 * lengthOfSecondPartM1);
@ -635,7 +636,7 @@ namespace Assets
surface->vertInfo.vertsBlend[offset] = static_cast<unsigned short>(index * sizeof(Game::DObjSkelMat));
}
};
};
// Fix bone offsets
if (surface->vertList)
@ -739,8 +740,9 @@ namespace Assets
void IXModel::TransferWeights(Game::XModel* model, const uint8_t origin, const uint8_t destination)
{
// Does not work
return;
const auto from = Game::SL_ConvertToString(model->boneNames[origin]);
const auto to = Game::SL_ConvertToString(model->boneNames[destination]);
Components::Logger::Print("Transferring bone weights from {} to {}\n", from, to);
const auto originalWeights = model->baseMat[origin].transWeight;
model->baseMat[origin].transWeight = model->baseMat[destination].transWeight;
@ -750,6 +752,12 @@ namespace Assets
{
const auto lod = &model->lodInfo[i];
if ((lod->partBits[5] & 0x1) == 0x1)
{
// surface lod already converted (more efficient)
continue;
}
for (int surfIndex = 0; surfIndex < lod->modelSurfs->numsurfs; surfIndex++)
{
auto vertsBlendOffset = 0u;
@ -760,12 +768,13 @@ namespace Assets
int index = static_cast<int>(surface->vertInfo.vertsBlend[offset] / sizeof(Game::DObjSkelMat));
if (index == origin)
{
if (index < 0 || index >= model->numBones - 1)
index = destination;
if (index < 0 || index >= model->numBones)
{
assert(false);
}
index = destination;
surface->vertInfo.vertsBlend[offset] = static_cast<unsigned short>(index * sizeof(Game::DObjSkelMat));
}
@ -778,13 +787,14 @@ namespace Assets
{
const auto vertList = &surface->vertList[vertListIndex];
auto index = vertList->boneOffset / sizeof(Game::DObjSkelMat);
if (index < 0 || index >= model->numBones - 1)
{
assert(false);
}
if (index == origin)
{
if (index < 0 || index >= model->numBones)
{
assert(false);
}
index = destination;
vertList->boneOffset = static_cast<unsigned short>(index * sizeof(Game::DObjSkelMat));
}
@ -834,6 +844,46 @@ namespace Assets
}
};
void IXModel::SetBoneTrans(Game::XModel* model, uint8_t boneIndex, bool baseMat, float x, float y, float z)
{
if (baseMat)
{
model->baseMat[boneIndex].trans[0] = x;
model->baseMat[boneIndex].trans[1] = y;
model->baseMat[boneIndex].trans[2] = z;
}
else
{
const auto index = boneIndex - model->numRootBones;
assert(index >= 0);
model->trans[index * 3 + 0] = x;
model->trans[index * 3 + 1] = y;
model->trans[index * 3 + 2] = z;
}
}
void IXModel::SetBoneQuaternion(Game::XModel* model, uint8_t boneIndex, bool baseMat, float x, float y, float z, float w)
{
if (baseMat)
{
model->baseMat[boneIndex].quat[0] = x;
model->baseMat[boneIndex].quat[1] = y;
model->baseMat[boneIndex].quat[2] = z;
model->baseMat[boneIndex].quat[3] = w;
}
else
{
const auto index = boneIndex - model->numRootBones;
assert(index >= 0);
model->quats[index * 4 + 0] = static_cast<uint16_t>(x * SHRT_MAX);
model->quats[index * 4 + 1] = static_cast<uint16_t>(y * SHRT_MAX);
model->quats[index * 4 + 2] = static_cast<uint16_t>(z * SHRT_MAX);
model->quats[index * 4 + 3] = static_cast<uint16_t>(w * SHRT_MAX);
}
}
void IXModel::ConvertPlayerModelFromSingleplayerToMultiplayer(Game::XModel* model, Utils::Memory::Allocator& allocator)
{
@ -866,24 +916,14 @@ namespace Assets
const auto root = GetIndexOfBone(model, "j_mainroot");
if (root < UCHAR_MAX) {
#if true
//// Works
//InsertBone(model, "offsetron_the_great_offsetter_of_bones", "j_mainroot", allocator);
//// Breaks the model
//InsertBone(model, "offsetron2_the_greater_offsetter_of_bones", "j_mainroot", allocator);
//for (auto lodIndex = 0; lodIndex < model->numLods; lodIndex++)
//{
// convertedSurfs.emplace(model->lodInfo[lodIndex].modelSurfs);
//}
//RebuildPartBits(model);
//return;
// Add pelvis
#if false
const uint8_t backLow = InsertBone(model, "back_low", "j_spinelower", allocator);
TransferWeights(model, GetIndexOfBone(model, "j_spinelower"), backLow);
SetParentIndexOfBone(model, GetIndexOfBone(model, "j_spineupper"), backLow);
#else
const uint8_t indexOfPelvis = InsertBone(model, "pelvis", "j_mainroot", allocator);
SetBoneQuaternion(model, indexOfPelvis, true, -0.494f, -0.506f, -0.506f, 0.494);
TransferWeights(model, root, indexOfPelvis);
@ -891,11 +931,23 @@ namespace Assets
SetParentIndexOfBone(model, GetIndexOfBone(model, "j_hip_ri"), indexOfPelvis);
SetParentIndexOfBone(model, GetIndexOfBone(model, "tag_stowed_hip_rear"), indexOfPelvis);
// These two are optional
if (GetIndexOfBone(model, "j_coatfront_le") == UCHAR_MAX)
{
InsertBone(model, "j_coatfront_le", "pelvis", allocator);
}
if (GetIndexOfBone(model, "j_coatfront_ri") == UCHAR_MAX)
{
InsertBone(model, "j_coatfront_ri", "pelvis", allocator);
}
const uint8_t torsoStabilizer = InsertBone(model, "torso_stabilizer", "pelvis", allocator);
SetParentIndexOfBone(model, GetIndexOfBone(model, "j_spinelower"), torsoStabilizer);
const uint8_t lowerSpine = GetIndexOfBone(model, "j_spinelower");
SetParentIndexOfBone(model, lowerSpine, torsoStabilizer);
const uint8_t backLow = InsertBone(model, "back_low", "j_spinelower", allocator);
TransferWeights(model, GetIndexOfBone(model, "j_spinelower"), backLow);
TransferWeights(model, lowerSpine, backLow);
SetParentIndexOfBone(model, GetIndexOfBone(model, "j_spineupper"), backLow);
const uint8_t backMid = InsertBone(model, "back_mid", "j_spineupper", allocator);
@ -908,29 +960,34 @@ namespace Assets
assert(backLow == GetIndexOfBone(model, "back_low"));
assert(backMid == GetIndexOfBone(model, "back_mid"));
// Fix up torso stabilizer
model->baseMat[torsoStabilizer].quat[0] = 0.F;
model->baseMat[torsoStabilizer].quat[1] = 0.F;
model->baseMat[torsoStabilizer].quat[2] = 0.F;
model->baseMat[torsoStabilizer].quat[3] = 1.F;
// Twister bone
SetBoneQuaternion(model, lowerSpine, false, -0.492f, -0.507f, -0.507f, 0.492f);
SetBoneQuaternion(model, torsoStabilizer, false, 0.494f, 0.506f, 0.506f, 0.494f);
const auto spineLowerM1 = GetIndexOfBone(model, "j_spinelower") - model->numRootBones;
// This doesn't feel like it should be necessary
model->trans[spineLowerM1 * 3 + 0] = 0.069828756;
model->trans[spineLowerM1 * 3 + 1] = -0.0f;
model->trans[spineLowerM1 * 3 + 2] = 5.2035017F;
// It is, on singleplayer models unfortunately. Could we add an extra bone to compensate this?
// Or compensate it another way?
SetBoneTrans(model, GetIndexOfBone(model, "j_spinelower"), false, 0.07, 0.0f, 5.2f);
//// Euler -180.000 88.572 -90.000
model->quats[(torsoStabilizer - model->numRootBones) * 4 + 0] = 16179; // 0.4952
model->quats[(torsoStabilizer - model->numRootBones) * 4 + 1] = 16586; // 0.5077
model->quats[(torsoStabilizer - model->numRootBones) * 4 + 2] = 16586; // 0.5077
model->quats[(torsoStabilizer - model->numRootBones) * 4 + 3] = 16178; // 0.4952
// These are often messed up on civilian models, but there is no obvious way to tell from code
const auto stowedBack = GetIndexOfBone(model, "tag_stowed_back");
if (stowedBack != UCHAR_MAX)
{
SetBoneTrans(model, stowedBack, false, -0.32f, -6.27f, -2.65F);
SetBoneQuaternion(model, stowedBack, false, -0.044, 0.088, -0.995, 0.025);
SetBoneTrans(model, stowedBack, true, -9.571f, -2.654f, 51.738f);
SetBoneQuaternion(model, stowedBack, true, -0.071f, 0.0f, -0.997f, 0.0f);
}
#else
const uint8_t torsoStabilizer = insertBone("torso_stabilizer", "j_mainroot");
transferWeights(getIndexOfBone("j_mainroot"), getIndexOfBone("torso_stabilizer"));
setParentIndexOfBone(getIndexOfBone("j_spinelower"), torsoStabilizer);
if (stowedBack != UCHAR_MAX)
{
const auto stowedRear = GetIndexOfBone(model, "tag_stowed_hip_rear");
SetBoneTrans(model, stowedRear, false, -0.75f, -6.45f, -4.99f);
SetBoneQuaternion(model, stowedRear, false, -0.553f, -0.062f, -0.049f, 0.830f);
SetBoneTrans(model, stowedBack, true, -9.866f, -4.989f, 36.315f);
SetBoneQuaternion(model, stowedRear, true, -0.054, -0.025f, -0.975f, 0.214f);
}
#endif
RebuildPartBits(model);

View File

@ -22,5 +22,8 @@ namespace Assets
static void RebuildPartBits(Game::XModel* model);
static uint8_t InsertBone(Game::XModel* model, const std::string& boneName, const std::string& parentName, Utils::Memory::Allocator& allocator);
static void TransferWeights(Game::XModel* model, const uint8_t origin, const uint8_t destination);
static void SetBoneTrans(Game::XModel* model, uint8_t boneIndex, bool baseMat, float x, float y, float z);
static void SetBoneQuaternion(Game::XModel* model, uint8_t boneIndex, bool baseMat, float x, float y, float z, float w);
};
}

View File

@ -314,9 +314,6 @@ namespace Components
QuickPatch::QuickPatch()
{
// Do not call DObjCalcAnim
Utils::Hook(0x6508C4, DObjCalcAnim, HOOK_CALL).install()->quick();
Utils::Hook(0x06508F6, DObjCalcAnim, HOOK_CALL).install()->quick();
// Filtering any mapents that is intended for Spec:Ops gamemode (CODO) and prevent them from spawning

View File

@ -7,6 +7,7 @@
#include <version.hpp>
#include "AssetInterfaces/ILocalizeEntry.hpp"
#include "Branding.hpp"
namespace Components
{
@ -21,7 +22,8 @@ namespace Components
iw4of::api ZoneBuilder::ExporterAPI(GetExporterAPIParams());
std::string ZoneBuilder::DumpingZone{};
ZoneBuilder::Zone::Zone(const std::string& name) : indexStart(0), externalSize(0),
ZoneBuilder::Zone::Zone(const std::string& name, const std::string& sourceName, const std::string& destination) :
indexStart(0), externalSize(0),
// Reserve 100MB by default.
// That's totally fine, as the dedi doesn't load images and therefore doesn't need much memory.
// That way we can be sure it won't need to reallocate memory.
@ -29,11 +31,17 @@ namespace Components
// Well, decompressed maps can get way larger than 100MB, so let's increase that.
buffer(0xC800000),
zoneName(name),
dataMap("zone_source/" + name + ".csv"),
destination(destination),
dataMap("zone_source/" + sourceName + ".csv"),
branding{ nullptr },
assetDepth(0),
iw4ofApi(getIW4OfApiParams())
{
}
ZoneBuilder::Zone::Zone(const std::string& name) : ZoneBuilder::Zone::Zone(name, name, std::format("zone/english/{}.ff", name))
{
}
ZoneBuilder::Zone::~Zone()
@ -148,7 +156,7 @@ namespace Components
{
Game::XZoneInfo info;
info.name = fastfile.data();
info.allocFlags = 0x20;
info.allocFlags = Game::DB_ZONE_LOAD;
info.freeFlags = 0;
Game::DB_LoadXAssets(&info, 1, true);
@ -449,11 +457,11 @@ namespace Components
zoneBuffer = Utils::Compression::ZLib::Compress(zoneBuffer);
outBuffer.append(zoneBuffer);
std::string outFile = "zone/" + this->zoneName + ".ff";
Utils::IO::WriteFile(outFile, outBuffer);
Logger::Print("done.\n");
Logger::Print("Zone '{}' written with {} assets and {} script strings\n", outFile, (this->aliasList.size() + this->loadedAssets.size()), this->scriptStrings.size());
Utils::IO::WriteFile(destination, outBuffer);
Logger::Print("done writing {}\n", destination);
Logger::Print("Zone '{}' written with {} assets and {} script strings\n", destination, (this->aliasList.size() + this->loadedAssets.size()), this->scriptStrings.size());
}
void ZoneBuilder::Zone::saveData()
@ -759,18 +767,23 @@ namespace Components
return header;
}
void ZoneBuilder::RefreshExporterWorkDirectory()
std::string ZoneBuilder::GetDumpingZonePath()
{
if (ZoneBuilder::DumpingZone.empty())
{
ExporterAPI.set_work_path(std::format("userraw/dump/stray"));
return std::format("userraw/dump/stray");
}
else
{
ExporterAPI.set_work_path(std::format("userraw/dump/{}", ZoneBuilder::DumpingZone));
return std::format("userraw/dump/{}", ZoneBuilder::DumpingZone);
}
}
void ZoneBuilder::RefreshExporterWorkDirectory()
{
ExporterAPI.set_work_path(GetDumpingZonePath());
}
iw4of::api* ZoneBuilder::GetExporter()
{
return &ExporterAPI;
@ -988,10 +1001,10 @@ namespace Components
Logger::Print(" --------------------------------------------------------------------------------\n");
Logger::Print(" IW4x ZoneBuilder - {}\n", REVISION_STR);
Logger::Print(" Commands:\n");
Logger::Print("\t-buildzone [zone]: builds a zone from a csv located in zone_source\n");
Logger::Print("\t-buildall: builds all zones in zone_source\n");
Logger::Print("\t-buildmod [mod name]: Build a mod.ff from the source located in zone_source/mod_name.csv\n");
Logger::Print("\t-buildzone [zone]: Builds a zone from a csv located in zone_source\n");
Logger::Print("\t-dumpzone [zone]: Loads and dump the specified zone in userraw/dump\n");
Logger::Print("\t-verifyzone [zone]: loads and verifies the specified zone\n");
Logger::Print("\t-dumpzone [zone]: loads and dump the specified zone\n");
Logger::Print("\t-listassets [assettype]: lists all loaded assets of the specified type\n");
Logger::Print("\t-quit: quits the program\n");
Logger::Print(" --------------------------------------------------------------------------------\n");
@ -1176,7 +1189,7 @@ namespace Components
Game::DB_LoadXAssets(&info, 1, true);
AssetHandler::FindOriginalAsset(Game::XAssetType::ASSET_TYPE_RAWFILE, "default"); // Lock until zone is unloaded
};
};
}
ZoneBuilder::ZoneBuilder()
@ -1288,43 +1301,139 @@ namespace Components
Utils::Hook::Nop(0x5BC791, 5);
AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader /* asset*/, const std::string& name, bool* /*restrict*/)
{
if (!ZoneBuilder::TraceZone.empty() && ZoneBuilder::TraceZone == FastFiles::Current())
{
ZoneBuilder::TraceAssets.emplace_back(std::make_pair(type, name));
}
});
if (!ZoneBuilder::TraceZone.empty() && ZoneBuilder::TraceZone == FastFiles::Current())
{
ZoneBuilder::TraceAssets.emplace_back(std::make_pair(type, name));
}
});
Command::Add("dumpzone", [](const Command::Params* params)
{
if (params->size() < 2) return;
std::string zone = params->get(1);
ZoneBuilder::DumpingZone = zone;
ZoneBuilder::RefreshExporterWorkDirectory();
std::vector<std::pair<Game::XAssetType, std::string>> assets{};
const auto unload = ZoneBuilder::LoadZoneWithTrace(zone, assets);
const auto unload = ZoneBuilder::LoadZoneWithTrace(zone, assets);
Logger::Print("Dumping zone '{}'...\n", zone);
for (const auto& asset : assets)
{
const auto type = asset.first;
const auto name = asset.second;
if (ExporterAPI.is_type_supported(type) && name[0] != ',')
Utils::IO::CreateDir(GetDumpingZonePath());
std::ofstream csv(std::filesystem::path(GetDumpingZonePath()) / std::format("{}.csv", zone));
csv
<< std::format("### Zone '{}' dumped with Zonebuilder {}", zone, Components::Branding::GetVersionString())
<< "\n\n";
constexpr Game::XAssetType typeOrder[] = {
Game::XAssetType::ASSET_TYPE_GAMEWORLD_MP,
Game::XAssetType::ASSET_TYPE_GAMEWORLD_SP,
Game::XAssetType::ASSET_TYPE_GFXWORLD,
Game::XAssetType::ASSET_TYPE_COMWORLD,
Game::XAssetType::ASSET_TYPE_FXWORLD,
Game::XAssetType::ASSET_TYPE_CLIPMAP_MP,
Game::XAssetType::ASSET_TYPE_CLIPMAP_SP,
Game::XAssetType::ASSET_TYPE_RAWFILE,
Game::XAssetType::ASSET_TYPE_VEHICLE,
Game::XAssetType::ASSET_TYPE_WEAPON,
Game::XAssetType::ASSET_TYPE_FX,
Game::XAssetType::ASSET_TYPE_TRACER,
Game::XAssetType::ASSET_TYPE_XMODEL,
Game::XAssetType::ASSET_TYPE_MATERIAL,
Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET,
Game::XAssetType::ASSET_TYPE_PIXELSHADER,
Game::XAssetType::ASSET_TYPE_VERTEXSHADER,
Game::XAssetType::ASSET_TYPE_VERTEXDECL,
Game::XAssetType::ASSET_TYPE_IMAGE,
Game::XAssetType::ASSET_TYPE_SOUND,
Game::XAssetType::ASSET_TYPE_LOADED_SOUND,
Game::XAssetType::ASSET_TYPE_SOUND_CURVE,
Game::XAssetType::ASSET_TYPE_PHYSPRESET,
};
std::unordered_map<Game::XAssetType, int> typePriority{};
for (auto i = 0; i < ARRAYSIZE(typeOrder); i++)
{
const auto assetHeader = Game::DB_FindXAssetHeader(type, name.data());
if (assetHeader.data)
const auto type = typeOrder[i];
typePriority.emplace(type, 1 + ARRAYSIZE(typeOrder) - i);
}
std::map<std::string, std::vector<std::string>> invalidAssets{};
std::sort(assets.begin(), assets.end(), [&](
const std::pair<Game::XAssetType, std::string>& a,
const std::pair<Game::XAssetType, std::string>& b
) {
if (a.first == b.first)
{
return a.second.compare(b.second) < 0;
}
else
{
const auto priorityA = typePriority[a.first];
const auto priorityB = typePriority[b.first];
if (priorityA == priorityB)
{
return a.second.compare(b.second) < 0;
}
else
{
return priorityB < priorityA;
}
}
});
// Used to format the CSV
Game::XAssetType lastTypeEncountered{};
for (const auto& asset : assets)
{
const auto type = asset.first;
const auto name = asset.second;
if (ExporterAPI.is_type_supported(type) && name[0] != ',')
{
ExporterAPI.write(type, assetHeader.data);
const auto assetHeader = Game::DB_FindXAssetHeader(type, name.data());
if (assetHeader.data)
{
ExporterAPI.write(type, assetHeader.data);
const auto typeName = Game::DB_GetXAssetTypeName(type) ;
if (type != lastTypeEncountered)
{
csv << "\n### " << typeName << "\n";
lastTypeEncountered = type;
}
csv << typeName << "," << name << "\n";
}
else
{
Logger::Warning(Game::conChannel_t::CON_CHANNEL_ERROR, "Asset {} has disappeared while dumping!\n", name);
invalidAssets["The following assets disappeared while dumping"].push_back(std::format("{},{}", Game::DB_GetXAssetTypeName(type), name));
}
}
else
{
Logger::Warning(Game::conChannel_t::CON_CHANNEL_ERROR, "Asset {} has disappeared while dumping!\n", name);
invalidAssets["The following assets are unsupported or not dumped as individual assets, but still present in the zone"].push_back(std::format("{},{}", Game::DB_GetXAssetTypeName(type), name));
}
}
for (const auto& kv : invalidAssets)
{
csv << "\n### " << kv.first << "\n";
for (const auto& line : kv.second)
{
csv << "#" << line << "\n";
}
}
csv << std::format("\n### {} assets", assets.size()) << "\n";
}
unload();
@ -1339,7 +1448,7 @@ namespace Components
std::string zone = params->get(1);
std::vector<std::pair<Game::XAssetType, std::string>> assets{};
const auto unload = ZoneBuilder::LoadZoneWithTrace(zone, assets);
const auto unload = ZoneBuilder::LoadZoneWithTrace(zone, assets);
int count = 0;
for (auto i = assets.begin(); i != assets.end(); ++i, ++count)
@ -1361,6 +1470,24 @@ namespace Components
Zone(zoneName).build();
});
Command::Add("buildmod", [](const Command::Params* params)
{
if (params->size() < 2) return;
std::string modName = params->get(1);
Logger::Print("Building zone for mod '{}'...\n", modName);
const std::string previousFsGame = Dvar::Var("fs_game").get<std::string>();
const std::string dir = "mods/" + modName;
Utils::IO::CreateDir(dir);
Dvar::Var("fs_game").set(dir);
Zone("mod", modName, dir + "/mod.ff").build();
Dvar::Var("fs_game").set(previousFsGame);
});
Command::Add("buildall", []()
{
auto path = std::format("{}\\zone_source", (*Game::fs_basepath)->current.string);
@ -1414,8 +1541,8 @@ namespace Components
#endif
}
}
}
});
}
});
Command::Add("listassets", [](const Command::Params* params)
{
@ -1431,7 +1558,7 @@ namespace Components
}, &type, false);
}
});
}
}
}
ZoneBuilder::~ZoneBuilder()

View File

@ -32,7 +32,8 @@ namespace Components
private:
Zone* builder;
};
Zone(const std::string& zoneName, const std::string& sourceName, const std::string& destination);
Zone(const std::string& zoneName);
~Zone();
@ -100,6 +101,7 @@ namespace Components
iw4of::api iw4ofApi;
std::string zoneName;
std::string destination;
Utils::CSV dataMap;
Utils::Memory::Allocator memAllocator;
@ -134,6 +136,7 @@ namespace Components
static std::vector<std::pair<Game::XAssetType, std::string>> EndAssetTrace();
static Game::XAssetHeader GetEmptyAssetIfCommon(Game::XAssetType type, const std::string& name, Zone* builder);
static std::string GetDumpingZonePath();
static void RefreshExporterWorkDirectory();
static iw4of::api* GetExporter();

View File

@ -960,7 +960,7 @@ namespace Game
union XAnimDynamicFrames
{
char(*_1)[3];
uint8_t(*_1)[3];
unsigned __int16(*_2)[3];
};