[IXModel] Don't load physpreset for ak74u

This commit is contained in:
momo5502 2017-01-18 14:55:02 +01:00
parent a24e68a83b
commit 4a4e938b00
11 changed files with 750 additions and 742 deletions

View File

@ -1,388 +1,388 @@
#include <STDInclude.hpp>
namespace Assets
{
void IFxEffectDef::load(Game::XAssetHeader* /*header*/, std::string name, Components::ZoneBuilder::Zone* /*builder*/)
{
#if 0
Components::FileSystem::File rawFx(fmt::sprintf("fx/%s.efx", name.data()));
if (rawFx.exists())
{
const char* session = rawFx.getBuffer().data();
Game::Com_BeginParseSession("fx");
Game::Com_SetSpaceDelimited(0);
Game::Com_SetParseNegativeNumbers(1);
const char* format = Game::Com_Parse(&session);
if (format != "iwfx"s)
{
Game::Com_EndParseSession();
Components::Logger::Error("Effect needs to be updated from the legacy format.\n");
}
int version = atoi(Game::Com_Parse(&session));
#include <STDInclude.hpp>
namespace Assets
{
void IFxEffectDef::load(Game::XAssetHeader* /*header*/, std::string name, Components::ZoneBuilder::Zone* /*builder*/)
{
#if 0
Components::FileSystem::File rawFx(fmt::sprintf("fx/%s.efx", name.data()));
if (rawFx.exists())
{
const char* session = rawFx.getBuffer().data();
Game::Com_BeginParseSession("fx");
Game::Com_SetSpaceDelimited(0);
Game::Com_SetParseNegativeNumbers(1);
const char* format = Game::Com_Parse(&session);
if (format != "iwfx"s)
{
Game::Com_EndParseSession();
Components::Logger::Error("Effect needs to be updated from the legacy format.\n");
}
int version = atoi(Game::Com_Parse(&session));
if (version > 2)
{
Game::Com_EndParseSession();
Components::Logger::Error("Version %i is too high. I can only handle up to %i.\n", version, 2);
}
Game::FxEditorEffectDef efx;
ZeroMemory(&efx, sizeof(efx));
// for (int i = 0; i < FX_ELEM_FIELD_COUNT; ++i)
// {
// Game::s_elemFields[i].handler(&session, efx.elems);
// }
Game::Com_EndParseSession();
}
#endif
}
void IFxEffectDef::markFxElemVisuals(Game::FxElemVisuals* visuals, char elemType, Components::ZoneBuilder::Zone* builder)
{
switch (elemType)
{
case 7:
{
if (visuals->xmodel)
{
builder->loadAsset(Game::XAssetType::ASSET_TYPE_XMODEL, visuals->xmodel);
}
break;
}
case 8:
case 9:
break;
case 0xA:
{
builder->loadAsset(Game::XAssetType::ASSET_TYPE_SOUND, std::string(visuals->soundName), false);
break;
}
case 0xC:
{
if (visuals->effectDef)
{
builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, visuals->effectDef, false);
}
break;
}
default:
{
if (visuals->material)
{
builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, visuals->material);
}
break;
}
}
}
void IFxEffectDef::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
{
Game::FxEffectDef* asset = header.fx;
for (int i = 0; i < (asset->elemDefCountEmission + asset->elemDefCountLooping + asset->elemDefCountOneShot); ++i)
{
Game::FxElemDef* elemDef = &asset->elemDefs[i];
{
if (elemDef->elemType == 11)
{
if (elemDef->visuals.markArray)
{
for (char j = 0; j < elemDef->visualCount; ++j)
{
if (elemDef->visuals.markArray[j].data[0])
{
builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, elemDef->visuals.markArray[j].data[0]);
}
if (elemDef->visuals.markArray[j].data[1])
{
builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, elemDef->visuals.markArray[j].data[1]);
}
}
}
}
else if (elemDef->visualCount > 1)
{
if (elemDef->visuals.array)
{
for (char j = 0; j < elemDef->visualCount; ++j)
{
this->markFxElemVisuals(&elemDef->visuals.array[j], elemDef->elemType, builder);
}
}
}
else
{
this->markFxElemVisuals(&elemDef->visuals.instance, elemDef->elemType, builder);
}
}
if (elemDef->effectOnImpact)
{
builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, elemDef->effectOnImpact, false);
}
if (elemDef->effectOnDeath)
{
builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, elemDef->effectOnDeath, false);
}
if (elemDef->effectEmitted)
{
builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, elemDef->effectEmitted, false);
}
}
}
void IFxEffectDef::saveFxElemVisuals(Game::FxElemVisuals* visuals, Game::FxElemVisuals* destVisuals, char elemType, Components::ZoneBuilder::Zone* builder)
{
Utils::Stream* buffer = builder->getBuffer();
switch (elemType)
{
case 7:
{
if (visuals->xmodel)
{
destVisuals->xmodel = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_XMODEL, visuals->xmodel).model;
}
break;
}
case 8:
case 9:
break;
case 0xA:
{
if (visuals->soundName)
{
buffer->saveString(visuals->soundName);
Utils::Stream::ClearPointer(&destVisuals->soundName);
}
break;
}
case 0xC:
{
if (visuals->effectDef)
{
buffer->saveString(visuals->effectDef->name);
Utils::Stream::ClearPointer(&destVisuals->effectDef);
}
break;
}
default:
{
if (visuals->material)
{
destVisuals->material = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, visuals->material).material;
}
break;
}
}
}
void IFxEffectDef::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
{
AssertSize(Game::FxEffectDef, 32);
Utils::Stream* buffer = builder->getBuffer();
Game::FxEffectDef* asset = header.fx;
Game::FxEffectDef* dest = buffer->dest<Game::FxEffectDef>();
buffer->save(asset);
buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL);
if (asset->name)
{
buffer->saveString(builder->getAssetName(this->getType(), asset->name));
Utils::Stream::ClearPointer(&dest->name);
}
if (asset->elemDefs)
{
AssertSize(Game::FxElemDef, 252);
buffer->align(Utils::Stream::ALIGN_4);
Game::FxElemDef* destElemDefs = buffer->dest<Game::FxElemDef>();
buffer->saveArray(asset->elemDefs, asset->elemDefCountEmission + asset->elemDefCountLooping + asset->elemDefCountOneShot);
for (int i = 0; i < (asset->elemDefCountEmission + asset->elemDefCountLooping + asset->elemDefCountOneShot); ++i)
{
Game::FxElemDef* destElemDef = &destElemDefs[i];
Game::FxElemDef* elemDef = &asset->elemDefs[i];
if (elemDef->velSamples)
{
AssertSize(Game::FxElemVelStateSample, 96);
buffer->align(Utils::Stream::ALIGN_4);
buffer->saveArray(elemDef->velSamples, elemDef->velIntervalCount + 1);
Utils::Stream::ClearPointer(&destElemDef->velSamples);
}
if (elemDef->visSamples)
{
AssertSize(Game::FxElemVisStateSample, 48);
buffer->align(Utils::Stream::ALIGN_4);
buffer->saveArray(elemDef->visSamples, elemDef->visStateIntervalCount + 1);
Utils::Stream::ClearPointer(&destElemDef->visSamples);
}
// Save_FxElemDefVisuals
{
if (elemDef->elemType == 11)
{
if (elemDef->visuals.markArray)
{
AssertSize(Game::FxElemMarkVisuals, 8);
buffer->align(Utils::Stream::ALIGN_4);
Game::FxElemMarkVisuals* destMarkArray = buffer->dest<Game::FxElemMarkVisuals>();
buffer->saveArray(elemDef->visuals.markArray, elemDef->visualCount);
for (char j = 0; j < elemDef->visualCount; ++j)
{
if (elemDef->visuals.markArray[j].data[0])
{
destMarkArray[j].data[0] = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, elemDef->visuals.markArray[j].data[0]).material;
}
if (elemDef->visuals.markArray[j].data[1])
{
destMarkArray[j].data[1] = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, elemDef->visuals.markArray[j].data[1]).material;
}
}
Utils::Stream::ClearPointer(&destElemDef->visuals.markArray);
}
}
else if(elemDef->visualCount > 1)
{
if (elemDef->visuals.array)
{
AssertSize(Game::FxElemVisuals, 4);
buffer->align(Utils::Stream::ALIGN_4);
Game::FxElemVisuals* destVisuals = buffer->dest<Game::FxElemVisuals>();
buffer->saveArray(elemDef->visuals.array, elemDef->visualCount);
for (char j = 0; j < elemDef->visualCount; ++j)
{
this->saveFxElemVisuals(&elemDef->visuals.array[j], &destVisuals[j], elemDef->elemType, builder);
}
Utils::Stream::ClearPointer(&destElemDef->visuals.array);
}
}
else
{
this->saveFxElemVisuals(&elemDef->visuals.instance, &destElemDef->visuals.instance, elemDef->elemType, builder);
}
}
if (elemDef->effectOnImpact)
{
buffer->saveString(elemDef->effectOnImpact->name);
Utils::Stream::ClearPointer(&destElemDef->effectOnImpact);
}
if (elemDef->effectOnDeath)
{
buffer->saveString(elemDef->effectOnDeath->name);
Utils::Stream::ClearPointer(&destElemDef->effectOnDeath);
}
if (elemDef->effectEmitted)
{
buffer->saveString(elemDef->effectEmitted->name);
Utils::Stream::ClearPointer(&destElemDef->effectEmitted);
}
// Save_FxElemExtendedDefPtr
{
AssertSize(Game::FxElemExtendedDef, 4);
if (elemDef->elemType == 3)
{
// Save_FxTrailDef
{
if (elemDef->extendedDef.trailDef)
{
AssertSize(Game::FxTrailDef, 36);
buffer->align(Utils::Stream::ALIGN_4);
Game::FxTrailDef* trailDef = elemDef->extendedDef.trailDef;
Game::FxTrailDef* destTrailDef = buffer->dest<Game::FxTrailDef>();
buffer->save(trailDef);
if (trailDef->verts)
{
AssertSize(Game::FxTrailVertex, 20);
buffer->align(Utils::Stream::ALIGN_4);
buffer->saveArray(trailDef->verts, trailDef->vertCount);
Utils::Stream::ClearPointer(&destTrailDef->verts);
}
if (trailDef->inds)
{
buffer->align(Utils::Stream::ALIGN_2);
buffer->saveArray(trailDef->inds, trailDef->indCount);
Utils::Stream::ClearPointer(&destTrailDef->inds);
}
Utils::Stream::ClearPointer(&destElemDef->extendedDef.trailDef);
}
}
}
else if (elemDef->elemType == 6)
{
if (elemDef->extendedDef.sparkFountain)
{
AssertSize(Game::FxSparkFountain, 52);
buffer->align(Utils::Stream::ALIGN_4);
buffer->save(elemDef->extendedDef.sparkFountain);
Utils::Stream::ClearPointer(&destElemDef->extendedDef.sparkFountain);
}
}
else
{
if (elemDef->extendedDef.unknownBytes)
{
buffer->save(elemDef->extendedDef.unknownBytes);
Utils::Stream::ClearPointer(&destElemDef->extendedDef.unknownBytes);
}
}
}
}
Utils::Stream::ClearPointer(&dest->elemDefs);
}
buffer->popBlock();
}
}
}
Game::FxEditorEffectDef efx;
ZeroMemory(&efx, sizeof(efx));
// for (int i = 0; i < FX_ELEM_FIELD_COUNT; ++i)
// {
// Game::s_elemFields[i].handler(&session, efx.elems);
// }
Game::Com_EndParseSession();
}
#endif
}
void IFxEffectDef::markFxElemVisuals(Game::FxElemVisuals* visuals, char elemType, Components::ZoneBuilder::Zone* builder)
{
switch (elemType)
{
case 7:
{
if (visuals->xmodel)
{
builder->loadAsset(Game::XAssetType::ASSET_TYPE_XMODEL, visuals->xmodel);
}
break;
}
case 8:
case 9:
break;
case 0xA:
{
builder->loadAsset(Game::XAssetType::ASSET_TYPE_SOUND, std::string(visuals->soundName), false);
break;
}
case 0xC:
{
if (visuals->effectDef)
{
builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, visuals->effectDef, false);
}
break;
}
default:
{
if (visuals->material)
{
builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, visuals->material);
}
break;
}
}
}
void IFxEffectDef::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
{
Game::FxEffectDef* asset = header.fx;
for (int i = 0; i < (asset->elemDefCountEmission + asset->elemDefCountLooping + asset->elemDefCountOneShot); ++i)
{
Game::FxElemDef* elemDef = &asset->elemDefs[i];
{
if (elemDef->elemType == 11)
{
if (elemDef->visuals.markArray)
{
for (char j = 0; j < elemDef->visualCount; ++j)
{
if (elemDef->visuals.markArray[j].data[0])
{
builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, elemDef->visuals.markArray[j].data[0]);
}
if (elemDef->visuals.markArray[j].data[1])
{
builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, elemDef->visuals.markArray[j].data[1]);
}
}
}
}
else if (elemDef->visualCount > 1)
{
if (elemDef->visuals.array)
{
for (char j = 0; j < elemDef->visualCount; ++j)
{
this->markFxElemVisuals(&elemDef->visuals.array[j], elemDef->elemType, builder);
}
}
}
else
{
this->markFxElemVisuals(&elemDef->visuals.instance, elemDef->elemType, builder);
}
}
if (elemDef->effectOnImpact)
{
builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, elemDef->effectOnImpact, false);
}
if (elemDef->effectOnDeath)
{
builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, elemDef->effectOnDeath, false);
}
if (elemDef->effectEmitted)
{
builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, elemDef->effectEmitted, false);
}
}
}
void IFxEffectDef::saveFxElemVisuals(Game::FxElemVisuals* visuals, Game::FxElemVisuals* destVisuals, char elemType, Components::ZoneBuilder::Zone* builder)
{
Utils::Stream* buffer = builder->getBuffer();
switch (elemType)
{
case 7:
{
if (visuals->xmodel)
{
destVisuals->xmodel = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_XMODEL, visuals->xmodel).model;
}
break;
}
case 8:
case 9:
break;
case 0xA:
{
if (visuals->soundName)
{
buffer->saveString(visuals->soundName);
Utils::Stream::ClearPointer(&destVisuals->soundName);
}
break;
}
case 0xC:
{
if (visuals->effectDef)
{
buffer->saveString(visuals->effectDef->name);
Utils::Stream::ClearPointer(&destVisuals->effectDef);
}
break;
}
default:
{
if (visuals->material)
{
destVisuals->material = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, visuals->material).material;
}
break;
}
}
}
void IFxEffectDef::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
{
AssertSize(Game::FxEffectDef, 32);
Utils::Stream* buffer = builder->getBuffer();
Game::FxEffectDef* asset = header.fx;
Game::FxEffectDef* dest = buffer->dest<Game::FxEffectDef>();
buffer->save(asset);
buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL);
if (asset->name)
{
buffer->saveString(builder->getAssetName(this->getType(), asset->name));
Utils::Stream::ClearPointer(&dest->name);
}
if (asset->elemDefs)
{
AssertSize(Game::FxElemDef, 252);
buffer->align(Utils::Stream::ALIGN_4);
Game::FxElemDef* destElemDefs = buffer->dest<Game::FxElemDef>();
buffer->saveArray(asset->elemDefs, asset->elemDefCountEmission + asset->elemDefCountLooping + asset->elemDefCountOneShot);
for (int i = 0; i < (asset->elemDefCountEmission + asset->elemDefCountLooping + asset->elemDefCountOneShot); ++i)
{
Game::FxElemDef* destElemDef = &destElemDefs[i];
Game::FxElemDef* elemDef = &asset->elemDefs[i];
if (elemDef->velSamples)
{
AssertSize(Game::FxElemVelStateSample, 96);
buffer->align(Utils::Stream::ALIGN_4);
buffer->saveArray(elemDef->velSamples, elemDef->velIntervalCount + 1);
Utils::Stream::ClearPointer(&destElemDef->velSamples);
}
if (elemDef->visSamples)
{
AssertSize(Game::FxElemVisStateSample, 48);
buffer->align(Utils::Stream::ALIGN_4);
buffer->saveArray(elemDef->visSamples, elemDef->visStateIntervalCount + 1);
Utils::Stream::ClearPointer(&destElemDef->visSamples);
}
// Save_FxElemDefVisuals
{
if (elemDef->elemType == 11)
{
if (elemDef->visuals.markArray)
{
AssertSize(Game::FxElemMarkVisuals, 8);
buffer->align(Utils::Stream::ALIGN_4);
Game::FxElemMarkVisuals* destMarkArray = buffer->dest<Game::FxElemMarkVisuals>();
buffer->saveArray(elemDef->visuals.markArray, elemDef->visualCount);
for (char j = 0; j < elemDef->visualCount; ++j)
{
if (elemDef->visuals.markArray[j].data[0])
{
destMarkArray[j].data[0] = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, elemDef->visuals.markArray[j].data[0]).material;
}
if (elemDef->visuals.markArray[j].data[1])
{
destMarkArray[j].data[1] = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, elemDef->visuals.markArray[j].data[1]).material;
}
}
Utils::Stream::ClearPointer(&destElemDef->visuals.markArray);
}
}
else if(elemDef->visualCount > 1)
{
if (elemDef->visuals.array)
{
AssertSize(Game::FxElemVisuals, 4);
buffer->align(Utils::Stream::ALIGN_4);
Game::FxElemVisuals* destVisuals = buffer->dest<Game::FxElemVisuals>();
buffer->saveArray(elemDef->visuals.array, elemDef->visualCount);
for (char j = 0; j < elemDef->visualCount; ++j)
{
this->saveFxElemVisuals(&elemDef->visuals.array[j], &destVisuals[j], elemDef->elemType, builder);
}
Utils::Stream::ClearPointer(&destElemDef->visuals.array);
}
}
else
{
this->saveFxElemVisuals(&elemDef->visuals.instance, &destElemDef->visuals.instance, elemDef->elemType, builder);
}
}
if (elemDef->effectOnImpact)
{
buffer->saveString(elemDef->effectOnImpact->name);
Utils::Stream::ClearPointer(&destElemDef->effectOnImpact);
}
if (elemDef->effectOnDeath)
{
buffer->saveString(elemDef->effectOnDeath->name);
Utils::Stream::ClearPointer(&destElemDef->effectOnDeath);
}
if (elemDef->effectEmitted)
{
buffer->saveString(elemDef->effectEmitted->name);
Utils::Stream::ClearPointer(&destElemDef->effectEmitted);
}
// Save_FxElemExtendedDefPtr
{
AssertSize(Game::FxElemExtendedDef, 4);
if (elemDef->elemType == 3)
{
// Save_FxTrailDef
{
if (elemDef->extendedDef.trailDef)
{
AssertSize(Game::FxTrailDef, 36);
buffer->align(Utils::Stream::ALIGN_4);
Game::FxTrailDef* trailDef = elemDef->extendedDef.trailDef;
Game::FxTrailDef* destTrailDef = buffer->dest<Game::FxTrailDef>();
buffer->save(trailDef);
if (trailDef->verts)
{
AssertSize(Game::FxTrailVertex, 20);
buffer->align(Utils::Stream::ALIGN_4);
buffer->saveArray(trailDef->verts, trailDef->vertCount);
Utils::Stream::ClearPointer(&destTrailDef->verts);
}
if (trailDef->inds)
{
buffer->align(Utils::Stream::ALIGN_2);
buffer->saveArray(trailDef->inds, trailDef->indCount);
Utils::Stream::ClearPointer(&destTrailDef->inds);
}
Utils::Stream::ClearPointer(&destElemDef->extendedDef.trailDef);
}
}
}
else if (elemDef->elemType == 6)
{
if (elemDef->extendedDef.sparkFountain)
{
AssertSize(Game::FxSparkFountain, 52);
buffer->align(Utils::Stream::ALIGN_4);
buffer->save(elemDef->extendedDef.sparkFountain);
Utils::Stream::ClearPointer(&destElemDef->extendedDef.sparkFountain);
}
}
else
{
if (elemDef->extendedDef.unknownBytes)
{
buffer->save(elemDef->extendedDef.unknownBytes);
Utils::Stream::ClearPointer(&destElemDef->extendedDef.unknownBytes);
}
}
}
}
Utils::Stream::ClearPointer(&dest->elemDefs);
}
buffer->popBlock();
}
}

