mirror of
https://github.com/AxioDL/PrimeWorldEditor.git
synced 2025-12-14 23:56:23 +00:00
Added property name generation system using dictionary attacks and added a UI dialog that allows you to search for property names and apply them to templates
This commit is contained in:
@@ -223,7 +223,11 @@ void CMasterTemplate::RenameProperty(IPropertyTemplate *pTemp, const TString& rk
|
||||
{
|
||||
u32 ID = pTemp->PropertyID();
|
||||
if (ID <= 0xFF) ID = CreatePropertyID(pTemp);
|
||||
RenameProperty(ID, rkNewName);
|
||||
}
|
||||
|
||||
void CMasterTemplate::RenameProperty(u32 ID, const TString& rkNewName)
|
||||
{
|
||||
// Master name list
|
||||
auto NameIt = smPropertyNames.find(ID);
|
||||
TString Original;
|
||||
@@ -249,17 +253,15 @@ void CMasterTemplate::RenameProperty(IPropertyTemplate *pTemp, const TString& rk
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<TString> CMasterTemplate::XMLsUsingID(u32 ID)
|
||||
void CMasterTemplate::XMLsUsingID(u32 ID, std::vector<TString>& rOutList)
|
||||
{
|
||||
auto InfoIt = smIDMap.find(ID);
|
||||
|
||||
if (InfoIt != smIDMap.end())
|
||||
{
|
||||
const SPropIDInfo& rkInfo = InfoIt->second;
|
||||
return rkInfo.XMLList;
|
||||
rOutList = rkInfo.XMLList;
|
||||
}
|
||||
else
|
||||
return std::vector<TString>();
|
||||
}
|
||||
|
||||
const std::vector<IPropertyTemplate*>* CMasterTemplate::TemplatesWithMatchingID(IPropertyTemplate *pTemp)
|
||||
|
||||
@@ -69,7 +69,8 @@ public:
|
||||
static u32 CreatePropertyID(IPropertyTemplate *pTemp);
|
||||
static void AddProperty(IPropertyTemplate *pTemp, const TString& rkTemplateName = "");
|
||||
static void RenameProperty(IPropertyTemplate *pTemp, const TString& rkNewName);
|
||||
static std::vector<TString> XMLsUsingID(u32 ID);
|
||||
static void RenameProperty(u32 ID, const TString& rkNewName);
|
||||
static void XMLsUsingID(u32 ID, std::vector<TString>& rOutList);
|
||||
static const std::vector<IPropertyTemplate*>* TemplatesWithMatchingID(IPropertyTemplate *pTemp);
|
||||
};
|
||||
|
||||
|
||||
211
src/Core/Resource/Script/CPropertyNameGenerator.cpp
Normal file
211
src/Core/Resource/Script/CPropertyNameGenerator.cpp
Normal file
@@ -0,0 +1,211 @@
|
||||
#include "CPropertyNameGenerator.h"
|
||||
#include "IUIRelay.h"
|
||||
#include "Core/Resource/Factory/CTemplateLoader.h"
|
||||
#include "Core/Resource/Script/CMasterTemplate.h"
|
||||
#include <Common/Hash/CCRC32.h>
|
||||
|
||||
/** Default constructor */
|
||||
CPropertyNameGenerator::CPropertyNameGenerator()
|
||||
: mWordListLoadStarted(false)
|
||||
, mWordListLoadFinished(false)
|
||||
, mIsRunning(false)
|
||||
, mFinishedRunning(false)
|
||||
{
|
||||
}
|
||||
|
||||
void CPropertyNameGenerator::Warmup()
|
||||
{
|
||||
// Clear output from previous runs
|
||||
ASSERT(!mWordListLoadStarted || mWordListLoadFinished);
|
||||
mWordListLoadFinished = false;
|
||||
mWordListLoadStarted = true;
|
||||
mWords.clear();
|
||||
|
||||
// Load the word list from the file
|
||||
FILE* pListFile = fopen("../resources/WordList.txt", "r");
|
||||
ASSERT(pListFile);
|
||||
|
||||
while (!feof(pListFile))
|
||||
{
|
||||
char WordBuffer[256];
|
||||
fgets(&WordBuffer[0], 256, pListFile);
|
||||
|
||||
// Capitalize first letter
|
||||
if (WordBuffer[0] >= 'a' && WordBuffer[0] <= 'z')
|
||||
{
|
||||
WordBuffer[0] -= 0x20;
|
||||
}
|
||||
|
||||
SWord Word;
|
||||
Word.Word = TString(WordBuffer).Trimmed();
|
||||
Word.Usages = 0;
|
||||
mWords.push_back(Word);
|
||||
}
|
||||
|
||||
fclose(pListFile);
|
||||
mWordListLoadFinished = true;
|
||||
}
|
||||
|
||||
void CPropertyNameGenerator::Generate(const SPropertyNameGenerationParameters& rkParams, IProgressNotifier* pProgress)
|
||||
{
|
||||
// Make sure all prerequisite data is loaded!
|
||||
ASSERT(!mIsRunning);
|
||||
ASSERT(rkParams.TypeNames.size() > 0);
|
||||
mGeneratedNames.clear();
|
||||
mIsRunning = true;
|
||||
mFinishedRunning = false;
|
||||
|
||||
// If we haven't loaded the word list yet, load it.
|
||||
// If we are still loading the word list, wait until we're finished.
|
||||
if (!mWordListLoadFinished)
|
||||
{
|
||||
if (mWordListLoadStarted)
|
||||
while (!mWordListLoadFinished) {}
|
||||
else
|
||||
Warmup();
|
||||
}
|
||||
|
||||
// Calculate the number of steps involved in this task.
|
||||
const int kNumWords = mWords.size();
|
||||
const int kMaxWords = rkParams.MaxWords;
|
||||
int TestsDone = 0;
|
||||
int TotalTests = 1;
|
||||
|
||||
for (int i = 0; i < kMaxWords; i++)
|
||||
TotalTests *= kNumWords;
|
||||
|
||||
pProgress->SetOneShotTask("Generating property names");
|
||||
pProgress->Report(TestsDone, TotalTests);
|
||||
|
||||
// Configure params needed to run the name generation!
|
||||
bool WriteToLog = rkParams.PrintToLog;
|
||||
bool SaveResults = true;
|
||||
|
||||
// The prefix only needs to be hashed this one time
|
||||
CCRC32 PrefixHash;
|
||||
PrefixHash.Hash( *rkParams.Prefix );
|
||||
|
||||
// Use a stack to keep track of the current word we are on. We can use this
|
||||
// to cache the hash of a word and then re-use it later instead of recaculating
|
||||
// the same hashes over and over. Init the stack with the first word.
|
||||
struct SWordCache
|
||||
{
|
||||
int WordIndex;
|
||||
CCRC32 Hash;
|
||||
};
|
||||
std::vector<SWordCache> WordCache;
|
||||
|
||||
SWordCache FirstWord { -1, CCRC32() };
|
||||
WordCache.push_back(FirstWord);
|
||||
|
||||
while ( true )
|
||||
{
|
||||
// Increment the current word, handle wrapping back to 0, and update cached hashes as needed.
|
||||
int RecalcIndex = WordCache.size() - 1;
|
||||
WordCache.back().WordIndex++;
|
||||
|
||||
while (WordCache[RecalcIndex].WordIndex >= kNumWords)
|
||||
{
|
||||
WordCache[RecalcIndex].WordIndex = 0;
|
||||
|
||||
if (RecalcIndex > 0)
|
||||
{
|
||||
RecalcIndex--;
|
||||
WordCache[RecalcIndex].WordIndex++;
|
||||
}
|
||||
else
|
||||
{
|
||||
SWordCache NewWord { 0, CCRC32() };
|
||||
WordCache.push_back(NewWord);
|
||||
}
|
||||
}
|
||||
|
||||
// If we've hit the word limit, break out and end the name generation system.
|
||||
if (WordCache.size() > kMaxWords)
|
||||
break;
|
||||
|
||||
// Now that all words are updated, calculate the new hashes.
|
||||
CCRC32 LastValidHash = (RecalcIndex > 0 ? WordCache[RecalcIndex-1].Hash : PrefixHash);
|
||||
|
||||
for (; RecalcIndex < WordCache.size(); RecalcIndex++)
|
||||
{
|
||||
int Index = WordCache[RecalcIndex].WordIndex;
|
||||
LastValidHash.Hash( *mWords[Index].Word );
|
||||
WordCache[RecalcIndex].Hash = LastValidHash;
|
||||
}
|
||||
|
||||
// We got our hash yay! Now hash the suffix and then we can test with each type name
|
||||
CCRC32 BaseHash = LastValidHash;
|
||||
BaseHash.Hash( *rkParams.Suffix );
|
||||
|
||||
for (int TypeIdx = 0; TypeIdx < rkParams.TypeNames.size(); TypeIdx++)
|
||||
{
|
||||
CCRC32 FullHash = BaseHash;
|
||||
FullHash.Hash( *rkParams.TypeNames[TypeIdx] );
|
||||
u32 PropertyID = FullHash.Digest();
|
||||
|
||||
// Check if this hash is a property ID - it's valid if there are any XMLs using this ID
|
||||
SGeneratedPropertyName PropertyName;
|
||||
CMasterTemplate::XMLsUsingID(PropertyID, PropertyName.XmlList);
|
||||
|
||||
if (PropertyName.XmlList.size() > 0)
|
||||
{
|
||||
// Generate a string with the complete name. (We wait to do this until now to avoid needless string allocation)
|
||||
PropertyName.Name = rkParams.Prefix;
|
||||
|
||||
for (int WordIdx = 0; WordIdx < WordCache.size(); WordIdx++)
|
||||
{
|
||||
int Index = WordCache[WordIdx].WordIndex;
|
||||
PropertyName.Name += mWords[Index].Word;
|
||||
}
|
||||
|
||||
PropertyName.Name += rkParams.Suffix;
|
||||
PropertyName.Type = rkParams.TypeNames[TypeIdx];
|
||||
PropertyName.ID = PropertyID;
|
||||
|
||||
if (SaveResults)
|
||||
{
|
||||
mGeneratedNames.push_back(PropertyName);
|
||||
|
||||
// Check if we have too many saved results. This can cause memory issues and crashing.
|
||||
// If we have too many saved results, then to avoid crashing we will force enable log output.
|
||||
if (mGeneratedNames.size() > 9999)
|
||||
{
|
||||
gpUIRelay->AsyncMessageBox("Warning", "There are over 10,000 results. To avoid memory issues, results will no longer print to the screen. Check the log for the rest of the output.");
|
||||
WriteToLog = true;
|
||||
SaveResults = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Log this out
|
||||
if ( WriteToLog )
|
||||
{
|
||||
TString DelimitedXmlList;
|
||||
|
||||
for (int XmlIdx = 0; XmlIdx < PropertyName.XmlList.size(); XmlIdx++)
|
||||
{
|
||||
DelimitedXmlList += PropertyName.XmlList[XmlIdx] + "\n";
|
||||
}
|
||||
|
||||
TString LogMsg = TString::Format("%s [%s] : 0x%08X\n", *PropertyName.Name, *PropertyName.Type, PropertyName.ID) + DelimitedXmlList;
|
||||
Log::Write(LogMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Every 250 tests, check with the progress notifier. Update the progress
|
||||
// bar and check whether the user has requested to cancel the operation.
|
||||
TestsDone++;
|
||||
|
||||
if ( (TestsDone % 250) == 0 )
|
||||
{
|
||||
if (pProgress->ShouldCancel())
|
||||
break;
|
||||
|
||||
pProgress->Report(TestsDone, TotalTests);
|
||||
}
|
||||
}
|
||||
|
||||
mIsRunning = false;
|
||||
mFinishedRunning = true;
|
||||
}
|
||||
86
src/Core/Resource/Script/CPropertyNameGenerator.h
Normal file
86
src/Core/Resource/Script/CPropertyNameGenerator.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#ifndef CPROPERTYNAMEGENERATOR_H
|
||||
#define CPROPERTYNAMEGENERATOR_H
|
||||
|
||||
#include "Core/IProgressNotifier.h"
|
||||
#include <Common/Common.h>
|
||||
|
||||
/** Parameters for using the name generator */
|
||||
struct SPropertyNameGenerationParameters
|
||||
{
|
||||
/** Maximum number of words per name; name generation will complete when all possibilities have been checked */
|
||||
int MaxWords;
|
||||
|
||||
/** Prefix to include at the beginning of every name */
|
||||
TString Prefix;
|
||||
|
||||
/** Suffix to include at the end of every name */
|
||||
TString Suffix;
|
||||
|
||||
/** List of valid type suffixes */
|
||||
std::vector<TString> TypeNames;
|
||||
|
||||
/** Whether to print the output from the generation process to the log */
|
||||
bool PrintToLog;
|
||||
};
|
||||
|
||||
/** A generated property name */
|
||||
struct SGeneratedPropertyName
|
||||
{
|
||||
TString Name;
|
||||
TString Type;
|
||||
u32 ID;
|
||||
std::vector<TString> XmlList;
|
||||
};
|
||||
|
||||
/** Generates property names and validates them against know property IDs. */
|
||||
class CPropertyNameGenerator
|
||||
{
|
||||
/** Whether we have started loading the word list */
|
||||
bool mWordListLoadStarted;
|
||||
|
||||
/** Whether the word list has been fully loaded */
|
||||
bool mWordListLoadFinished;
|
||||
|
||||
/** Whether the generation process is running */
|
||||
bool mIsRunning;
|
||||
|
||||
/** Whether the generation process finished running */
|
||||
bool mFinishedRunning;
|
||||
|
||||
/** List of words */
|
||||
struct SWord
|
||||
{
|
||||
TString Word;
|
||||
int Usages;
|
||||
};
|
||||
std::vector<SWord> mWords;
|
||||
|
||||
/** List of output generated property names */
|
||||
std::list<SGeneratedPropertyName> mGeneratedNames;
|
||||
|
||||
/** List of word indices */
|
||||
std::vector<int> mWordIndices;
|
||||
|
||||
public:
|
||||
/** Default constructor */
|
||||
CPropertyNameGenerator();
|
||||
|
||||
/** Prepares the generator for running name generation */
|
||||
void Warmup();
|
||||
|
||||
/** Run the name generation system */
|
||||
void Generate(const SPropertyNameGenerationParameters& rkParams, IProgressNotifier* pProgressNotifier);
|
||||
|
||||
/** Accessors */
|
||||
bool IsRunning() const
|
||||
{
|
||||
return mIsRunning;
|
||||
}
|
||||
|
||||
const std::list<SGeneratedPropertyName>& GetOutput() const
|
||||
{
|
||||
return mGeneratedNames;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CPROPERTYNAMEGENERATOR_H
|
||||
@@ -315,10 +315,10 @@ const char* HashablePropTypeName(EPropertyType Prop)
|
||||
case eFloatProperty: return "float";
|
||||
case eStringProperty: return "string";
|
||||
case eColorProperty: return "Color";
|
||||
case eVector3Property: return "Vector3f";
|
||||
case eSoundProperty: return "SfxId";
|
||||
case eVector3Property: return "Vector";
|
||||
case eSoundProperty: return "sound";
|
||||
case eAssetProperty: return "asset";
|
||||
case eMayaSplineProperty: return "MayaSpline";
|
||||
case eMayaSplineProperty: return "spline";
|
||||
|
||||
// All other types are either invalid or need a custom reimplementation because they can return multiple strings (like struct)
|
||||
default:
|
||||
|
||||
@@ -338,7 +338,7 @@ public:
|
||||
|
||||
const char* GetTypeNameString() const
|
||||
{
|
||||
return (Game() < eCorruptionProto ? "AnimationParameters" : "CharacterAnimationSet");
|
||||
return (Game() < eCorruptionProto ? "AnimationSet" : "CharacterAnimationSet");
|
||||
}
|
||||
|
||||
IMPLEMENT_TEMPLATE_CLONE(TCharacterTemplate)
|
||||
|
||||
Reference in New Issue
Block a user