Tweak loader for MP1

This commit is contained in:
Aruki
2018-12-27 20:16:39 -07:00
parent d6340dced9
commit 7588200c26
37 changed files with 3780 additions and 65 deletions

View File

@@ -248,7 +248,10 @@ HEADERS += \
Resource/Script/CGameTemplate.h \
Resource/Script/NPropertyMap.h \
Resource/Script/NGameList.h \
Resource/StringTable/ELanguage.h
Resource/StringTable/ELanguage.h \
Tweaks/CTweakManager.h \
Tweaks/CTweakData.h \
Tweaks/CTweakLoader.h
# Source Files
SOURCES += \
@@ -360,7 +363,9 @@ SOURCES += \
Resource/Script/CGameTemplate.cpp \
Resource/Script/NPropertyMap.cpp \
Resource/Script/NGameList.cpp \
Resource/StringTable/CStringTable.cpp
Resource/StringTable/CStringTable.cpp \
Tweaks/CTweakManager.cpp \
Tweaks/CTweakLoader.cpp
# Codegen
CODEGEN_DIR = $$EXTERNALS_DIR/CodeGen

View File

@@ -10,16 +10,12 @@ CGameProject::~CGameProject()
{
ASSERT(!mpResourceStore->IsCacheDirty());
if (gpResourceStore == mpResourceStore)
if (gpResourceStore == mpResourceStore.get())
gpResourceStore = nullptr;
}
for (uint32 iPkg = 0; iPkg < mPackages.size(); iPkg++)
delete mPackages[iPkg];
delete mpAudioManager;
delete mpGameInfo;
delete mpResourceStore;
}
bool CGameProject::Save()
@@ -200,7 +196,7 @@ CGameProject* CGameProject::CreateProjectForExport(
pProj->mProjectRoot = rkProjRootDir;
pProj->mProjectRoot.Replace("\\", "/");
pProj->mpResourceStore = new CResourceStore(pProj);
pProj->mpResourceStore = std::make_unique<CResourceStore>(pProj);
pProj->mpGameInfo->LoadGameInfo(Game);
return pProj;
}
@@ -234,7 +230,7 @@ CGameProject* CGameProject::LoadProject(const TString& rkProjPath, IProgressNoti
{
// Load resource database
pProgress->Report("Loading resource database");
pProj->mpResourceStore = new CResourceStore(pProj);
pProj->mpResourceStore = std::make_unique<CResourceStore>(pProj);
LoadSuccess = pProj->mpResourceStore->LoadDatabaseCache();
// Validate resource database
@@ -268,5 +264,6 @@ CGameProject* CGameProject::LoadProject(const TString& rkProjPath, IProgressNoti
pProj->mProjFileLock.Lock(ProjPath);
pProj->mpGameInfo->LoadGameInfo(pProj->mGame);
pProj->mpAudioManager->LoadAssets();
pProj->mpTweakManager->LoadTweaks();
return pProj;
}

View File

@@ -7,6 +7,7 @@
#include "Core/CAudioManager.h"
#include "Core/IProgressNotifier.h"
#include "Core/Resource/Script/CGameTemplate.h"
#include "Core/Tweaks/CTweakManager.h"
#include <Common/CAssetID.h>
#include <Common/EGame.h>
#include <Common/FileUtil.h>
@@ -34,9 +35,10 @@ class CGameProject
TString mProjectRoot;
std::vector<CPackage*> mPackages;
CResourceStore *mpResourceStore;
CGameInfo *mpGameInfo;
CAudioManager *mpAudioManager;
std::unique_ptr<CResourceStore> mpResourceStore;
std::unique_ptr<CGameInfo> mpGameInfo;
std::unique_ptr<CAudioManager> mpAudioManager;
std::unique_ptr<CTweakManager> mpTweakManager;
// Keep file handle open for the .prj file to prevent users from opening the same project
// in multiple instances of PWE
@@ -51,14 +53,14 @@ class CGameProject
, mBuildVersion(0.f)
, mpResourceStore(nullptr)
{
mpGameInfo = new CGameInfo();
mpAudioManager = new CAudioManager(this);
mpGameInfo = std::make_unique<CGameInfo>();
mpAudioManager = std::make_unique<CAudioManager>(this);
mpTweakManager = std::make_unique<CTweakManager>(this);
}
public:
~CGameProject();
bool Save();
bool Serialize(IArchive& rArc);
bool BuildISO(const TString& rkIsoPath, IProgressNotifier *pProgress);
@@ -95,9 +97,10 @@ public:
inline uint32 NumPackages() const { return mPackages.size(); }
inline CPackage* PackageByIndex(uint32 Index) const { return mPackages[Index]; }
inline void AddPackage(CPackage *pPackage) { mPackages.push_back(pPackage); }
inline CResourceStore* ResourceStore() const { return mpResourceStore; }
inline CGameInfo* GameInfo() const { return mpGameInfo; }
inline CAudioManager* AudioManager() const { return mpAudioManager; }
inline CResourceStore* ResourceStore() const { return mpResourceStore.get(); }
inline CGameInfo* GameInfo() const { return mpGameInfo.get(); }
inline CAudioManager* AudioManager() const { return mpAudioManager.get(); }
inline CTweakManager* TweakManager() const { return mpTweakManager.get(); }
inline EGame Game() const { return mGame; }
inline ERegion Region() const { return mRegion; }
inline TString GameID() const { return mGameID; }

View File

@@ -388,7 +388,7 @@ void CResTypeInfo::CResTypeInfoFactory::InitTypes()
pType->mCanHaveDependencies = false;
}
{
CResTypeInfo *pType = new CResTypeInfo(EResourceType::Tweak, "Tweak Data", "ctwk");
CResTypeInfo *pType = new CResTypeInfo(EResourceType::Tweaks, "Tweak Data", "ctwk");
AddExtension(pType, "CTWK", EGame::PrimeDemo, EGame::Prime);
pType->mCanHaveDependencies = false;
}

View File

@@ -66,7 +66,7 @@ enum class EResourceType
StringList,
StringTable,
Texture,
Tweak,
Tweaks,
UserEvaluatorData,
Video,
World,

View File

@@ -22,6 +22,8 @@
#include "CUnsupportedParticleLoader.h"
#include "CWorldLoader.h"
#include "Core/Tweaks/CTweakLoader.h"
#include "Core/Resource/Resources.h"
// Static helper class to allow spawning resources based on an EResType
@@ -97,6 +99,7 @@ public:
case EResourceType::StringList: pRes = CAudioGroupLoader::LoadSTLC(rInput, pEntry); break;
case EResourceType::StringTable: pRes = CStringLoader::LoadSTRG(rInput, pEntry); break;
case EResourceType::Texture: pRes = CTextureDecoder::LoadTXTR(rInput, pEntry); break;
case EResourceType::Tweaks: pRes = CTweakLoader::LoadCTWK(rInput, pEntry); break;
case EResourceType::World: pRes = CWorldLoader::LoadMLVL(rInput, pEntry); break;
case EResourceType::StateMachine:

View File

@@ -15,13 +15,13 @@
CScriptLoader::CScriptLoader()
: mpObj(nullptr)
, mpArrayItemData(nullptr)
, mpCurrentData(nullptr)
{
}
void CScriptLoader::ReadProperty(IProperty *pProp, uint32 Size, IInputStream& rSCLY)
{
void* pData = (mpArrayItemData ? mpArrayItemData : mpObj->mPropertyData.data());
void* pData = (mpCurrentData ? mpCurrentData : mpObj->mPropertyData.data());
switch (pProp->Type())
{
@@ -237,7 +237,7 @@ void CScriptLoader::ReadProperty(IProperty *pProp, uint32 Size, IInputStream& rS
int Count = rSCLY.ReadLong();
pArray->Resize(pData, Count);
void* pOldArrayItemData = mpArrayItemData;
void* pOldData = mpCurrentData;
// Make sure the array archetype is atomic... non-atomic array archetypes is not supported
// because arrays can only have one possible archetype so having property IDs here wouldn't make sense
@@ -257,11 +257,11 @@ void CScriptLoader::ReadProperty(IProperty *pProp, uint32 Size, IInputStream& rS
* migrated to Sequence properties eventually, so there isn't really any good reason to spend a lot of effort refactoring
* things to make this cleaner
*/
mpArrayItemData = pArray->ItemPointer(pData, ElementIdx);
mpCurrentData = pArray->ItemPointer(pData, ElementIdx);
ReadProperty(pArray->ItemArchetype(), 0, rSCLY);
}
mpArrayItemData = pOldArrayItemData;
mpCurrentData = pOldData;
break;
}
@@ -505,3 +505,20 @@ CScriptObject* CScriptLoader::LoadInstance(IInputStream& rSCLY, CGameArea *pArea
else
return Loader.LoadObjectMP2(rSCLY);
}
void CScriptLoader::LoadStructData(IInputStream& rInput, CStructRef InStruct)
{
if (!rInput.IsValid()) return;
CScriptLoader Loader;
Loader.mVersion = InStruct.Property()->Game();
Loader.mpGameTemplate = NGameList::GetGameTemplate(Loader.mVersion);
Loader.mpArea = nullptr;
Loader.mpLayer = nullptr;
Loader.mpCurrentData = InStruct.DataPointer();
if (Loader.mVersion <= EGame::Prime)
Loader.LoadStructMP1(rInput, InStruct.Property());
else
Loader.LoadStructMP2(rInput, InStruct.Property());
}

View File

@@ -15,8 +15,8 @@ class CScriptLoader
CGameArea* mpArea;
CGameTemplate *mpGameTemplate;
// Current array item pointer
void* mpArrayItemData;
// Current data pointer
void* mpCurrentData;
CScriptLoader();
void ReadProperty(IProperty* pProp, uint32 Size, IInputStream& rSCLY);
@@ -32,6 +32,7 @@ class CScriptLoader
public:
static CScriptLayer* LoadLayer(IInputStream& rSCLY, CGameArea *pArea, EGame Version);
static CScriptObject* LoadInstance(IInputStream& rSCLY, CGameArea *pArea, CScriptLayer *pLayer, EGame Version, bool ForceReturnsFormat);
static void LoadStructData(IInputStream& rInput, CStructRef InStruct);
};
#endif // CSCRIPTLOADER_H

View File

@@ -13,6 +13,7 @@ void CGameTemplate::Serialize(IArchive& Arc)
{
Arc << SerialParameter("ScriptObjects", mScriptTemplates)
<< SerialParameter("PropertyArchetypes", mPropertyTemplates)
<< SerialParameter("MiscTemplates", mMiscTemplates)
<< SerialParameter("States", mStates)
<< SerialParameter("Messages", mMessages);
}
@@ -51,6 +52,13 @@ void CGameTemplate::Load(const TString& kFilePath)
Internal_LoadPropertyTemplate(Iter->second);
}
}
for (auto Iter = mMiscTemplates.begin(); Iter != mMiscTemplates.end(); Iter++)
{
SScriptTemplatePath& MiscPath = Iter->second;
TString AbsPath = gkGameRoot + MiscPath.Path;
MiscPath.pTemplate = std::make_shared<CScriptTemplate>(this, -1, AbsPath);
}
}
void CGameTemplate::Save()
@@ -118,6 +126,16 @@ void CGameTemplate::SaveGameTemplates(bool ForceAll /*= false*/)
}
}
}
for (auto Iter = mMiscTemplates.begin(); Iter != mMiscTemplates.end(); Iter++)
{
SScriptTemplatePath& Path = Iter->second;
if( Path.pTemplate )
{
Path.pTemplate->Save(ForceAll);
}
}
}
uint32 CGameTemplate::GameVersion(TString VersionName)
@@ -286,6 +304,21 @@ bool CGameTemplate::RenamePropertyArchetype(const TString& kTypeName, const TStr
return false;
}
CScriptTemplate* CGameTemplate::FindMiscTemplate(const TString& kTemplateName)
{
auto Iter = mMiscTemplates.find(kTemplateName);
if (Iter == mMiscTemplates.end())
{
return nullptr;
}
else
{
SScriptTemplatePath& Path = Iter->second;
return Path.pTemplate.get();
}
}
TString CGameTemplate::GetGameDirectory() const
{
return mSourceFile.GetFileDirectory();

View File

@@ -35,54 +35,23 @@ struct SObjId
}
};
/** Struct holding a reference to a script object template */
struct SScriptTemplatePath
/** Struct holding a reference to a template */
template<typename TemplateT>
struct TTemplatePath
{
/** File path to the template file, relative to the game directory */
TString Path;
/** Template in memory */
std::shared_ptr<CScriptTemplate> pTemplate;
std::shared_ptr<TemplateT> pTemplate;
/** Constructor */
SScriptTemplatePath()
TTemplatePath()
{}
SScriptTemplatePath(const TString& kInPath, CScriptTemplate* pInTemplate)
TTemplatePath(const TString& kInPath, TemplateT* pInTemplate)
: Path(kInPath)
, pTemplate( std::shared_ptr<CScriptTemplate>(pInTemplate) )
{}
/** Serializer */
void Serialize(IArchive& Arc)
{
if (Arc.FileVersion() == 0)
{
Arc << SerialParameter("Path", Path, SH_Attribute);
}
else
{
Arc.SerializePrimitive(Path, 0);
}
}
};
/** Struct holding a reference to a property template */
struct SPropertyTemplatePath
{
/** File path to the template file, relative to the game directory */
TString Path;
/** Template in memory */
std::shared_ptr<IProperty> pTemplate;
/** Constructor */
SPropertyTemplatePath()
{}
SPropertyTemplatePath(const TString& kInPath, IProperty* pInTemplate)
: Path(kInPath)
, pTemplate( std::shared_ptr<IProperty>(pInTemplate) )
, pTemplate( std::shared_ptr<TemplateT>(pInTemplate) )
{}
/** Serializer */
@@ -92,6 +61,9 @@ struct SPropertyTemplatePath
}
};
typedef TTemplatePath<CScriptTemplate> SScriptTemplatePath;
typedef TTemplatePath<IProperty> SPropertyTemplatePath;
/** CGameTemplate - Per-game template data */
class CGameTemplate
{
@@ -103,6 +75,7 @@ class CGameTemplate
/** Template arrays */
std::map<SObjId, SScriptTemplatePath> mScriptTemplates;
std::map<TString, SPropertyTemplatePath> mPropertyTemplates;
std::map<TString, SScriptTemplatePath> mMiscTemplates;
std::map<SObjId, TString> mStates;
std::map<SObjId, TString> mMessages;
@@ -130,6 +103,7 @@ public:
IProperty* FindPropertyArchetype(const TString& kTypeName);
TString GetPropertyArchetypeFilePath(const TString& kTypeName);
bool RenamePropertyArchetype(const TString& kTypeName, const TString& kNewTypeName);
CScriptTemplate* FindMiscTemplate(const TString& kTemplateName);
TString GetGameDirectory() const;
// Inline Accessors

View File

@@ -91,6 +91,7 @@ void IProperty::Serialize(IArchive& rArc)
// The archetype must exist, or else the template file is malformed.
ASSERT(pArchetype != nullptr);
ASSERT(pArchetype->Type() == Type());
InitFromArchetype(pArchetype);
}

