Skip to content

Commit

Permalink
Improve suffix resolving
Browse files Browse the repository at this point in the history
  • Loading branch information
psiberx committed Sep 2, 2024
1 parent 7f04a20 commit ca5053b
Show file tree
Hide file tree
Showing 10 changed files with 103 additions and 31 deletions.
2 changes: 1 addition & 1 deletion lib/Red/TypeInfo/Resolving.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ inline const Handle<T>& Cast(const Handle<U>& aObject)
}

template<typename T, typename U = ISerializable>
inline const WeakHandle<T>& Cast(const WeakHandle<U>& aObject)
inline const WeakHandle<T>& CastWeak(const WeakHandle<U>& aObject)
{
static const WeakHandle<T> s_null;
return (aObject && aObject.instance->GetType()->IsA(Red::GetClass<T>()))
Expand Down
25 changes: 16 additions & 9 deletions src/App/Extensions/GarmentOverride/Dynamic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -464,16 +464,16 @@ App::DynamicAppearanceController::DynamicString App::DynamicAppearanceController
return result;
}

void App::DynamicAppearanceController::UpdateState(Red::Entity* aEntity)
void App::DynamicAppearanceController::UpdateState(Red::Entity* aEntity, Red::TweakDBID aEquippedItemID)
{
auto& state = m_states[aEntity];

state.values[GenderAttr] = GetSuffixData(aEntity, GenderSuffix);
state.values[CameraAttr] = GetSuffixData(aEntity, CameraSuffix);
state.values[BodyTypeAttr] = GetSuffixData(aEntity, BodyTypeSuffix);
state.values[ArmsStateAttr] = GetSuffixData(aEntity, ArmsStateSuffix);
state.values[FeetStateAttr] = GetSuffixData(aEntity, FeetStateSuffix);
state.values[InnerSleevesAttr] = GetSuffixData(aEntity, InnerSleevesSuffix);
state.values[GenderAttr] = GetSuffixData(aEntity, GenderSuffix, aEquippedItemID);
state.values[CameraAttr] = GetSuffixData(aEntity, CameraSuffix, aEquippedItemID);
state.values[BodyTypeAttr] = GetSuffixData(aEntity, BodyTypeSuffix, aEquippedItemID);
state.values[ArmsStateAttr] = GetSuffixData(aEntity, ArmsStateSuffix, aEquippedItemID);
state.values[FeetStateAttr] = GetSuffixData(aEntity, FeetStateSuffix, aEquippedItemID);
state.values[InnerSleevesAttr] = GetSuffixData(aEntity, InnerSleevesSuffix, aEquippedItemID);

auto custimizationData = GetCustomizationData(aEntity);
state.values[SkinColorAttr] = {custimizationData.skinColor};
Expand Down Expand Up @@ -501,7 +501,8 @@ void App::DynamicAppearanceController::RemoveState(Red::Entity* aEntity)
}

