mirror of
https://github.com/AxioDL/PrimeWorldEditor.git
synced 2025-06-21 14:03:43 +00:00
Added functionality to import filenames from .pak.contents.txt files
This commit is contained in:
parent
ed446ccbec
commit
d96a3c2af7
@ -317,4 +317,5 @@ SOURCES += \
|
||||
Resource/Animation/CSkeleton.cpp \
|
||||
Resource/Animation/IMetaAnimation.cpp \
|
||||
Resource/Animation/IMetaTransition.cpp \
|
||||
GameProject/AssetNameGeneration.cpp
|
||||
GameProject/AssetNameGeneration.cpp \
|
||||
GameProject/CAssetNameMap.cpp
|
||||
|
@ -207,10 +207,11 @@ void GenerateAssetNames(CGameProject *pProj)
|
||||
{
|
||||
TString Name = pInst->InstanceName();
|
||||
|
||||
if (Name.EndsWith(".scan"))
|
||||
if (Name.EndsWith(".SCAN", false))
|
||||
{
|
||||
TIDString ScanIDString = (pProj->Game() <= ePrime ? "0x4:0x0" : "0xBDBEC295:0xB94E9BE7");
|
||||
TAssetProperty *pScanProperty = TPropCast<TAssetProperty>(pInst->PropertyByIDString(ScanIDString));
|
||||
ASSERT(pScanProperty); // Temporary assert to remind myself later to update this code when uncooked properties are added to the template
|
||||
|
||||
if (pScanProperty)
|
||||
{
|
||||
@ -220,6 +221,10 @@ void GenerateAssetNames(CGameProject *pProj)
|
||||
if (pEntry && !pEntry->IsNamed())
|
||||
{
|
||||
TWideString ScanName = Name.ToUTF16().ChopBack(5);
|
||||
|
||||
if (ScanName.StartsWith(L"POI_"))
|
||||
ScanName = ScanName.ChopFront(4);
|
||||
|
||||
ApplyGeneratedName(pEntry, pEntry->DirectoryPath(), ScanName);
|
||||
|
||||
CScan *pScan = (CScan*) pEntry->Load();
|
||||
@ -232,6 +237,34 @@ void GenerateAssetNames(CGameProject *pProj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (pInst->ObjectTypeID() == 0x17 || pInst->ObjectTypeID() == FOURCC("MEMO"))
|
||||
{
|
||||
TString Name = pInst->InstanceName();
|
||||
|
||||
if (Name.EndsWith(".STRG", false))
|
||||
{
|
||||
u32 StringPropID = (pProj->Game() <= ePrime ? 0x4 : 0x9182250C);
|
||||
TAssetProperty *pStringProperty = TPropCast<TAssetProperty>(pInst->Properties()->PropertyByID(StringPropID));
|
||||
ASSERT(pStringProperty); // Temporary assert to remind myself later to update this code when uncooked properties are added to the template
|
||||
|
||||
if (pStringProperty)
|
||||
{
|
||||
CAssetID StringID = pStringProperty->Get();
|
||||
CResourceEntry *pEntry = pStore->FindEntry(StringID);
|
||||
|
||||
if (pEntry && !pEntry->IsNamed())
|
||||
{
|
||||
TWideString StringName = Name.ToUTF16().ChopBack(5);
|
||||
|
||||
if (StringName.StartsWith(L"HUDMemo - "))
|
||||
StringName = StringName.ChopFront(10);
|
||||
|
||||
ApplyGeneratedName(pEntry, pEntry->DirectoryPath(), StringName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
50
src/Core/GameProject/CAssetNameMap.cpp
Normal file
50
src/Core/GameProject/CAssetNameMap.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
#include "CAssetNameMap.h"
|
||||
|
||||
std::map<EGame, CAssetNameMap*> CAssetNameMap::smGameMap;
|
||||
|
||||
CAssetNameMap::CAssetNameMap(EGame Game)
|
||||
: mGame(Game)
|
||||
{
|
||||
TString ListPath = GetAssetListPath(mGame);
|
||||
CXMLReader Reader(ListPath);
|
||||
Serialize(Reader);
|
||||
}
|
||||
|
||||
void CAssetNameMap::SaveAssetNames()
|
||||
{
|
||||
TString ListPath = GetAssetListPath(mGame);
|
||||
CXMLWriter Writer(ListPath, "AssetList", 0, mGame);
|
||||
Serialize(Writer);
|
||||
}
|
||||
|
||||
void CAssetNameMap::GetNameInfo(CAssetID ID, TString& rOutDirectory, TString& rOutName)
|
||||
{
|
||||
auto It = mMap.find(ID);
|
||||
|
||||
if (It != mMap.end())
|
||||
{
|
||||
SAssetNameInfo& rInfo = It->second;
|
||||
rOutName = rInfo.Name;
|
||||
rOutDirectory = rInfo.Directory;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
rOutDirectory = "Uncategorized\\";
|
||||
rOutName = ID.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
void CAssetNameMap::CopyFromStore(CResourceStore *pStore /*= gpResourceStore*/)
|
||||
{
|
||||
for (CResourceIterator It(pStore); It; ++It)
|
||||
{
|
||||
if (It->IsCategorized() || It->IsNamed())
|
||||
{
|
||||
CAssetID ID = It->ID();
|
||||
TWideString Name = It->Name();
|
||||
TWideString Directory = It->Directory()->FullPath();
|
||||
mMap[ID] = SAssetNameInfo { Name, Directory };
|
||||
}
|
||||
}
|
||||
}
|
@ -10,84 +10,50 @@
|
||||
|
||||
const TString gkAssetListDir = "..\\resources\\list\\";
|
||||
|
||||
struct SAssetNameInfo
|
||||
{
|
||||
TWideString Name;
|
||||
TWideString Directory;
|
||||
|
||||
void Serialize(IArchive& rArc)
|
||||
{
|
||||
rArc << SERIAL_AUTO(Name) << SERIAL_AUTO(Directory);
|
||||
}
|
||||
};
|
||||
|
||||
class CAssetNameMap
|
||||
{
|
||||
typedef std::map<CAssetID, SAssetNameInfo> TAssetMap;
|
||||
std::shared_ptr<TAssetMap> mpMap;
|
||||
struct SAssetNameInfo
|
||||
{
|
||||
TWideString Name;
|
||||
TWideString Directory;
|
||||
|
||||
void Serialize(IArchive& rArc)
|
||||
{
|
||||
rArc << SERIAL_AUTO(Name) << SERIAL_AUTO(Directory);
|
||||
}
|
||||
};
|
||||
|
||||
EGame mGame;
|
||||
std::map<CAssetID, SAssetNameInfo> mMap;
|
||||
static std::map<EGame, CAssetNameMap*> smGameMap;
|
||||
|
||||
// Private Methods
|
||||
CAssetNameMap(EGame Game);
|
||||
|
||||
void Serialize(IArchive& rArc)
|
||||
{
|
||||
rArc << SERIAL_CONTAINER("AssetNameMap", *mpMap.get(), "Asset");
|
||||
rArc << SERIAL_CONTAINER("AssetNameMap", mMap, "Asset");
|
||||
}
|
||||
|
||||
public:
|
||||
CAssetNameMap()
|
||||
{
|
||||
mpMap = std::make_shared<TAssetMap>(TAssetMap());
|
||||
}
|
||||
|
||||
void GetNameInfo(CAssetID ID, TString& rOutDirectory, TString& rOutName)
|
||||
{
|
||||
auto It = mpMap->find(ID);
|
||||
|
||||
if (It != mpMap->end())
|
||||
{
|
||||
SAssetNameInfo& rInfo = It->second;
|
||||
rOutName = rInfo.Name;
|
||||
rOutDirectory = rInfo.Directory;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
rOutDirectory = "Uncategorized\\";
|
||||
rOutName = ID.ToString();
|
||||
}
|
||||
}
|
||||
void SaveAssetNames();
|
||||
void GetNameInfo(CAssetID ID, TString& rOutDirectory, TString& rOutName);
|
||||
void CopyFromStore(CResourceStore *pStore);
|
||||
|
||||
// Static Methods
|
||||
static TString GetAssetListPath(EGame Game)
|
||||
{
|
||||
return gkAssetListDir + "AssetList" + GetGameShortName(Game) + ".xml";
|
||||
}
|
||||
|
||||
static CAssetNameMap LoadAssetNames(EGame Game)
|
||||
static CAssetNameMap* GetGameNameMap(EGame Game)
|
||||
{
|
||||
TString ListPath = GetAssetListPath(Game);
|
||||
CXMLReader Reader(ListPath);
|
||||
auto Find = smGameMap.find(Game);
|
||||
if (Find != smGameMap.end()) return Find->second;
|
||||
|
||||
CAssetNameMap Map;
|
||||
Map.Serialize(Reader);
|
||||
return Map;
|
||||
}
|
||||
|
||||
static void SaveAssetNames(CResourceStore *pStore = gpResourceStore)
|
||||
{
|
||||
CAssetNameMap Map;
|
||||
|
||||
for (CResourceIterator It(pStore); It; ++It)
|
||||
{
|
||||
if (It->IsCategorized() || It->IsNamed())
|
||||
{
|
||||
CAssetID ID = It->ID();
|
||||
TWideString Name = It->Name();
|
||||
TWideString Directory = It->Directory()->FullPath();
|
||||
(*Map.mpMap)[ID] = SAssetNameInfo { Name, Directory };
|
||||
}
|
||||
}
|
||||
|
||||
TString ListPath = GetAssetListPath(pStore->Game());
|
||||
CXMLWriter Writer(ListPath, "AssetList", 0, pStore->Game());
|
||||
Map.Serialize(Writer);
|
||||
CAssetNameMap *pMap = new CAssetNameMap(Game);
|
||||
smGameMap[Game] = pMap;
|
||||
return pMap;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
#define EXPORT_COOKED 1
|
||||
|
||||
CGameExporter::CGameExporter(const TString& rkInputDir, const TString& rkOutputDir)
|
||||
: mpNameMap(nullptr)
|
||||
{
|
||||
mGame = eUnknownGame;
|
||||
mGameDir = FileUtil::MakeAbsolute(rkInputDir);
|
||||
@ -51,7 +52,7 @@ bool CGameExporter::Export()
|
||||
mCookedDir = mpStore->CookedDir(false);
|
||||
|
||||
#if USE_ASSET_NAME_MAP
|
||||
mNameMap = CAssetNameMap::LoadAssetNames(mGame);
|
||||
mpNameMap = CAssetNameMap::GetGameNameMap(mGame);
|
||||
#endif
|
||||
|
||||
// Export game data
|
||||
@ -470,7 +471,7 @@ void CGameExporter::ExportResource(SResourceInstance& rRes)
|
||||
|
||||
// Register resource and write to file
|
||||
TString Directory, Name;
|
||||
mNameMap.GetNameInfo(rRes.ResourceID, Directory, Name);
|
||||
mpNameMap->GetNameInfo(rRes.ResourceID, Directory, Name);
|
||||
CResourceEntry *pEntry = mpStore->RegisterResource(rRes.ResourceID, CResource::ResTypeForExtension(rRes.ResourceType), Directory, Name);
|
||||
|
||||
#if EXPORT_COOKED
|
||||
|
@ -29,7 +29,7 @@ class CGameExporter
|
||||
// Resources
|
||||
TWideStringList mPaks;
|
||||
std::map<CAssetID, bool> mAreaDuplicateMap;
|
||||
CAssetNameMap mNameMap;
|
||||
CAssetNameMap *mpNameMap;
|
||||
|
||||
struct SResourceInstance
|
||||
{
|
||||
|
@ -620,3 +620,73 @@ void CResourceStore::SetTransientLoadDir(const TString& rkDir)
|
||||
mTransientLoadDir.EnsureEndsWith('\\');
|
||||
Log::Write("Set resource directory: " + rkDir);
|
||||
}
|
||||
|
||||
void CResourceStore::ImportNamesFromPakContentsTxt(const TString& rkTxtPath, bool UnnamedOnly)
|
||||
{
|
||||
// Read file contents -first- then move assets -after-; this
|
||||
// 1. avoids anything fucking up if the contents file is badly formatted and we crash, and
|
||||
// 2. avoids extra redundant moves (since there are redundant entries in the file)
|
||||
std::map<CResourceEntry*, TString> PathMap;
|
||||
FILE *pContentsFile;
|
||||
fopen_s(&pContentsFile, *rkTxtPath, "r");
|
||||
|
||||
if (!pContentsFile)
|
||||
{
|
||||
Log::Error("Failed to open .contents.txt file: " + rkTxtPath);
|
||||
return;
|
||||
}
|
||||
|
||||
while (!feof(pContentsFile))
|
||||
{
|
||||
// Get new line, parse to extract the ID/path
|
||||
char LineBuffer[512];
|
||||
fgets(LineBuffer, 512, pContentsFile);
|
||||
|
||||
TString Line(LineBuffer);
|
||||
if (Line.IsEmpty()) break;
|
||||
|
||||
u32 IDStart = Line.IndexOfPhrase("0x") + 2;
|
||||
if (IDStart == 1) continue;
|
||||
|
||||
u32 IDEnd = Line.IndexOf(" \t", IDStart);
|
||||
u32 PathStart = IDEnd + 1;
|
||||
u32 PathEnd = Line.Size() - 4;
|
||||
|
||||
TString IDStr = Line.SubString(IDStart, IDEnd - IDStart);
|
||||
TString Path = Line.SubString(PathStart, PathEnd - PathStart);
|
||||
|
||||
CAssetID ID = CAssetID::FromString(IDStr);
|
||||
CResourceEntry *pEntry = FindEntry(ID);
|
||||
|
||||
// Only process this entry if the ID exists
|
||||
if (pEntry)
|
||||
{
|
||||
// Chop name to just after "x_rep"
|
||||
u32 RepStart = Path.IndexOfPhrase("_rep");
|
||||
|
||||
if (RepStart != -1)
|
||||
Path = Path.ChopFront(RepStart + 5);
|
||||
|
||||
// If the "x_rep" folder doesn't exist in this path for some reason, then just chop off the drive letter
|
||||
else
|
||||
Path = Path.ChopFront(3);
|
||||
|
||||
PathMap[pEntry] = Path;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(pContentsFile);
|
||||
|
||||
// Assign names
|
||||
for (auto Iter = PathMap.begin(); Iter != PathMap.end(); Iter++)
|
||||
{
|
||||
CResourceEntry *pEntry = Iter->first;
|
||||
if (UnnamedOnly && pEntry->IsNamed()) continue;
|
||||
|
||||
TWideString Path = Iter->second.ToUTF16();
|
||||
pEntry->Move(Path.GetFileDirectory(), Path.GetFileName(false));
|
||||
}
|
||||
|
||||
// Save
|
||||
ConditionalSaveStore();
|
||||
}
|
||||
|
@ -77,6 +77,8 @@ public:
|
||||
bool DeleteResourceEntry(CResourceEntry *pEntry);
|
||||
void SetTransientLoadDir(const TString& rkDir);
|
||||
|
||||
void ImportNamesFromPakContentsTxt(const TString& rkTxtPath, bool UnnamedOnly);
|
||||
|
||||
// Accessors
|
||||
inline CGameProject* Project() const { return mpProj; }
|
||||
inline EGame Game() const { return mGame; }
|
||||
|
@ -2,6 +2,8 @@
|
||||
#include "ui_CResourceBrowser.h"
|
||||
#include "Editor/ModelEditor/CModelEditorWindow.h"
|
||||
#include "Editor/CharacterEditor/CCharacterEditor.h"
|
||||
#include <QFileDialog>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
|
||||
CResourceBrowser::CResourceBrowser(QWidget *pParent)
|
||||
@ -17,7 +19,6 @@ CResourceBrowser::CResourceBrowser(QWidget *pParent)
|
||||
mpProxyModel = new CResourceProxyModel(this);
|
||||
mpProxyModel->setSourceModel(mpModel);
|
||||
mpUI->ResourceTableView->setModel(mpProxyModel);
|
||||
RefreshResources();
|
||||
|
||||
QHeaderView *pHeader = mpUI->ResourceTableView->horizontalHeader();
|
||||
pHeader->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||
@ -26,9 +27,16 @@ CResourceBrowser::CResourceBrowser(QWidget *pParent)
|
||||
|
||||
// Set up directory tree model
|
||||
mpDirectoryModel = new CVirtualDirectoryModel(this);
|
||||
mpDirectoryModel->SetRoot(gpResourceStore ? gpResourceStore->RootDirectory() : nullptr);
|
||||
mpUI->DirectoryTreeView->setModel(mpDirectoryModel);
|
||||
mpUI->DirectoryTreeView->expand(mpDirectoryModel->index(0, 0, QModelIndex()));
|
||||
|
||||
RefreshResources();
|
||||
|
||||
// Set up Import Names menu
|
||||
QMenu *pImportNamesMenu = new QMenu(this);
|
||||
mpUI->ImportNamesButton->setMenu(pImportNamesMenu);
|
||||
|
||||
QAction *pImportFromContentsTxtAction = new QAction("Import from Pak Contents List", this);
|
||||
pImportNamesMenu->addAction(pImportFromContentsTxtAction);
|
||||
|
||||
// Set up connections
|
||||
connect(mpUI->StoreComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(OnStoreChanged(int)));
|
||||
@ -36,6 +44,7 @@ CResourceBrowser::CResourceBrowser(QWidget *pParent)
|
||||
connect(mpUI->SortComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSortModeChanged(int)));
|
||||
connect(mpUI->DirectoryTreeView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(OnDirectorySelectionChanged(QModelIndex,QModelIndex)));
|
||||
connect(mpUI->ResourceTableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(OnDoubleClickResource(QModelIndex)));
|
||||
connect(pImportFromContentsTxtAction, SIGNAL(triggered()), this, SLOT(OnImportPakContentsTxt()));
|
||||
}
|
||||
|
||||
CResourceBrowser::~CResourceBrowser()
|
||||
@ -45,19 +54,21 @@ CResourceBrowser::~CResourceBrowser()
|
||||
|
||||
void CResourceBrowser::RefreshResources()
|
||||
{
|
||||
// Fill resource table
|
||||
mpModel->FillEntryList(mpStore);
|
||||
|
||||
// Fill directory tree
|
||||
mpDirectoryModel->SetRoot(mpStore ? mpStore->RootDirectory() : nullptr);
|
||||
QModelIndex RootIndex = mpDirectoryModel->index(0, 0, QModelIndex());
|
||||
mpUI->DirectoryTreeView->expand(RootIndex);
|
||||
mpUI->DirectoryTreeView->clearSelection();
|
||||
OnDirectorySelectionChanged(QModelIndex(), QModelIndex());
|
||||
}
|
||||
|
||||
void CResourceBrowser::OnStoreChanged(int Index)
|
||||
{
|
||||
mpStore = (Index == 0 ? gpResourceStore : gpEditorStore);
|
||||
RefreshResources();
|
||||
|
||||
mpDirectoryModel->SetRoot(mpStore ? mpStore->RootDirectory() : nullptr);
|
||||
QModelIndex RootIndex = mpDirectoryModel->index(0, 0, QModelIndex());
|
||||
mpUI->DirectoryTreeView->expand(RootIndex);
|
||||
mpUI->DirectoryTreeView->clearSelection();
|
||||
OnDirectorySelectionChanged(QModelIndex(), QModelIndex());
|
||||
}
|
||||
|
||||
void CResourceBrowser::OnSortModeChanged(int Index)
|
||||
@ -117,3 +128,14 @@ void CResourceBrowser::OnDoubleClickResource(QModelIndex Index)
|
||||
else
|
||||
QMessageBox::information(this, "Unsupported Resource", "The selected resource type is currently unsupported for editing.");
|
||||
}
|
||||
|
||||
void CResourceBrowser::OnImportPakContentsTxt()
|
||||
{
|
||||
QStringList PathList = QFileDialog::getOpenFileNames(this, "Open pak contents list", "", "*.pak.contents.txt");
|
||||
if (PathList.isEmpty()) return;
|
||||
|
||||
foreach(const QString& rkPath, PathList)
|
||||
mpStore->ImportNamesFromPakContentsTxt(TO_TSTRING(rkPath), false);
|
||||
|
||||
RefreshResources();
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ public slots:
|
||||
void OnSearchStringChanged();
|
||||
void OnDirectorySelectionChanged(const QModelIndex& rkNewIndex, const QModelIndex& rkPrevIndex);
|
||||
void OnDoubleClickResource(QModelIndex Index);
|
||||
void OnImportPakContentsTxt();
|
||||
};
|
||||
|
||||
#endif // CRESOURCEBROWSER_H
|
||||
|
@ -187,6 +187,20 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="ImportNamesButton">
|
||||
<property name="text">
|
||||
<string>Import Names</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="ExportNamesButton">
|
||||
<property name="text">
|
||||
<string>Export Names</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
@ -36,8 +36,15 @@ public:
|
||||
|
||||
if (Role == Qt::DisplayRole)
|
||||
{
|
||||
if (Col == 0) return TO_QSTRING(pEntry->Name());
|
||||
if (Col == 1) return TO_QSTRING(GetResourceTypeName(pEntry->ResourceType()));
|
||||
if (Col == 0)
|
||||
{
|
||||
return TO_QSTRING(pEntry->Name());
|
||||
}
|
||||
|
||||
if (Col == 1)
|
||||
{
|
||||
return TO_QSTRING(GetResourceTypeName(pEntry->ResourceType()));
|
||||
}
|
||||
|
||||
if (Col == 2)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user