View File

@@ -0,0 +1,40 @@
#ifndef CTWEAKDATA_H
#define CTWEAKDATA_H
#include "Core/Resource/CResource.h"
#include "Core/Resource/Script/CScriptTemplate.h"
#include "Core/Resource/Script/Property/TPropertyRef.h"
/** Tweak data assets for MP1 */
class CTweakData : public CResource
{
DECLARE_RESOURCE_TYPE(Tweaks)
/** Script template specifying tweak data layout */
CScriptTemplate* mpTemplate;
/** Tweak data */
std::vector<uint8> mTweakData;
public:
CTweakData(CScriptTemplate* pTemplate, CResourceEntry* pEntry = 0)
: mpTemplate(pTemplate)
, CResource(pEntry)
{
CStructProperty* pProperties = pTemplate->Properties();
mTweakData.resize(pProperties->DataSize());
pProperties->Construct(mTweakData.data());
}
inline CScriptTemplate* TweakTemplate() const
{
return mpTemplate;
}
inline CStructRef TweakData() const
{
return CStructRef((void*) mTweakData.data(), mpTemplate->Properties());
}
};
#endif // CTWEAKDATA_H

View File

@@ -0,0 +1,54 @@
#include "CTweakLoader.h"
#include "Core/Resource/Factory/CScriptLoader.h"
#include "Core/Resource/Script/NGameList.h"
CTweakData* CTweakLoader::LoadCTWK(IInputStream& CTWK, CResourceEntry* pEntry)
{
// Find the correct template based on the asset ID.
static const std::unordered_map<uint, const char*> skIdToTemplateName =
{
{ 0x1D180D7C, "TweakParticle" },
{ 0x264A4972, "TweakPlayer" },
{ 0x33B3323A, "TweakGunRes" },
{ 0x39AD28D3, "TweakCameraBob" },
{ 0x3FAEC012, "TweakPlayerControls", },
{ 0x5ED56350, "TweakBall", },
{ 0x5F24EFF8, "TweakSlideShow", },
{ 0x6907A32D, "TweakPlayerGun", },
{ 0x85CA11E9, "TweakPlayerRes", },
{ 0x94C76ECD, "TweakTargeting", },
{ 0x953A7C63, "TweakGame", },
{ 0xC9954E56, "TweakGuiColors", },
{ 0xE66A4F86, "TweakAutoMapper", },
{ 0xED2E48A9, "TweakGui", },
{ 0xF1ED8FD7, "TweakPlayerControls", }
};
auto Find = skIdToTemplateName.find( pEntry->ID().ToLong() );
ASSERT( Find != skIdToTemplateName.end() );
const char* pkTemplateName = Find->second;
// Fetch template
CGameTemplate* pGameTemplate = NGameList::GetGameTemplate( pEntry->Game() );
ASSERT( pGameTemplate != nullptr );
CScriptTemplate* pTweakTemplate = pGameTemplate->FindMiscTemplate(pkTemplateName);
ASSERT( pTweakTemplate != nullptr );
// Load tweak data
CTweakData* pTweakData = new CTweakData(pTweakTemplate, pEntry);
CScriptLoader::LoadStructData( CTWK, pTweakData->TweakData() );
// Verify
if (!CTWK.EoF() && CTWK.PeekShort() != -1)
{
errorf("%s: unread property data, tweak template may be malformed (%d bytes left)", *CTWK.GetSourceString(), CTWK.Size() - CTWK.Tell());
}
return pTweakData;
}
void CTweakLoader::LoadNTWK(IInputStream& NTWK, std::vector<CTweakData*>& OutTweaks)
{
// Unimplemented
}