App::DynamicAttributeData App::DynamicAppearanceController::GetSuffixData(Red::Entity* aEntity,
Red::TweakDBID aSuffixID) const
Red::TweakDBID aSuffixID,
Red::TweakDBID aEquippedItemID) const
{
DynamicAttributeData data{};

Expand All @@ -510,7 +511,7 @@ App::DynamicAttributeData App::DynamicAppearanceController::GetSuffixData(Red::E
auto* handle = reinterpret_cast<Red::Handle<Red::GameObject>*>(&aEntity->ref);

Red::CString suffixValue;
Raw::AppearanceChanger::GetSuffixValue({}, 1ull, *handle, aSuffixID, suffixValue);
(~Raw::AppearanceChanger::GetSuffixValue)({aEquippedItemID}, 1ull, *handle, aSuffixID, suffixValue);

data.suffix = suffixValue.c_str();
}
Expand Down Expand Up @@ -606,6 +607,12 @@ std::string App::DynamicAppearanceController::GetPathString(Red::ResourcePath aP
return m_pathRegistry->ResolvePath(aPath);
}

std::string App::DynamicAppearanceController::GetPathStringOrHash(Red::ResourcePath aPath) const
{
auto pathStr = m_pathRegistry->ResolvePath(aPath);
return !pathStr.empty() ? pathStr : std::to_string(aPath.hash);
}

bool App::DynamicAppearanceController::SupportsDynamicAppearance(const Red::EntityTemplate* aTemplate)
{
return aTemplate->visualTagsSchema &&
Expand Down
6 changes: 4 additions & 2 deletions src/App/Extensions/GarmentOverride/Dynamic.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ class DynamicAppearanceController
[[nodiscard]] Red::ResourcePath ResolvePath(Red::Entity* aEntity, const DynamicPartList& aVariant,
Red::ResourcePath aPath) const;

void UpdateState(Red::Entity* aEntity);
void UpdateState(Red::Entity* aEntity, Red::TweakDBID aEquippedItemID = {});
void RemoveState(Red::Entity* aEntity);

bool SupportsDynamicAppearance(const Red::EntityTemplate* aTemplate);
Expand All @@ -103,6 +103,7 @@ class DynamicAppearanceController
std::string_view GetBaseAppearanceName(Red::CName aAppearanceName);

[[nodiscard]] std::string GetPathString(Red::ResourcePath aPath) const;
[[nodiscard]] std::string GetPathStringOrHash(Red::ResourcePath aPath) const;

static bool IsMale(Red::Entity* aEntity);

Expand Down Expand Up @@ -131,7 +132,8 @@ class DynamicAppearanceController
Red::CName hairColor;
};

DynamicAttributeData GetSuffixData(Red::Entity* aEntity, Red::TweakDBID aSuffixID) const;
DynamicAttributeData GetSuffixData(Red::Entity* aEntity, Red::TweakDBID aSuffixID,
Red::TweakDBID aEquippedItemID) const;
CustomizationData GetCustomizationData(Red::Entity* aEntity) const;

Core::Map<Red::Entity*, EntityState> m_states;
Expand Down
74 changes: 58 additions & 16 deletions src/App/Extensions/GarmentOverride/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ bool App::GarmentOverrideModule::Load()
HookBefore<Raw::ItemFactoryAppearanceChangeRequest::LoadAppearance>(&OnChangeAppearanceResource).OrThrow();
Hook<Raw::EntityTemplate::FindAppearance>(&OnResolveAppearance).OrThrow();
HookAfter<Raw::AppearanceResource::FindAppearance>(&OnResolveDefinition).OrThrow();
Hook<Raw::AppearanceChanger::GetSuffixes>(&OnResolveSuffixes).OrThrow();
HookAfter<Raw::AppearanceNameVisualTagsPreset::GetVisualTags>(&OnGetVisualTags).OrThrow();
HookAfter<Raw::GarmentAssembler::FindState>(&OnFindState).OrThrow();
HookBefore<Raw::GarmentAssemblerState::AddItem>(&OnAddItem).OrThrow();
Expand Down Expand Up @@ -74,6 +75,7 @@ bool App::GarmentOverrideModule::Unload()
Unhook<Raw::ItemFactoryAppearanceChangeRequest::LoadAppearance>();
Unhook<Raw::EntityTemplate::FindAppearance>();
Unhook<Raw::AppearanceResource::FindAppearance>();
Unhook<Raw::AppearanceChanger::GetSuffixes>();
Unhook<Raw::AppearanceNameVisualTagsPreset::GetVisualTags>();
Unhook<Raw::GarmentAssembler::FindState>();
Unhook<Raw::GarmentAssemblerState::AddItem>();
Expand Down Expand Up @@ -114,14 +116,11 @@ void App::GarmentOverrideModule::OnLoadAppearanceResource(Red::ItemFactoryReques
auto& entityWeak = Raw::ItemFactoryRequest::Entity::Ref(aRequest);
auto& templateToken = Raw::ItemFactoryRequest::EntityTemplate::Ref(aRequest);
auto& appearanceName = Raw::ItemFactoryRequest::AppearanceName::Ref(aRequest);
auto itemRecord = Raw::ItemFactoryRequest::ItemRecord::Ptr(aRequest);

if (!appearanceName)
if (!appearanceName && itemRecord)
{
auto itemRecord = Raw::ItemFactoryRequest::ItemRecord::Ptr(aRequest);
if (itemRecord)
{
appearanceName = Red::GetFlatValue<Red::CName>({itemRecord->recordID, ".appearanceName"});
}
appearanceName = Red::GetFlatValue<Red::CName>({itemRecord->recordID, ".appearanceName"});
}

if (PrepareDynamicAppearanceName(entityWeak, templateToken, appearanceName))
Expand All @@ -132,7 +131,14 @@ void App::GarmentOverrideModule::OnLoadAppearanceResource(Red::ItemFactoryReques
#ifndef NDEBUG
LogDebug("|{}| [event=LoadAppearanceResource entity={}]", ModuleName, entityState->GetName());
#endif
UpdateDynamicAttributes(entityState);
if (itemRecord)
{
UpdateDynamicAttributes(entityState, itemRecord->recordID);
}
else
{
UpdateDynamicAttributes(entityState);
}
}
}
}
Expand All @@ -142,14 +148,11 @@ void App::GarmentOverrideModule::OnChangeAppearanceResource(Red::ItemFactoryAppe
auto& entityWeak = Raw::ItemFactoryAppearanceChangeRequest::Entity::Ref(aRequest);
auto& templateToken = Raw::ItemFactoryAppearanceChangeRequest::EntityTemplate::Ref(aRequest);
auto& appearanceName = Raw::ItemFactoryAppearanceChangeRequest::AppearanceName::Ref(aRequest);
auto itemRecord = Raw::ItemFactoryAppearanceChangeRequest::ItemRecord::Ptr(aRequest);

if (!appearanceName)
if (!appearanceName && itemRecord)
{
auto itemRecord = Raw::ItemFactoryAppearanceChangeRequest::ItemRecord::Ptr(aRequest);
if (itemRecord)
{
appearanceName = Red::GetFlatValue<Red::CName>({itemRecord->recordID, ".appearanceName"});
}
appearanceName = Red::GetFlatValue<Red::CName>({itemRecord->recordID, ".appearanceName"});
}

if (PrepareDynamicAppearanceName(entityWeak, templateToken, appearanceName))
Expand All @@ -160,7 +163,14 @@ void App::GarmentOverrideModule::OnChangeAppearanceResource(Red::ItemFactoryAppe
#ifndef NDEBUG
LogDebug("|{}| [event=ChangeAppearanceResource entity={}]", ModuleName, entityState->GetName());
#endif
UpdateDynamicAttributes(entityState);
if (itemRecord)
{
UpdateDynamicAttributes(entityState, itemRecord->recordID);
}
else
{
UpdateDynamicAttributes(entityState);
}
}
}
}
Expand Down Expand Up @@ -274,13 +284,39 @@ void App::GarmentOverrideModule::OnResolveDefinition(Red::AppearanceResource* aR
}
}