View File

@ -27,7 +27,7 @@ namespace Assets
{
AssertSize(Game::G_GlassData, 128);
Game::G_GlassData* destGlass = buffer->dest<Game::G_GlassData>();
Game::G_GlassData* destGlass = buffer->dest<Game::G_GlassData>();
buffer->save(asset->data);
if (asset->data->glassPieces)

View File

@ -54,10 +54,10 @@ namespace Assets
buffer->align(Utils::Stream::ALIGN_4);
builder->storePointer(*childNodeTreePtr);
Game::pathnode_tree_t* destChildNodeTree = buffer->dest<Game::pathnode_tree_t>();
buffer->save(*childNodeTreePtr);
this->savepathnode_tree_info_t(*childNodeTreePtr, destChildNodeTree, builder);
Game::pathnode_tree_t* destChildNodeTree = buffer->dest<Game::pathnode_tree_t>();
buffer->save(*childNodeTreePtr);
this->savepathnode_tree_info_t(*childNodeTreePtr, destChildNodeTree, builder);
Utils::Stream::ClearPointer(destChildNodeTreePtr);
}
}
@ -88,12 +88,12 @@ namespace Assets
{
buffer->align(Utils::Stream::ALIGN_4);
builder->storePointer(*trackSegmentPtr);
Game::VehicleTrackSegment* destTrackSegment = buffer->dest<Game::VehicleTrackSegment>();
buffer->save(*trackSegmentPtr);
this->saveVehicleTrackSegment(*trackSegmentPtr, destTrackSegment, builder);
buffer->save(*trackSegmentPtr);
this->saveVehicleTrackSegment(*trackSegmentPtr, destTrackSegment, builder);
Utils::Stream::ClearPointer(destTrackSegmentPtr);
}
}
@ -108,15 +108,15 @@ namespace Assets
{
buffer->saveString(trackSegment->name);
Utils::Stream::ClearPointer(&destTrackSegment->name);
}
if (trackSegment->trackSectors)
{
}
if (trackSegment->trackSectors)
{
AssertSize(Game::VehicleTrackSector, 60);
buffer->align(Utils::Stream::ALIGN_4);
Game::VehicleTrackSector* destTrackSectors = buffer->dest<Game::VehicleTrackSector>();
buffer->saveArray(trackSegment->trackSectors, trackSegment->trackSectorCount);
buffer->saveArray(trackSegment->trackSectors, trackSegment->trackSectorCount);
for (int i = 0; i < trackSegment->trackSectorCount; ++i)
{
@ -130,21 +130,21 @@ namespace Assets
buffer->saveArray(trackSector->trackObstacles, trackSector->trackObstacleCount);
Utils::Stream::ClearPointer(&destTrackSector->trackObstacles);
}
}
}
if (trackSegment->trackSegments1)
{
buffer->align(Utils::Stream::ALIGN_4);
this->saveVehicleTrackSegment_ptrArray(trackSegment->trackSegments1, trackSegment->trackSegmentCount1, builder);
Utils::Stream::ClearPointer(&destTrackSegment->trackSegments1);
}
}
if (trackSegment->trackSegments2)
{
buffer->align(Utils::Stream::ALIGN_4);
this->saveVehicleTrackSegment_ptrArray(trackSegment->trackSegments2, trackSegment->trackSegmentCount2, builder);
Utils::Stream::ClearPointer(&destTrackSegment->trackSegments2);
if (trackSegment->trackSegments1)
{
buffer->align(Utils::Stream::ALIGN_4);
this->saveVehicleTrackSegment_ptrArray(trackSegment->trackSegments1, trackSegment->trackSegmentCount1, builder);
Utils::Stream::ClearPointer(&destTrackSegment->trackSegments1);
}
if (trackSegment->trackSegments2)
{
buffer->align(Utils::Stream::ALIGN_4);
this->saveVehicleTrackSegment_ptrArray(trackSegment->trackSegments2, trackSegment->trackSegmentCount2, builder);
Utils::Stream::ClearPointer(&destTrackSegment->trackSegments2);
}
}
@ -273,18 +273,18 @@ namespace Assets
for (int i = 0; i < asset->vehicleTrack.trackSegmentCount; ++i)
{
builder->storePointer(&asset->vehicleTrack.trackSegments[i]);
buffer->save(&asset->vehicleTrack.trackSegments[i]);
}
builder->storePointer(&asset->vehicleTrack.trackSegments[i]);
buffer->save(&asset->vehicleTrack.trackSegments[i]);
}
for (int i = 0; i < asset->vehicleTrack.trackSegmentCount; ++i)
{
Game::VehicleTrackSegment* destTrackSegment = &destTrackSegments[i];
Game::VehicleTrackSegment* trackSegment = &asset->vehicleTrack.trackSegments[i];
Game::VehicleTrackSegment* destTrackSegment = &destTrackSegments[i];
Game::VehicleTrackSegment* trackSegment = &asset->vehicleTrack.trackSegments[i];
this->saveVehicleTrackSegment(trackSegment, destTrackSegment, builder);
}
this->saveVehicleTrackSegment(trackSegment, destTrackSegment, builder);
}
Utils::Stream::ClearPointer(&dest->vehicleTrack.trackSegments);
}
}
@ -297,7 +297,7 @@ namespace Assets
AssertSize(Game::G_GlassData, 128);
buffer->align(Utils::Stream::ALIGN_4);
Game::G_GlassData* destGlass = buffer->dest<Game::G_GlassData>();
Game::G_GlassData* destGlass = buffer->dest<Game::G_GlassData>();
buffer->save(asset->data);
if (asset->data->glassPieces)