View File

@@ -0,0 +1,18 @@
#ifndef CTWEAKLOADER_H
#define CTWEAKLOADER_H
#include "CTweakData.h"
/** Class responsible for loading tweak data */
class CTweakLoader
{
/** Private constructor */
CTweakLoader() {}
public:
/** Loader entry point */
static CTweakData* LoadCTWK(IInputStream& CTWK, CResourceEntry* pEntry);
static void LoadNTWK(IInputStream& NTWK, std::vector<CTweakData*>& OutTweaks);
};
#endif // CTWEAKLOADER_H

View File

@@ -0,0 +1,33 @@
#include "CTweakManager.h"
#include "Core/GameProject/CGameProject.h"
#include "Core/GameProject/CResourceIterator.h"
CTweakManager::CTweakManager(CGameProject* pInProject)
: mpProject(pInProject)
{
}
void CTweakManager::LoadTweaks()
{
// MP1 - Load all tweak assets into memory
if (mpProject->Game() <= EGame::Prime)
{
for (TResourceIterator<EResourceType::Tweaks> It(mpProject->ResourceStore()); It; ++It)
{
CTweakData* pTweaks = (CTweakData*) It->Load();
mTweakObjects.push_back(pTweaks);
}
}
// MP2+ - Not supported, but tweaks are stored in Standard.ntwk
else
{
}
}
void CTweakManager::SaveTweaks()
{
// In MP1, to save an individual tweak asset, just call Tweak->Entry()->Save()
// In MP2+, call this function.
//@todo
}

View File

@@ -0,0 +1,27 @@
#ifndef CTWEAKMANAGER_H
#define CTWEAKMANAGER_H
#include "CTweakData.h"
/** Class responsible for managing game tweak data, including saving/loading and providing access */
class CTweakManager
{
/** Project */
CGameProject* mpProject;
/** All tweak resources in the current game */
std::vector< TResPtr<CTweakData> > mTweakObjects;
public:
CTweakManager(CGameProject* pInProject);
void LoadTweaks();
void SaveTweaks();
// Accessors
inline const std::vector< TResPtr<CTweakData> >& TweakObjects() const
{
return mTweakObjects;
}
};
#endif // CTWEAKMANAGER_H