void App::GarmentOverrideModule::OnGetVisualTags(Red::AppearanceNameVisualTagsPreset& aPreset,
void* App::GarmentOverrideModule::OnResolveSuffixes(Red::CString& aResult,
Red::Handle<Red::GameObject>& aOwner,
Red::Handle<Red::GameObject>& aOwnerOverride,
const Red::TweakDBRecord& aItemRecord,
const Red::ItemID& aItemID)
{
auto appearanceName = Red::GetFlatValue<Red::CName>({aItemRecord.recordID, ".appearanceName"});
if (s_dynamicAppearance->IsDynamicAppearanceName(appearanceName))
{
if (aResult.Length() > 0)
{
aResult = {};
}
return &aResult;
}

return Raw::AppearanceChanger::GetSuffixes(aResult, aOwner, aOwnerOverride, aItemRecord, aItemID);
}

void App::GarmentOverrideModule::OnGetVisualTags(Red::AppearanceNameVisualTagsPreset* aPreset,
Red::ResourcePath aEntityPath, Red::CName aAppearanceName,
Red::TagList& aFinalTags)
{
if (!aAppearanceName) // aFinalTags.tags.size > 0
if (!aAppearanceName || aAppearanceName == EmptyAppearanceName)
return;

#ifndef NDEBUG
if (aFinalTags.tags.size == 0)
{
LogDebug("|{}| [event=GetVisualTags ent={} app={}]", ModuleName, aEntityPath.hash, aAppearanceName.ToString());
}
#endif

auto cacheKey = Red::FNV1a64(aAppearanceName.ToString(), aEntityPath.hash);

{
Expand Down Expand Up @@ -859,6 +895,12 @@ void App::GarmentOverrideModule::UpdateDynamicAttributes(Core::SharedPtr<EntityS
aEntityState->UpdateDynamicAttributes();
}

void App::GarmentOverrideModule::UpdateDynamicAttributes(Core::SharedPtr<EntityState>& aEntityState,
Red::TweakDBID aEquippedItemID)
{
aEntityState->UpdateDynamicAttributes(aEquippedItemID);
}

void App::GarmentOverrideModule::UpdateDynamicAttributes()
{
s_stateManager->UpdateDynamicAttributes();
Expand Down
8 changes: 7 additions & 1 deletion src/App/Extensions/GarmentOverride/Module.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,12 @@ class GarmentOverrideModule : public ConfigurableModuleImpl<GarmentOverrideConfi
static void OnResolveDefinition(Red::AppearanceResource* aResource,
Red::Handle<Red::AppearanceDefinition>* aDefinition,
Red::CName aAppearanceSelector, uint32_t a4, uint8_t a5);
static void OnGetVisualTags(Red::AppearanceNameVisualTagsPreset& aPreset,
static void* OnResolveSuffixes(Red::CString& aResult,
Red::Handle<Red::GameObject>& aOwner,
Red::Handle<Red::GameObject>& aOwnerOverride,
const Red::TweakDBRecord& aItemRecord,
const Red::ItemID& aItemID);
static void OnGetVisualTags(Red::AppearanceNameVisualTagsPreset* aPreset,
Red::ResourcePath aEntityPath,
Red::CName aAppearanceName,
Red::TagList& aFinalTags);
Expand Down Expand Up @@ -101,6 +106,7 @@ class GarmentOverrideModule : public ConfigurableModuleImpl<GarmentOverrideConfi
Red::DynArray<Red::ResourcePath>& aResourcePaths);

static void UpdateDynamicAttributes(Core::SharedPtr<EntityState>& aEntityState);
static void UpdateDynamicAttributes(Core::SharedPtr<EntityState>& aEntityState, Red::TweakDBID aEquippedItemID);
static void UpdateDynamicAttributes();

static bool IsUniqueAppearanceName(Red::CName aName);
Expand Down
5 changes: 5 additions & 0 deletions src/App/Extensions/GarmentOverride/States.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,11 @@ void App::EntityState::UpdateDynamicAttributes()
m_dynamicAppearance->UpdateState(m_entity);
}

void App::EntityState::UpdateDynamicAttributes(Red::TweakDBID aEquippedItemID)
{
m_dynamicAppearance->UpdateState(m_entity, aEquippedItemID);
}

bool App::EntityState::SelectDynamicAppearance(App::DynamicAppearanceName& aSelector, Red::EntityTemplate* aResource,
Red::TemplateAppearance*& aAppearance)
{
Expand Down
1 change: 1 addition & 0 deletions src/App/Extensions/GarmentOverride/States.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ class EntityState
void LinkPartToAppearance(Red::ResourcePath aResource, DynamicAppearanceName& aAppearance);
void LinkPartToAppearance(Red::ResourcePath aResource, Red::CName aAppearance);
void UpdateDynamicAttributes();
void UpdateDynamicAttributes(Red::TweakDBID aEquippedItemID);

void RemoveAllOverrides(uint64_t aHash);

Expand Down
1 change: 1 addition & 0 deletions src/Red/Addresses/Library.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ constexpr uint32_t AnimatedComponent_InitializeAnimations = 2855474741;

constexpr uint32_t AppearanceChanger_ComputePlayerGarment = 3243419919;
constexpr uint32_t AppearanceChanger_GetBaseMeshOffset = 4219677283;
constexpr uint32_t AppearanceChanger_GetSuffixes = 332345115;
constexpr uint32_t AppearanceChanger_GetSuffixValue = 1317875529;
constexpr uint32_t AppearanceChanger_RegisterPart = 3169139695;
constexpr uint32_t AppearanceChanger_SelectAppearanceName = 2770550105;
Expand Down
10 changes: 9 additions & 1 deletion src/Red/AppearanceChanger.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,17 @@ RED4EXT_ASSERT_OFFSET(AppearanceChangeRequest, newAppearance, 0x20);

namespace Raw::AppearanceChanger
{
constexpr auto GetSuffixes = Core::RawFunc<
/* addr = */ Red::AddressLib::AppearanceChanger_GetSuffixes,
/* type = */ void* (*)(Red::CString& aResult,
Red::Handle<Red::GameObject>& aOwner,
Red::Handle<Red::GameObject>& aOwnerOverride,
const Red::TweakDBRecord& aItemRecord,
const Red::ItemID& aItemID)>();

constexpr auto GetSuffixValue = Core::RawFunc<
/* addr = */ Red::AddressLib::AppearanceChanger_GetSuffixValue,
/* type = */ bool (*)(Red::ItemID aItemID,
/* type = */ bool (*)(const Red::ItemID& aItemID,
uint64_t a2, // Must be non-zero
Red::Handle<Red::GameObject>& aOwner,
Red::TweakDBID aSuffixRecordID,
Expand Down
2 changes: 1 addition & 1 deletion src/Red/VisualTagsPreset.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace Raw::AppearanceNameVisualTagsPreset
{
constexpr auto GetVisualTags = Core::RawFunc<
/* addr = */ Red::AddressLib::AppearanceNameVisualTagsPreset_GetVisualTags,
/* type = */ void (*)(Red::AppearanceNameVisualTagsPreset& aPreset, // FIXME: might not be a preset anymore
/* type = */ void (*)(Red::AppearanceNameVisualTagsPreset* aPreset, // FIXME: might not be a preset anymore
Red::ResourcePath aEntityPath,
Red::CName aAppearanceName,
Red::TagList& aOutTags)>();
Expand Down

0 comments on commit ca5053b

Please sign in to comment.