View File

@ -4,71 +4,71 @@
namespace Assets
{
void IXModel::loadXSurfaceCollisionTree(Game::XSurfaceCollisionTree* entry, Utils::Stream::Reader* reader)
{
if (entry->nodes)
{
entry->nodes = reader->readArray<Game::XSurfaceCollisionNode>(entry->nodeCount);
}
if (entry->leafs)
{
entry->leafs = reader->readArray<Game::XSurfaceCollisionLeaf>(entry->leafCount);
}
void IXModel::loadXSurfaceCollisionTree(Game::XSurfaceCollisionTree* entry, Utils::Stream::Reader* reader)
{
if (entry->nodes)
{
entry->nodes = reader->readArray<Game::XSurfaceCollisionNode>(entry->nodeCount);
}
if (entry->leafs)
{
entry->leafs = reader->readArray<Game::XSurfaceCollisionLeaf>(entry->leafCount);
}
}
void IXModel::loadXSurface(Game::XSurface* surf, Utils::Stream::Reader* reader)
{
if (surf->vertInfo.vertsBlend)
{
surf->vertInfo.vertsBlend = reader->readArray<unsigned short>(surf->vertInfo.vertCount[0] + (surf->vertInfo.vertCount[1] * 3) + (surf->vertInfo.vertCount[2] * 5) + (surf->vertInfo.vertCount[3] * 7));
}
// Access vertex block
if (surf->verts0)
{
surf->verts0 = reader->readArray<Game::GfxPackedVertex>(surf->vertCount);
}
// Save_XRigidVertListArray
if (surf->vertList)
{
surf->vertList = reader->readArray<Game::XRigidVertList>(surf->vertListCount);
for (unsigned int i = 0; i < surf->vertListCount; ++i)
{
Game::XRigidVertList* rigidVertList = &surf->vertList[i];
if (rigidVertList->collisionTree)
{
rigidVertList->collisionTree = reader->readObject<Game::XSurfaceCollisionTree>();
this->loadXSurfaceCollisionTree(rigidVertList->collisionTree, reader);
}
}
}
// Access index block
if (surf->triIndices)
{
surf->triIndices = reader->readArray<unsigned short>(surf->triCount * 3);
if (surf->vertInfo.vertsBlend)
{
surf->vertInfo.vertsBlend = reader->readArray<unsigned short>(surf->vertInfo.vertCount[0] + (surf->vertInfo.vertCount[1] * 3) + (surf->vertInfo.vertCount[2] * 5) + (surf->vertInfo.vertCount[3] * 7));
}
// Access vertex block
if (surf->verts0)
{
surf->verts0 = reader->readArray<Game::GfxPackedVertex>(surf->vertCount);
}
// Save_XRigidVertListArray
if (surf->vertList)
{
surf->vertList = reader->readArray<Game::XRigidVertList>(surf->vertListCount);
for (unsigned int i = 0; i < surf->vertListCount; ++i)
{
Game::XRigidVertList* rigidVertList = &surf->vertList[i];
if (rigidVertList->collisionTree)
{
rigidVertList->collisionTree = reader->readObject<Game::XSurfaceCollisionTree>();
this->loadXSurfaceCollisionTree(rigidVertList->collisionTree, reader);
}
}
}
// Access index block
if (surf->triIndices)
{
surf->triIndices = reader->readArray<unsigned short>(surf->triCount * 3);
}
}
void IXModel::loadXModelSurfs(Game::XModelSurfs* asset, Utils::Stream::Reader* reader)
{
if (asset->name)
{
asset->name = reader->readCString();
}
if (asset->surfaces)
{
asset->surfaces = reader->readArray<Game::XSurface>(asset->numSurfaces);
for (int i = 0; i < asset->numSurfaces; ++i)
{
this->loadXSurface(&asset->surfaces[i], reader);
}
if (asset->name)
{
asset->name = reader->readCString();
}
if (asset->surfaces)
{
asset->surfaces = reader->readArray<Game::XSurface>(asset->numSurfaces);
for (int i = 0; i < asset->numSurfaces; ++i)
{
this->loadXSurface(&asset->surfaces[i], reader);
}
}
}
@ -192,17 +192,25 @@ namespace Assets
{
asset->physPreset = reader.readObject<Game::PhysPreset>();
if (asset->physPreset->name)
{
asset->physPreset->name = reader.readCString();
}
if (asset->physPreset->sndAliasPrefix)
{
asset->physPreset->sndAliasPrefix = reader.readCString();
if (asset->physPreset->name)
{
asset->physPreset->name = reader.readCString();
}
Components::AssetHandler::StoreTemporaryAsset(Game::XAssetType::ASSET_TYPE_PHYSPRESET, { asset->physPreset });
if (asset->physPreset->sndAliasPrefix)
{
asset->physPreset->sndAliasPrefix = reader.readCString();
}
// This is an experiment, ak74 fails though
if (asset->name == "weapon_ak74u"s)
{
asset->physPreset = nullptr;
}
else
{
Components::AssetHandler::StoreTemporaryAsset(Game::XAssetType::ASSET_TYPE_PHYSPRESET, { asset->physPreset });
}
}
if (asset->physCollmap)

View File

@ -601,24 +601,24 @@ namespace Assets
clipMap->name = reader.readCString();
clipMap->numCPlanes = reader.read<int>();
clipMap->numStaticModels = reader.read<int>();
clipMap->numMaterials = reader.read<int>();
clipMap->numCBrushSides = reader.read<int>();
clipMap->numCBrushEdges = reader.read<int>();
clipMap->numCNodes = reader.read<int>();
clipMap->numCLeaf = reader.read<int>();
clipMap->numCLeafBrushNodes = reader.read<int>();
clipMap->numLeafBrushes = reader.read<int>();
clipMap->numLeafSurfaces = reader.read<int>();
clipMap->numVerts = reader.read<int>();
clipMap->numTriIndices = reader.read<int>();
clipMap->numCollisionBorders = reader.read<int>();
clipMap->numCollisionPartitions = reader.read<int>();
clipMap->numCollisionAABBTrees = reader.read<int>();
clipMap->numCModels = reader.read<int>();
clipMap->numCBrushes = reader.read<short>();
clipMap->dynEntCount[0] = reader.read<unsigned __int16>();
clipMap->numCPlanes = reader.read<int>();
clipMap->numStaticModels = reader.read<int>();
clipMap->numMaterials = reader.read<int>();
clipMap->numCBrushSides = reader.read<int>();
clipMap->numCBrushEdges = reader.read<int>();
clipMap->numCNodes = reader.read<int>();
clipMap->numCLeaf = reader.read<int>();
clipMap->numCLeafBrushNodes = reader.read<int>();
clipMap->numLeafBrushes = reader.read<int>();
clipMap->numLeafSurfaces = reader.read<int>();
clipMap->numVerts = reader.read<int>();
clipMap->numTriIndices = reader.read<int>();
clipMap->numCollisionBorders = reader.read<int>();
clipMap->numCollisionPartitions = reader.read<int>();
clipMap->numCollisionAABBTrees = reader.read<int>();
clipMap->numCModels = reader.read<int>();
clipMap->numCBrushes = reader.read<short>();
clipMap->dynEntCount[0] = reader.read<unsigned __int16>();
clipMap->dynEntCount[1] = reader.read<unsigned __int16>();
if (clipMap->numCPlanes)

View File

@ -2,12 +2,12 @@
namespace Assets
{
void Isnd_alias_list_t::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
{
Game::snd_alias_list_t* asset = header.sound;
void Isnd_alias_list_t::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
{
Game::snd_alias_list_t* asset = header.sound;
for (int i = 0; i < asset->count; ++i)
{
{
Game::snd_alias_t* alias = &asset->head[i];
if (alias->soundFile && alias->soundFile->type == Game::snd_alias_type_t::SAT_LOADED)
@ -52,12 +52,12 @@ namespace Assets
buffer->align(Utils::Stream::ALIGN_4);
builder->storePointer(asset->head);
Game::snd_alias_t* destHead = buffer->dest<Game::snd_alias_t>();
buffer->saveArray(asset->head, asset->count);
Game::snd_alias_t* destHead = buffer->dest<Game::snd_alias_t>();
buffer->saveArray(asset->head, asset->count);
for (int i = 0; i < asset->count; ++i)
{
Game::snd_alias_t* destAlias = &destHead[i];
Game::snd_alias_t* destAlias = &destHead[i];
Game::snd_alias_t* alias = &asset->head[i];
if (alias->name)

View File

@ -37,7 +37,7 @@ namespace Components
if (Dvar::Var("com_logFilter").get<bool>())
{
Utils::Hook::Nop(0x647466, 5); // 'dvar set' lines
Utils::Hook::Nop(0x647466, 5); // 'dvar set' lines
Utils::Hook::Nop(0x5DF4F2, 5); // 'sending splash open' lines
}

View File

@ -413,18 +413,18 @@ namespace Components
Utils::Hook::Call<void(Game::XZoneInfo*, unsigned int)>(0x5BBAC0)(zoneInfo, zoneCount);
}
#ifdef DEBUG
void FastFiles::LogStreamRead(int len)
{
*Game::g_streamPos += len;
if (FastFiles::StreamRead)
{
std::string data = Utils::String::VA("%d\n", len);
if (*Game::g_streamPosIndex == 2) data = Utils::String::VA("(%d)\n", len);
Utils::IO::WriteFile("userraw/logs/iw4_reads.log", data, true);
}
}
#ifdef DEBUG
void FastFiles::LogStreamRead(int len)
{
*Game::g_streamPos += len;
if (FastFiles::StreamRead)
{
std::string data = Utils::String::VA("%d\n", len);
if (*Game::g_streamPosIndex == 2) data = Utils::String::VA("(%d)\n", len);
Utils::IO::WriteFile("userraw/logs/iw4_reads.log", data, true);
}
}
#endif
FastFiles::FastFiles()
@ -518,15 +518,15 @@ namespace Components
Game::DB_LoadXAssets(&info, 1, true);
});
#ifdef DEBUG
// ZoneBuilder debugging
Utils::IO::WriteFile("userraw/logs/iw4_reads.log", "", false);
#ifdef DEBUG
// ZoneBuilder debugging
Utils::IO::WriteFile("userraw/logs/iw4_reads.log", "", false);
Utils::Hook(0x4A8FA0, FastFiles::LogStreamRead, HOOK_JUMP).install()->quick();
Utils::Hook(0x4BCB62, []()
{
FastFiles::StreamRead = true;
Utils::Hook::Call<void(bool)>(0x4B8DB0)(true); // currently set to Load_GfxWorld
FastFiles::StreamRead = false;
Utils::Hook(0x4BCB62, []()
{
FastFiles::StreamRead = true;
Utils::Hook::Call<void(bool)>(0x4B8DB0)(true); // currently set to Load_GfxWorld
FastFiles::StreamRead = false;
}, HOOK_CALL).install()->quick();
#endif
}

View File

@ -59,16 +59,16 @@ namespace Utils
}
}
void* Stream::Reader::readPointer()
{
void* pointer = this->read<void*>();
if (!this->hasPointer(pointer))
{
this->pointerMap[pointer] = 0;
}
return pointer;
}
void* Stream::Reader::readPointer()
{
void* pointer = this->read<void*>();
if (!this->hasPointer(pointer))
{
this->pointerMap[pointer] = 0;
}
return pointer;
}
void Stream::Reader::mapPointer(void* oldPointer, void* newPointer)
{
if (this->hasPointer(oldPointer))
@ -77,9 +77,9 @@ namespace Utils
}
}
bool Stream::Reader::hasPointer(void* pointer)
{
return this->pointerMap.find(pointer) != this->pointerMap.end();
bool Stream::Reader::hasPointer(void* pointer)
{
return this->pointerMap.find(pointer) != this->pointerMap.end();
}
Stream::Stream() : criticalSectionState(0)

View File

@ -6,186 +6,186 @@
#else
#define SaveLogEnter(x) builder->getBuffer()->enterStruct(x)
#define SaveLogExit() builder->getBuffer()->leaveStruct()
#endif
namespace Utils
{
class Stream
{
private:
int criticalSectionState;
unsigned int blockSize[Game::MAX_XFILE_COUNT];
std::vector<Game::XFILE_BLOCK_TYPES> streamStack;
std::string buffer;
public:
class Reader
{
public:
Reader(Utils::Memory::Allocator* _allocator, std::string _buffer) : buffer(_buffer), allocator(_allocator), position(0) {}
std::string readString();
const char* readCString();
char readByte();
void* read(size_t size, size_t count = 1);
template <typename T> inline T* readObject()
{
return readArray<T>(1);
}
template <typename T> inline T* readArray(size_t count = 1)
{
return reinterpret_cast<T*>(this->read(sizeof(T), count));
}
template <typename T> T read()
{
T obj;
for (unsigned int i = 0; i < sizeof(T); ++i)
{
reinterpret_cast<char*>(&obj)[i] = this->readByte();
}
return obj;
}
bool end();
void seek(unsigned int position);
void* readPointer();
void mapPointer(void* oldPointer, void* newPointer);
bool hasPointer(void* pointer);
private:
unsigned int position;
std::string buffer;
std::map<void*, void*> pointerMap;
Utils::Memory::Allocator* allocator;
};
enum Alignment
{
ALIGN_2,
ALIGN_4,
ALIGN_8,
ALIGN_16,
ALIGN_32,
ALIGN_64,
ALIGN_128,
ALIGN_256,
ALIGN_512,
ALIGN_1024,
ALIGN_2048,
};
Stream();
Stream(size_t size);
~Stream();
size_t length();
size_t capacity();
char* save(const void * _str, size_t size, size_t count = 1);
char* save(Game::XFILE_BLOCK_TYPES stream, const void * _str, size_t size, size_t count);
char* save(Game::XFILE_BLOCK_TYPES stream, int value, size_t count);
template <typename T> inline char* save(T* object)
{
return saveArray<T>(object, 1);
}
template <typename T> inline char* saveArray(T* array, size_t count)
{
return save(array, sizeof(T), count);
}
char* saveString(std::string string);
char* saveString(const char* string);
char* saveString(const char* string, size_t len);
char* saveByte(unsigned char byte, size_t count = 1);
char* saveNull(size_t count = 1);
char* saveMax(size_t count = 1);
char* saveText(std::string string);
void align(Alignment align);
bool pushBlock(Game::XFILE_BLOCK_TYPES stream);
bool popBlock();
bool isValidBlock(Game::XFILE_BLOCK_TYPES stream);
void increaseBlockSize(Game::XFILE_BLOCK_TYPES stream, unsigned int size);
void increaseBlockSize(unsigned int size);
Game::XFILE_BLOCK_TYPES getCurrentBlock();
unsigned int getBlockSize(Game::XFILE_BLOCK_TYPES stream);
DWORD getPackedOffset();
char* data();
char* at();
template <typename T> T* dest()
{
return reinterpret_cast<T*>(this->at());
}
template <typename T> static inline void ClearPointer(T** object)
{
*object = reinterpret_cast<T*>(-1);
}
void toBuffer(std::string& outBuffer);
std::string toBuffer();
// Enter/Leave critical sections in which reallocations are not allowed.
// If buffer reallocation is detected, the operation has to be terminated
// and more memory has to be allocated next time. This will have to be done
// by editing the code though.
void enterCriticalSection();
void leaveCriticalSection();
bool isCriticalSection();
// for recording zb writes
#ifdef WRITE_LOGS
int structLevel;
void enterStruct(const char* structName);
void leaveStruct();
#endif
// This represents packed offset in streams:
// - lowest 28 bits store the value/offset
// - highest 4 bits store the stream block
class Offset
{
public:
union
{
struct
{
uint32_t offset : 28;
Game::XFILE_BLOCK_TYPES block : 4;
};
uint32_t packed;
void* pointer;
};
Offset() : packed(0) {};
Offset(Game::XFILE_BLOCK_TYPES _block, uint32_t _offset) : offset(_offset), block(_block) {};
// The game needs it to be incremented
uint32_t getPackedOffset()
{
return this->packed + 1;
};
uint32_t getUnpackedOffset()
{
Offset lOffset = *this;
lOffset.packed--;
return lOffset.offset;
};
int getUnpackedBlock()
{
Offset lOffset = *this;
lOffset.packed--;
return lOffset.block;
};
};
};
}
#endif
namespace Utils
{
class Stream
{
private:
int criticalSectionState;
unsigned int blockSize[Game::MAX_XFILE_COUNT];
std::vector<Game::XFILE_BLOCK_TYPES> streamStack;
std::string buffer;
public:
class Reader
{
public:
Reader(Utils::Memory::Allocator* _allocator, std::string _buffer) : buffer(_buffer), allocator(_allocator), position(0) {}
std::string readString();
const char* readCString();
char readByte();
void* read(size_t size, size_t count = 1);
template <typename T> inline T* readObject()
{
return readArray<T>(1);
}
template <typename T> inline T* readArray(size_t count = 1)
{
return reinterpret_cast<T*>(this->read(sizeof(T), count));
}
template <typename T> T read()
{
T obj;
for (unsigned int i = 0; i < sizeof(T); ++i)
{
reinterpret_cast<char*>(&obj)[i] = this->readByte();
}
return obj;
}
bool end();
void seek(unsigned int position);
void* readPointer();
void mapPointer(void* oldPointer, void* newPointer);
bool hasPointer(void* pointer);
private:
unsigned int position;
std::string buffer;
std::map<void*, void*> pointerMap;
Utils::Memory::Allocator* allocator;
};
enum Alignment
{
ALIGN_2,
ALIGN_4,
ALIGN_8,
ALIGN_16,
ALIGN_32,
ALIGN_64,
ALIGN_128,
ALIGN_256,
ALIGN_512,
ALIGN_1024,
ALIGN_2048,
};
Stream();
Stream(size_t size);
~Stream();
size_t length();
size_t capacity();
char* save(const void * _str, size_t size, size_t count = 1);
char* save(Game::XFILE_BLOCK_TYPES stream, const void * _str, size_t size, size_t count);
char* save(Game::XFILE_BLOCK_TYPES stream, int value, size_t count);
template <typename T> inline char* save(T* object)
{
return saveArray<T>(object, 1);
}
template <typename T> inline char* saveArray(T* array, size_t count)
{
return save(array, sizeof(T), count);
}
char* saveString(std::string string);
char* saveString(const char* string);
char* saveString(const char* string, size_t len);
char* saveByte(unsigned char byte, size_t count = 1);
char* saveNull(size_t count = 1);
char* saveMax(size_t count = 1);
char* saveText(std::string string);
void align(Alignment align);
bool pushBlock(Game::XFILE_BLOCK_TYPES stream);
bool popBlock();
bool isValidBlock(Game::XFILE_BLOCK_TYPES stream);
void increaseBlockSize(Game::XFILE_BLOCK_TYPES stream, unsigned int size);
void increaseBlockSize(unsigned int size);
Game::XFILE_BLOCK_TYPES getCurrentBlock();
unsigned int getBlockSize(Game::XFILE_BLOCK_TYPES stream);
DWORD getPackedOffset();
char* data();
char* at();
template <typename T> T* dest()
{
return reinterpret_cast<T*>(this->at());
}
template <typename T> static inline void ClearPointer(T** object)
{
*object = reinterpret_cast<T*>(-1);
}
void toBuffer(std::string& outBuffer);
std::string toBuffer();
// Enter/Leave critical sections in which reallocations are not allowed.
// If buffer reallocation is detected, the operation has to be terminated
// and more memory has to be allocated next time. This will have to be done
// by editing the code though.
void enterCriticalSection();
void leaveCriticalSection();
bool isCriticalSection();
// for recording zb writes
#ifdef WRITE_LOGS
int structLevel;
void enterStruct(const char* structName);
void leaveStruct();
#endif
// This represents packed offset in streams:
// - lowest 28 bits store the value/offset
// - highest 4 bits store the stream block
class Offset
{
public:
union
{
struct
{
uint32_t offset : 28;
Game::XFILE_BLOCK_TYPES block : 4;
};
uint32_t packed;
void* pointer;
};
Offset() : packed(0) {};
Offset(Game::XFILE_BLOCK_TYPES _block, uint32_t _offset) : offset(_offset), block(_block) {};
// The game needs it to be incremented
uint32_t getPackedOffset()
{
return this->packed + 1;
};
uint32_t getUnpackedOffset()
{
Offset lOffset = *this;
lOffset.packed--;
return lOffset.offset;
};
int getUnpackedBlock()
{
Offset lOffset = *this;
lOffset.packed--;
return lOffset.block;
};
};
};
}

View File

@ -3,25 +3,25 @@
#include "base128.h"
#endif
#define VA_BUFFER_COUNT 32
#define VA_BUFFER_COUNT 32
#define VA_BUFFER_SIZE 65536
namespace Utils
{
namespace String
{
const char *VA(const char *fmt, ...)
{
static char g_vaBuffer[VA_BUFFER_COUNT][VA_BUFFER_SIZE];
static int g_vaNextBufferIndex = 0;
va_list ap;
va_start(ap, fmt);
char* dest = g_vaBuffer[g_vaNextBufferIndex];
vsnprintf_s(g_vaBuffer[g_vaNextBufferIndex], VA_BUFFER_SIZE, fmt, ap);
g_vaNextBufferIndex = (g_vaNextBufferIndex + 1) % VA_BUFFER_COUNT;
va_end(ap);
return dest;
const char *VA(const char *fmt, ...)
{
static char g_vaBuffer[VA_BUFFER_COUNT][VA_BUFFER_SIZE];
static int g_vaNextBufferIndex = 0;
va_list ap;
va_start(ap, fmt);
char* dest = g_vaBuffer[g_vaNextBufferIndex];
vsnprintf_s(g_vaBuffer[g_vaNextBufferIndex], VA_BUFFER_SIZE, fmt, ap);
g_vaNextBufferIndex = (g_vaNextBufferIndex + 1) % VA_BUFFER_COUNT;
va_end(ap);
return dest;
}
std::string ToLower(std::string input)