From 992c76720df579930a884bb7af5e96559f1f8c25 Mon Sep 17 00:00:00 2001 From: Aruki Date: Fri, 25 Jan 2019 14:06:13 -0700 Subject: [PATCH] Support for loading MP2/MP3/DKCR tweaks --- src/Core/Resource/Factory/CTextureDecoder.cpp | 2 +- src/Core/Tweaks/CTweakData.h | 13 ++++ src/Core/Tweaks/CTweakLoader.cpp | 78 ++++++++++++++++++- src/Core/Tweaks/CTweakLoader.h | 2 +- src/Core/Tweaks/CTweakManager.cpp | 24 +++++- src/Core/Tweaks/CTweakManager.h | 5 +- src/Editor/CTweakEditor.cpp | 8 +- src/Editor/CTweakEditor.ui | 6 +- src/Editor/PropertyEdit/CPropertyView.cpp | 3 +- 9 files changed, 125 insertions(+), 16 deletions(-) diff --git a/src/Core/Resource/Factory/CTextureDecoder.cpp b/src/Core/Resource/Factory/CTextureDecoder.cpp index 8b874fc4..7e383b07 100644 --- a/src/Core/Resource/Factory/CTextureDecoder.cpp +++ b/src/Core/Resource/Factory/CTextureDecoder.cpp @@ -562,7 +562,7 @@ void CTextureDecoder::ReadPixelC8(IInputStream& rSrc, IOutputStream& rDst) ((Index >> 2) & 0x1) ? G = 0xFF : G = 0x0; ((Index >> 1) & 0x1) ? B = 0xFF : B = 0x0; ((Index >> 0) & 0x1) ? A = 0xFF : A = 0x0; - u32 RGBA = (R << 24) | (G << 16) | (B << 8) | (A); + uint32 RGBA = (R << 24) | (G << 16) | (B << 8) | (A); dst.WriteLong(RGBA);*/ mPaletteInput.Seek(Index * 2, SEEK_SET); diff --git a/src/Core/Tweaks/CTweakData.h b/src/Core/Tweaks/CTweakData.h index a0042775..2c01514d 100644 --- a/src/Core/Tweaks/CTweakData.h +++ b/src/Core/Tweaks/CTweakData.h @@ -26,6 +26,19 @@ public: pProperties->Construct(mTweakData.data()); } + TString TweakName() + { + if (Entry() != nullptr) + { + return Entry()->Name(); + } + else + { + IProperty* pNameProperty = mpTemplate->Properties()->ChildByID(0x7FDA1466); + return CStringRef(mTweakData.data(), pNameProperty); + } + } + inline CScriptTemplate* TweakTemplate() const { return mpTemplate; diff --git a/src/Core/Tweaks/CTweakLoader.cpp b/src/Core/Tweaks/CTweakLoader.cpp index 470671e2..9ace88e2 100644 --- a/src/Core/Tweaks/CTweakLoader.cpp +++ b/src/Core/Tweaks/CTweakLoader.cpp @@ -50,7 +50,81 @@ CTweakData* CTweakLoader::LoadCTWK(IInputStream& CTWK, CResourceEntry* pEntry) return pTweakData; } -void CTweakLoader::LoadNTWK(IInputStream& NTWK, std::vector& OutTweaks) +void CTweakLoader::LoadNTWK(IInputStream& NTWK, EGame Game, std::vector& OutTweaks) { - // Unimplemented + // Validate file. NTWK basically embeds a bunch of tweak objects using the script layers + // format, so it has the same version byte that script layers have. + uint Magic = NTWK.ReadLong(); + uint8 LayerVersion = NTWK.ReadByte(); + + if (Magic != FOURCC('NTWK')) + { + errorf("Unrecognized NTWK magic: 0x%08X", Magic); + return; + } + else if (LayerVersion != 1) + { + errorf("Unrecognized layer version in NTWK: %d", LayerVersion); + return; + } + + CGameTemplate* pGameTemplate = NGameList::GetGameTemplate( Game ); + ASSERT( pGameTemplate != nullptr ); + + // Start reading tweaks + uint NumTweaks = NTWK.ReadLong(); + + for (uint TweakIdx = 0; TweakIdx < NumTweaks; TweakIdx++) + { + // Find the correct template based on the tweak ID. + static const std::unordered_map skIdToTemplateName = + { + { FOURCC('TWAC'), "TweakAdvancedControls" }, + { FOURCC('TWAM'), "TweakAutoMapper" }, + { FOURCC('TWBL'), "TweakBall" }, + { FOURCC('TWC2'), "TweakPlayerControls" }, + { FOURCC('TWCB'), "TweakCameraBob" }, + { FOURCC('TWCC'), "TweakGamecubeControls" }, + { FOURCC('TWCT'), "TweakControls" }, + { FOURCC('TWEC'), "TweakExpertControls" }, + { FOURCC('TWGM'), "TweakGame" }, + { FOURCC('TWGT'), "TweakGraphicalTransitions" }, + { FOURCC('TWGU'), "TweakGui" }, + { FOURCC('TWGC'), "TweakGuiColors" }, + { FOURCC('TWP2'), "TweakPlayer" }, + { FOURCC('TWPC'), "TweakPlayerControls" }, + { FOURCC('TWPG'), "TweakPlayerGun" }, + { FOURCC('TWPL'), "TweakPlayer" }, + { FOURCC('TWPM'), "TweakPlayerGun" }, + { FOURCC('TWPA'), "TweakParticle" }, + { FOURCC('TWPR'), "TweakPlayerRes" }, + { FOURCC('TWRC'), "TweakRevolutionControls" }, + { FOURCC('TWSS'), "TweakSlideShow" }, + { FOURCC('TWTG'), "TweakTargeting" }, + }; + + uint TweakID = NTWK.ReadLong(); + uint16 TweakSize = NTWK.ReadShort(); + uint NextTweak = NTWK.Tell() + TweakSize; + + auto Find = skIdToTemplateName.find(TweakID); + + if (Find == skIdToTemplateName.end()) + { + errorf("Unrecognized tweak ID: %s (0x%08X)", *CFourCC(TweakID).ToString(), TweakID); + NTWK.GoTo(NextTweak); + continue; + } + + CScriptTemplate* pTweakTemplate = pGameTemplate->FindMiscTemplate( Find->second ); + ASSERT( pTweakTemplate != nullptr ); + + // Load tweak data + NTWK.Skip(0xC); + CTweakData* pTweakData = new CTweakData(pTweakTemplate); + CScriptLoader::LoadStructData( NTWK, pTweakData->TweakData() ); + OutTweaks.push_back(pTweakData); + + NTWK.GoTo(NextTweak); + } } diff --git a/src/Core/Tweaks/CTweakLoader.h b/src/Core/Tweaks/CTweakLoader.h index a73f213a..4467449f 100644 --- a/src/Core/Tweaks/CTweakLoader.h +++ b/src/Core/Tweaks/CTweakLoader.h @@ -12,7 +12,7 @@ class CTweakLoader public: /** Loader entry point */ static CTweakData* LoadCTWK(IInputStream& CTWK, CResourceEntry* pEntry); - static void LoadNTWK(IInputStream& NTWK, std::vector& OutTweaks); + static void LoadNTWK(IInputStream& NTWK, EGame Game, std::vector& OutTweaks); }; #endif // CTWEAKLOADER_H diff --git a/src/Core/Tweaks/CTweakManager.cpp b/src/Core/Tweaks/CTweakManager.cpp index 897b1b85..b91b57f7 100644 --- a/src/Core/Tweaks/CTweakManager.cpp +++ b/src/Core/Tweaks/CTweakManager.cpp @@ -1,6 +1,7 @@ #include "CTweakManager.h" #include "Core/GameProject/CGameProject.h" #include "Core/GameProject/CResourceIterator.h" +#include "Core/Tweaks/CTweakLoader.h" CTweakManager::CTweakManager(CGameProject* pInProject) : mpProject(pInProject) @@ -9,19 +10,40 @@ CTweakManager::CTweakManager(CGameProject* pInProject) void CTweakManager::LoadTweaks() { + ASSERT( mTweakObjects.empty() ); + // MP1 - Load all tweak assets into memory if (mpProject->Game() <= EGame::Prime) { for (TResourceIterator It(mpProject->ResourceStore()); It; ++It) { CTweakData* pTweaks = (CTweakData*) It->Load(); + pTweaks->Lock(); mTweakObjects.push_back(pTweaks); } } - // MP2+ - Not supported, but tweaks are stored in Standard.ntwk + // MP2+ - Load tweaks from Standard.ntwk else { + TString FilePath = mpProject->DiscFilesystemRoot(false) + "Standard.ntwk"; + CFileInStream StandardNTWK(FilePath, EEndian::BigEndian); + CTweakLoader::LoadNTWK(StandardNTWK, mpProject->Game(), mTweakObjects); + } +} + +CTweakManager::~CTweakManager() +{ + for (CTweakData* pTweakData : mTweakObjects) + { + if (pTweakData->Entry() != nullptr) + { + pTweakData->Release(); + } + else + { + delete pTweakData; + } } } diff --git a/src/Core/Tweaks/CTweakManager.h b/src/Core/Tweaks/CTweakManager.h index c7e7ea94..2928c6dc 100644 --- a/src/Core/Tweaks/CTweakManager.h +++ b/src/Core/Tweaks/CTweakManager.h @@ -10,15 +10,16 @@ class CTweakManager CGameProject* mpProject; /** All tweak resources in the current game */ - std::vector< TResPtr > mTweakObjects; + std::vector< CTweakData* > mTweakObjects; public: CTweakManager(CGameProject* pInProject); + ~CTweakManager(); void LoadTweaks(); void SaveTweaks(); // Accessors - inline const std::vector< TResPtr >& TweakObjects() const + inline const std::vector& TweakObjects() const { return mTweakObjects; } diff --git a/src/Editor/CTweakEditor.cpp b/src/Editor/CTweakEditor.cpp index 74c6c335..4eea3d20 100644 --- a/src/Editor/CTweakEditor.cpp +++ b/src/Editor/CTweakEditor.cpp @@ -144,9 +144,9 @@ void CTweakEditor::OnProjectChanged(CGameProject* pNewProject) // Create tweak list if (pNewProject != nullptr) { - for (TResPtr pTweakData : pNewProject->TweakManager()->TweakObjects()) + for (CTweakData* pTweakData : pNewProject->TweakManager()->TweakObjects()) { - mTweakAssets << pTweakData.RawPointer(); + mTweakAssets << pTweakData; } } @@ -154,12 +154,12 @@ void CTweakEditor::OnProjectChanged(CGameProject* pNewProject) if (!mTweakAssets.isEmpty()) { qSort(mTweakAssets.begin(), mTweakAssets.end(), [](CTweakData* pLeft, CTweakData* pRight) -> bool { - return pLeft->Entry()->Name().ToUpper() < pRight->Entry()->Name().ToUpper(); + return pLeft->TweakName().ToUpper() < pRight->TweakName().ToUpper(); }); foreach (CTweakData* pTweakData, mTweakAssets) { - QString TweakName = TO_QSTRING( pTweakData->Entry()->Name() ); + QString TweakName = TO_QSTRING( pTweakData->TweakName() ); mpUI->TweakTabs->addTab(TweakName); } diff --git a/src/Editor/CTweakEditor.ui b/src/Editor/CTweakEditor.ui index 4c4d8550..efd4eda0 100644 --- a/src/Editor/CTweakEditor.ui +++ b/src/Editor/CTweakEditor.ui @@ -11,7 +11,7 @@ - MainWindow + Tweaks Editor @@ -93,10 +93,10 @@ :/icons/SaveAndRepack_32px.png:/icons/SaveAndRepack_32px.png - Save and Repack + Save and Cook - Save and Repack + Save and Cook diff --git a/src/Editor/PropertyEdit/CPropertyView.cpp b/src/Editor/PropertyEdit/CPropertyView.cpp index 1e7bfc04..0b1e3cd9 100644 --- a/src/Editor/PropertyEdit/CPropertyView.cpp +++ b/src/Editor/PropertyEdit/CPropertyView.cpp @@ -183,8 +183,7 @@ void CPropertyView::SetPersistentEditors(const QModelIndex& rkParent) if (pProp->Type() == EPropertyType::AnimationSet) { - EGame Game = mpObject->Area()->Game(); - Type = mpDelegate->DetermineCharacterPropType(Game, ChildIndex); + Type = mpDelegate->DetermineCharacterPropType(pProp->Game(), ChildIndex); IsAnimSet = true; }