diff --git a/src/Core/Resource/Cooker/CAreaCooker.cpp b/src/Core/Resource/Cooker/CAreaCooker.cpp index c617a375..586f7f7d 100644 --- a/src/Core/Resource/Cooker/CAreaCooker.cpp +++ b/src/Core/Resource/Cooker/CAreaCooker.cpp @@ -185,31 +185,43 @@ void CAreaCooker::WriteAreaData(IOutputStream& rOut) // ************ SCLY ************ void CAreaCooker::WritePrimeSCLY(IOutputStream& rOut) { - rOut.WriteString("SCLY", 4); + // This function covers both Prime 1 and the Echoes demo. + // The Echoes demo has a similar SCLY format but with minor layout differences and with SCGN. + rOut.WriteFourCC( FOURCC('SCLY') ); mVersion <= ePrime ? rOut.WriteLong(1) : rOut.WriteByte(1); u32 NumLayers = mpArea->mScriptLayers.size(); rOut.WriteLong(NumLayers); u32 LayerSizesStart = rOut.Tell(); - for (u32 iLyr = 0; iLyr < NumLayers; iLyr++) + for (u32 LayerIdx = 0; LayerIdx < NumLayers; LayerIdx++) rOut.WriteLong(0); // SCLY + CScriptCooker ScriptCooker(mVersion, true); std::vector LayerSizes(NumLayers); - for (u32 iLyr = 0; iLyr < NumLayers; iLyr++) + for (u32 LayerIdx = 0; LayerIdx < NumLayers; LayerIdx++) { u32 LayerStart = rOut.Tell(); - CScriptCooker::WriteLayer(mVersion, mpArea->mScriptLayers[iLyr], rOut); - LayerSizes[iLyr] = rOut.Tell() - LayerStart; + ScriptCooker.WriteLayer(rOut, mpArea->mScriptLayers[LayerIdx]); + + // Pad the layer to 32 bytes + u32 LayerSize = rOut.Tell() - LayerStart; + u32 PaddedSize = (LayerSize + 31) & ~31; + u32 NumPadBytes = PaddedSize - LayerSize; + + for (u32 Pad = 0; Pad < NumPadBytes; Pad++) + rOut.WriteByte(0); + + LayerSizes[LayerIdx] = PaddedSize; } u32 LayersEnd = rOut.Tell(); rOut.Seek(LayerSizesStart, SEEK_SET); - for (u32 iLyr = 0; iLyr < NumLayers; iLyr++) - rOut.WriteLong(LayerSizes[iLyr]); + for (u32 LayerIdx = 0; LayerIdx < NumLayers; LayerIdx++) + rOut.WriteLong(LayerSizes[LayerIdx]); rOut.Seek(LayersEnd, SEEK_SET); FinishSection(false); @@ -217,31 +229,31 @@ void CAreaCooker::WritePrimeSCLY(IOutputStream& rOut) // SCGN if (mVersion == eEchoesDemo) { - // IMPORTANT TODO - REGENERATE SCGN LAYER - /*rOut.WriteString("SCGN", 4); + rOut.WriteFourCC( FOURCC('SCGN') ); rOut.WriteByte(1); - CScriptCooker::WriteLayer(mVersion, mpArea->mpGeneratorLayer, rOut); - FinishSection(false);*/ + ScriptCooker.WriteGeneratedLayer(rOut); + FinishSection(false); } } void CAreaCooker::WriteEchoesSCLY(IOutputStream& rOut) { // SCLY - for (u32 iLyr = 0; iLyr < mpArea->mScriptLayers.size(); iLyr++) + CScriptCooker ScriptCooker(mVersion); + + for (u32 LayerIdx = 0; LayerIdx < mpArea->mScriptLayers.size(); LayerIdx++) { - rOut.WriteString("SCLY", 4); - rOut.WriteByte(0x1); - rOut.WriteLong(iLyr); - CScriptCooker::WriteLayer(mVersion, mpArea->mScriptLayers[iLyr], rOut); + rOut.WriteFourCC( FOURCC('SCLY') ); + rOut.WriteByte(1); + rOut.WriteLong(LayerIdx); + ScriptCooker.WriteLayer(rOut, mpArea->mScriptLayers[LayerIdx]); FinishSection(true); } // SCGN - // IMPORTANT TODO - REGENERATE SCGN - rOut.WriteString("SCGN", 4); - rOut.WriteByte(0x1); - //CScriptCooker::WriteLayer(mVersion, mpArea->mpGeneratorLayer, rOut); + rOut.WriteFourCC( FOURCC('SCGN') ); + rOut.WriteByte(1); + ScriptCooker.WriteGeneratedLayer(rOut); FinishSection(true); } diff --git a/src/Core/Resource/Cooker/CScriptCooker.cpp b/src/Core/Resource/Cooker/CScriptCooker.cpp index 52ee28bb..8b6d66cb 100644 --- a/src/Core/Resource/Cooker/CScriptCooker.cpp +++ b/src/Core/Resource/Cooker/CScriptCooker.cpp @@ -1,16 +1,16 @@ #include "CScriptCooker.h" #include "Core/Resource/Script/CLink.h" -void CScriptCooker::WriteProperty(IProperty *pProp, bool InSingleStruct) +void CScriptCooker::WriteProperty(IOutputStream& rOut,IProperty *pProp, bool InSingleStruct) { u32 SizeOffset = 0, PropStart = 0; - if (mVersion >= eEchoesDemo && !InSingleStruct) + if (mGame >= eEchoesDemo && !InSingleStruct) { - mpSCLY->WriteLong(pProp->ID()); - SizeOffset = mpSCLY->Tell(); - mpSCLY->WriteShort(0x0); - PropStart = mpSCLY->Tell(); + rOut.WriteLong(pProp->ID()); + SizeOffset = rOut.Tell(); + rOut.WriteShort(0x0); + PropStart = rOut.Tell(); } switch (pProp->Type()) @@ -19,91 +19,91 @@ void CScriptCooker::WriteProperty(IProperty *pProp, bool InSingleStruct) case eBoolProperty: { TBoolProperty *pBoolCast = static_cast(pProp); - mpSCLY->WriteByte(pBoolCast->Get() ? 1 : 0); + rOut.WriteBool(pBoolCast->Get()); break; } case eByteProperty: { TByteProperty *pByteCast = static_cast(pProp); - mpSCLY->WriteByte(pByteCast->Get()); + rOut.WriteByte(pByteCast->Get()); break; } case eShortProperty: { TShortProperty *pShortCast = static_cast(pProp); - mpSCLY->WriteShort(pShortCast->Get()); + rOut.WriteShort(pShortCast->Get()); break; } case eLongProperty: { TLongProperty *pLongCast = static_cast(pProp); - mpSCLY->WriteLong(pLongCast->Get()); + rOut.WriteLong(pLongCast->Get()); break; } case eEnumProperty: { TEnumProperty *pEnumCast = static_cast(pProp); - mpSCLY->WriteLong(pEnumCast->Get()); + rOut.WriteLong(pEnumCast->Get()); break; } case eBitfieldProperty: { TBitfieldProperty *pBitfieldCast = static_cast(pProp); - mpSCLY->WriteLong(pBitfieldCast->Get()); + rOut.WriteLong(pBitfieldCast->Get()); break; } case eFloatProperty: { TFloatProperty *pFloatCast = static_cast(pProp); - mpSCLY->WriteFloat(pFloatCast->Get()); + rOut.WriteFloat(pFloatCast->Get()); break; } case eStringProperty: { TStringProperty *pStringCast = static_cast(pProp); - mpSCLY->WriteString(pStringCast->Get()); + rOut.WriteString(pStringCast->Get()); break; } case eVector3Property: { TVector3Property *pVectorCast = static_cast(pProp); - pVectorCast->Get().Write(*mpSCLY); + pVectorCast->Get().Write(rOut); break; } case eColorProperty: { TColorProperty *pColorCast = static_cast(pProp); - pColorCast->Get().Write(*mpSCLY, false); + pColorCast->Get().Write(rOut, false); break; } case eSoundProperty: { TSoundProperty *pSoundCast = static_cast(pProp); - mpSCLY->WriteLong(pSoundCast->Get()); + rOut.WriteLong(pSoundCast->Get()); break; } case eAssetProperty: { TAssetProperty *pAssetCast = static_cast(pProp); - pAssetCast->Get().Write(*mpSCLY); + pAssetCast->Get().Write(rOut); break; } case eCharacterProperty: { TCharacterProperty *pCharCast = static_cast(pProp); - pCharCast->Get().Write(*mpSCLY); + pCharCast->Get().Write(rOut); break; } @@ -111,25 +111,25 @@ void CScriptCooker::WriteProperty(IProperty *pProp, bool InSingleStruct) { TMayaSplineProperty *pSplineCast = static_cast(pProp); std::vector Buffer = pSplineCast->Get(); - if (!Buffer.empty()) mpSCLY->WriteBytes(Buffer.data(), Buffer.size()); + if (!Buffer.empty()) rOut.WriteBytes(Buffer.data(), Buffer.size()); else { - if (mVersion < eReturns) + if (mGame < eReturns) { - mpSCLY->WriteShort(0); - mpSCLY->WriteLong(0); - mpSCLY->WriteByte(1); - mpSCLY->WriteFloat(0); - mpSCLY->WriteFloat(1); + rOut.WriteShort(0); + rOut.WriteLong(0); + rOut.WriteByte(1); + rOut.WriteFloat(0); + rOut.WriteFloat(1); } else { - mpSCLY->WriteLong(0); - mpSCLY->WriteFloat(0); - mpSCLY->WriteFloat(1); - mpSCLY->WriteShort(0); - mpSCLY->WriteByte(1); + rOut.WriteLong(0); + rOut.WriteFloat(0); + rOut.WriteFloat(1); + rOut.WriteShort(0); + rOut.WriteByte(1); } } @@ -153,14 +153,14 @@ void CScriptCooker::WriteProperty(IProperty *pProp, bool InSingleStruct) if (!pTemp->IsSingleProperty()) { - if (mVersion <= ePrime) - mpSCLY->WriteLong(PropertiesToWrite.size()); + if (mGame <= ePrime) + rOut.WriteLong(PropertiesToWrite.size()); else - mpSCLY->WriteShort((u16) PropertiesToWrite.size()); + rOut.WriteShort((u16) PropertiesToWrite.size()); } for (u32 iProp = 0; iProp < PropertiesToWrite.size(); iProp++) - WriteProperty(PropertiesToWrite[iProp], pTemp->IsSingleProperty()); + WriteProperty(rOut, PropertiesToWrite[iProp], pTemp->IsSingleProperty()); break; } @@ -168,10 +168,10 @@ void CScriptCooker::WriteProperty(IProperty *pProp, bool InSingleStruct) case eArrayProperty: { CArrayProperty *pArray = static_cast(pProp); - mpSCLY->WriteLong(pArray->Count()); + rOut.WriteLong(pArray->Count()); for (u32 iProp = 0; iProp < pArray->Count(); iProp++) - WriteProperty(pArray->PropertyByIndex(iProp), true); + WriteProperty(rOut, pArray->PropertyByIndex(iProp), true); break; } @@ -180,133 +180,128 @@ void CScriptCooker::WriteProperty(IProperty *pProp, bool InSingleStruct) if (SizeOffset != 0) { - u32 PropEnd = mpSCLY->Tell(); - mpSCLY->Seek(SizeOffset, SEEK_SET); - mpSCLY->WriteShort((u16) (PropEnd - PropStart)); - mpSCLY->Seek(PropEnd, SEEK_SET); + u32 PropEnd = rOut.Tell(); + rOut.Seek(SizeOffset, SEEK_SET); + rOut.WriteShort((u16) (PropEnd - PropStart)); + rOut.Seek(PropEnd, SEEK_SET); } } -void CScriptCooker::WriteLayerMP1(CScriptLayer *pLayer) +// ************ PUBLIC ************ +void CScriptCooker::WriteInstance(IOutputStream& rOut, CScriptObject *pInstance) { - u32 LayerStart = mpSCLY->Tell(); - mpSCLY->WriteByte(0); // Unknown value - mpSCLY->WriteLong(pLayer->NumInstances()); + ASSERT(pInstance->Area()->Game() == mGame); - for (u32 iInst = 0; iInst < pLayer->NumInstances(); iInst++) - { - CScriptObject *pInstance = pLayer->InstanceByIndex(iInst); - WriteInstanceMP1(pInstance); - } + // Note the format is pretty much the same between games; the main difference is a + // number of fields changed size between MP1 and 2, but they're still the same fields + bool IsPrime1 = (mGame <= ePrime); - u32 LayerSize = mpSCLY->Tell() - LayerStart; - u32 NumPadBytes = 32 - (LayerSize % 32); - if (NumPadBytes == 32) NumPadBytes = 0; + u32 ObjectType = pInstance->ObjectTypeID(); + IsPrime1 ? rOut.WriteByte((u8) ObjectType) : rOut.WriteLong(ObjectType); - for (u32 iPad = 0; iPad < NumPadBytes; iPad++) - mpSCLY->WriteByte(0); -} - -void CScriptCooker::WriteInstanceMP1(CScriptObject *pInstance) -{ - mpSCLY->WriteByte((u8) pInstance->ObjectTypeID()); - - u32 SizeOffset = mpSCLY->Tell(); - mpSCLY->WriteLong(0); - u32 InstanceStart = mpSCLY->Tell(); + u32 SizeOffset = rOut.Tell(); + IsPrime1 ? rOut.WriteLong(0) : rOut.WriteShort(0); + u32 InstanceStart = rOut.Tell(); u32 InstanceID = (pInstance->Layer()->AreaIndex() << 26) | pInstance->InstanceID(); - mpSCLY->WriteLong(InstanceID); - mpSCLY->WriteLong(pInstance->NumLinks(eOutgoing)); + rOut.WriteLong(InstanceID); + + u32 NumLinks = pInstance->NumLinks(eOutgoing); + IsPrime1 ? rOut.WriteLong(NumLinks) : rOut.WriteShort((u16) NumLinks); for (u32 iLink = 0; iLink < pInstance->NumLinks(eOutgoing); iLink++) { CLink *pLink = pInstance->Link(eOutgoing, iLink); - mpSCLY->WriteLong(pLink->State()); - mpSCLY->WriteLong(pLink->Message()); - mpSCLY->WriteLong(pLink->ReceiverID()); + rOut.WriteLong(pLink->State()); + rOut.WriteLong(pLink->Message()); + rOut.WriteLong(pLink->ReceiverID()); } - WriteProperty(pInstance->Properties(), false); - u32 InstanceEnd = mpSCLY->Tell(); + WriteProperty(rOut, pInstance->Properties(), false); + u32 InstanceEnd = rOut.Tell(); - mpSCLY->Seek(SizeOffset, SEEK_SET); - mpSCLY->WriteLong(InstanceEnd - InstanceStart); - mpSCLY->Seek(InstanceEnd, SEEK_SET); + rOut.Seek(SizeOffset, SEEK_SET); + u32 Size = InstanceEnd - InstanceStart; + IsPrime1 ? rOut.WriteLong(Size) : rOut.WriteShort((u16) Size); + rOut.Seek(InstanceEnd, SEEK_SET); } -void CScriptCooker::WriteLayerMP2(CScriptLayer *pLayer) +void CScriptCooker::WriteLayer(IOutputStream& rOut, CScriptLayer *pLayer) { - u32 LayerStart = mpSCLY->Tell(); - mpSCLY->WriteByte(0x1); - mpSCLY->WriteLong(pLayer->NumInstances()); + ASSERT(pLayer->Area()->Game() == mGame); + + rOut.WriteByte( mGame <= ePrime ? 0 : 1 ); // Version + + u32 InstanceCountOffset = rOut.Tell(); + u32 NumWrittenInstances = 0; + rOut.WriteLong(0); for (u32 iInst = 0; iInst < pLayer->NumInstances(); iInst++) { CScriptObject *pInstance = pLayer->InstanceByIndex(iInst); - WriteInstanceMP2(pInstance); + + // Is this a generated instance? + bool ShouldWrite = true; + + if (mWriteGeneratedSeparately) + { + // GenericCreature instances in DKCR always write to both SCLY and SCGN + if (mGame == eReturns && pInstance->ObjectTypeID() == FOURCC('GCTR')) + mGeneratedObjects.push_back(pInstance); + + // Instances receiving a Generate/Activate message (MP2) or a + // Generate/Attach message (MP3+) should be written to SCGN, not SCLY + else + { + for (u32 LinkIdx = 0; LinkIdx < pInstance->NumLinks(eIncoming); LinkIdx++) + { + CLink *pLink = pInstance->Link(eIncoming, LinkIdx); + + if (mGame <= eEchoes) + { + if (pLink->State() == FOURCC('GRNT') && pLink->Message() == FOURCC('ACTV')) + { + ShouldWrite = false; + break; + } + } + + else + { + if (pLink->Message() == FOURCC('ATCH')) + { + if (pLink->State() == FOURCC('GRNT') || pLink->State() == FOURCC('GRN0') || pLink->State() == FOURCC('GRN1')) + { + ShouldWrite = false; + break; + } + } + } + } + + if (!ShouldWrite) + mGeneratedObjects.push_back(pInstance); + } + } + + if (ShouldWrite) + { + WriteInstance(rOut, pInstance); + NumWrittenInstances++; + } } - if (mVersion == eEchoesDemo) - { - u32 LayerSize = mpSCLY->Tell() - LayerStart; - u32 NumPadBytes = 32 - (LayerSize % 32); - if (NumPadBytes == 32) NumPadBytes = 0; - - for (u32 iPad = 0; iPad < NumPadBytes; iPad++) - mpSCLY->WriteByte(0); - } + u32 LayerEnd = rOut.Tell(); + rOut.GoTo(InstanceCountOffset); + rOut.WriteLong(NumWrittenInstances); + rOut.GoTo(LayerEnd); } -void CScriptCooker::WriteInstanceMP2(CScriptObject *pInstance) +void CScriptCooker::WriteGeneratedLayer(IOutputStream& rOut) { - mpSCLY->WriteLong(pInstance->ObjectTypeID()); + rOut.WriteByte(1); // Version + rOut.WriteLong(mGeneratedObjects.size()); - u32 SizeOffset = mpSCLY->Tell(); - mpSCLY->WriteShort(0); - u32 InstanceStart = mpSCLY->Tell(); - - u32 InstanceID = (pInstance->Layer()->AreaIndex() << 26) | pInstance->InstanceID(); - mpSCLY->WriteLong(InstanceID); - mpSCLY->WriteShort((u16) pInstance->NumLinks(eOutgoing)); - - for (u32 iLink = 0; iLink < pInstance->NumLinks(eOutgoing); iLink++) - { - CLink *pLink = pInstance->Link(eOutgoing, iLink); - mpSCLY->WriteLong(pLink->State()); - mpSCLY->WriteLong(pLink->Message()); - mpSCLY->WriteLong(pLink->ReceiverID()); - } - - WriteProperty(pInstance->Properties(), false); - u32 InstanceEnd = mpSCLY->Tell(); - - mpSCLY->Seek(SizeOffset, SEEK_SET); - mpSCLY->WriteShort((u16) (InstanceEnd - InstanceStart)); - mpSCLY->Seek(InstanceEnd, SEEK_SET); -} - -// ************ STATIC ************ -void CScriptCooker::WriteLayer(EGame Game, CScriptLayer *pLayer, IOutputStream& rOut) -{ - CScriptCooker Cooker; - Cooker.mpSCLY = &rOut; - Cooker.mVersion = Game; - - if (Game <= ePrime) - Cooker.WriteLayerMP1(pLayer); - else - Cooker.WriteLayerMP2(pLayer); -} - -void CScriptCooker::CookInstance(EGame Game, CScriptObject *pInstance, IOutputStream& rOut) -{ - CScriptCooker Cooker; - Cooker.mpSCLY = &rOut; - Cooker.mVersion = Game; - - if (Game <= ePrime) - Cooker.WriteInstanceMP1(pInstance); - else - Cooker.WriteInstanceMP2(pInstance); + for (u32 InstIdx = 0; InstIdx < mGeneratedObjects.size(); InstIdx++) + WriteInstance(rOut, mGeneratedObjects[InstIdx]); } diff --git a/src/Core/Resource/Cooker/CScriptCooker.h b/src/Core/Resource/Cooker/CScriptCooker.h index d2e334e1..c28a1bf2 100644 --- a/src/Core/Resource/Cooker/CScriptCooker.h +++ b/src/Core/Resource/Cooker/CScriptCooker.h @@ -1,6 +1,7 @@ #ifndef CSCRIPTCOOKER_H #define CSCRIPTCOOKER_H +#include "CSectionMgrOut.h" #include "Core/Resource/Script/CScriptLayer.h" #include "Core/Resource/Script/CScriptObject.h" #include @@ -8,19 +9,21 @@ class CScriptCooker { - IOutputStream *mpSCLY; - EGame mVersion; + EGame mGame; + std::vector mGeneratedObjects; + bool mWriteGeneratedSeparately; - CScriptCooker() {} - void WriteProperty(IProperty *pProp, bool InSingleStruct); - void WriteLayerMP1(CScriptLayer *pLayer); - void WriteInstanceMP1(CScriptObject *pInstance); - void WriteLayerMP2(CScriptLayer *pLayer); - void WriteInstanceMP2(CScriptObject *pInstance); + void WriteProperty(IOutputStream& rOut,IProperty *pProp, bool InSingleStruct); public: - static void WriteLayer(EGame Game, CScriptLayer *pLayer, IOutputStream& rOut); - static void CookInstance(EGame Game, CScriptObject *pInstance, IOutputStream& rOut); + CScriptCooker(EGame Game, bool WriteGeneratedObjectsSeparately = true) + : mGame(Game) + , mWriteGeneratedSeparately(WriteGeneratedObjectsSeparately && mGame >= eEchoesDemo) + {} + + void WriteInstance(IOutputStream& rOut, CScriptObject *pInstance); + void WriteLayer(IOutputStream& rOut, CScriptLayer *pLayer); + void WriteGeneratedLayer(IOutputStream& rOut); }; #endif // CSCRIPTCOOKER_H diff --git a/src/Core/Resource/Factory/CAreaLoader.cpp b/src/Core/Resource/Factory/CAreaLoader.cpp index 0fbedc04..b659f4ef 100644 --- a/src/Core/Resource/Factory/CAreaLoader.cpp +++ b/src/Core/Resource/Factory/CAreaLoader.cpp @@ -143,28 +143,25 @@ void CAreaLoader::ReadSCLYPrime() } // SCGN - if (mVersion == eEchoesDemo) + CScriptLayer *pGenLayer = nullptr; + + if (mVersion >= eEchoesDemo) { mpSectionMgr->ToSection(mScriptGeneratorBlockNum); + CFourCC SCGN = mpMREA->ReadFourCC(); - CFourCC SCGN(*mpMREA); - if (SCGN != "SCGN") + if (SCGN != FOURCC('SCGN')) Log::FileError(mpMREA->GetSourceString(), mpMREA->Tell() - 4, "Invalid SCGN magic: " + SCGN.ToString()); else { mpMREA->Seek(0x1, SEEK_CUR); - CScriptLayer *pLayer = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion); - - if (pLayer) - { - MergeGeneratedLayer(pLayer); - delete pLayer; - } + pGenLayer = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion); } } - SetUpObjects(); + SetUpObjects(pGenLayer); + delete pGenLayer; } void CAreaLoader::ReadLightsPrime() @@ -310,7 +307,6 @@ void CAreaLoader::ReadSCLYEchoes() } // SCGN - // we want to regenerate the SCGN layer on cook - for now just move everything back to its original layer CFourCC SCGN(*mpMREA); if (SCGN != "SCGN") { @@ -320,14 +316,8 @@ void CAreaLoader::ReadSCLYEchoes() mpMREA->Seek(0x1, SEEK_CUR); // Skipping unknown CScriptLayer *pGeneratedLayer = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion); - - if (pGeneratedLayer) - { - MergeGeneratedLayer(pGeneratedLayer); - delete pGeneratedLayer; - } - - SetUpObjects(); + SetUpObjects(pGeneratedLayer); + delete pGeneratedLayer; } // ************ CORRUPTION ************ @@ -606,61 +596,66 @@ void CAreaLoader::ReadEGMC() mpArea->mpPoiToWorldMap = gpResourceStore->LoadResource(EGMC, eStaticGeometryMap); } -void CAreaLoader::MergeGeneratedLayer(CScriptLayer *pLayer) +void CAreaLoader::SetUpObjects(CScriptLayer *pGenLayer) { - while (pLayer->NumInstances() != 0) + // Create instance map + for (u32 LayerIdx = 0; LayerIdx < mpArea->NumScriptLayers(); LayerIdx++) { - CScriptObject *pObj = pLayer->InstanceByIndex(0); - u32 InstanceID = pObj->InstanceID(); + CScriptLayer *pLayer = mpArea->mScriptLayers[LayerIdx]; + + for (u32 InstIdx = 0; InstIdx < pLayer->NumInstances(); InstIdx++) + { + CScriptObject *pInst = pLayer->InstanceByIndex(InstIdx); + u32 InstanceID = pInst->InstanceID(); + CScriptObject *pExisting = mpArea->InstanceByID(InstanceID); + ASSERT(pExisting == nullptr); + mpArea->mObjectMap[InstanceID] = pInst; + } + } + + // Merge objects from the generated layer back into the regular script layers + while (pGenLayer->NumInstances() != 0) + { + CScriptObject *pInst = pGenLayer->InstanceByIndex(0); + u32 InstanceID = pInst->InstanceID(); // Check if this is a duplicate of an existing instance (this only happens with DKCR GenericCreature as far as I'm aware) - CScriptObject *pDupe = mpArea->InstanceByID(InstanceID); - - if (pDupe) + if (mpArea->InstanceByID(InstanceID) != nullptr) { - Log::Write("Duplicate SCGN object: [" + pObj->Template()->Name() + "] " + pObj->InstanceName() + " (" + TString::HexString(pObj->InstanceID(), 8, false) + ")"); - pLayer->RemoveInstance(pObj); - delete pObj; + Log::Write("Duplicate SCGN object: [" + pInst->Template()->Name() + "] " + pInst->InstanceName() + " (" + TString::HexString(pInst->InstanceID(), 8, false) + ")"); + pGenLayer->RemoveInstance(pInst); + delete pInst; } else { u32 LayerIdx = (InstanceID >> 26) & 0x3F; - pObj->SetLayer( mpArea->ScriptLayer(LayerIdx) ); + pInst->SetLayer( mpArea->ScriptLayer(LayerIdx) ); + mpArea->mObjectMap[InstanceID] = pInst; } } -} -void CAreaLoader::SetUpObjects() -{ // Iterate over all objects - for (u32 iLyr = 0; iLyr < mpArea->NumScriptLayers(); iLyr++) + for (auto Iter = mpArea->mObjectMap.begin(); Iter != mpArea->mObjectMap.end(); Iter++) { - CScriptLayer *pLayer = mpArea->mScriptLayers[iLyr]; + CScriptObject *pInst = Iter->second; - for (u32 iObj = 0; iObj < pLayer->NumInstances(); iObj++) + // Store outgoing connections + for (u32 iCon = 0; iCon < pInst->NumLinks(eOutgoing); iCon++) { - // Add object to object map - CScriptObject *pObj = (*pLayer)[iObj]; - mpArea->mObjectMap[pObj->InstanceID()] = pObj; + CLink *pLink = pInst->Link(eOutgoing, iCon); + mConnectionMap[pLink->ReceiverID()].push_back(pLink); + } - // Store outgoing connections - for (u32 iCon = 0; iCon < pObj->NumLinks(eOutgoing); iCon++) - { - CLink *pLink = pObj->Link(eOutgoing, iCon); - mConnectionMap[pLink->ReceiverID()].push_back(pLink); - } + // Remove "-component" garbage from MP1 instance names + if (mVersion <= ePrime) + { + TString InstanceName = pInst->InstanceName(); - // Remove "-component" garbage from MP1 instance names - if (mVersion <= ePrime) - { - TString InstanceName = pObj->InstanceName(); + while (InstanceName.EndsWith("-component")) + InstanceName = InstanceName.ChopBack(10); - while (InstanceName.EndsWith("-component")) - InstanceName = InstanceName.ChopBack(10); - - pObj->SetName(InstanceName); - } + pInst->SetName(InstanceName); } } diff --git a/src/Core/Resource/Factory/CAreaLoader.h b/src/Core/Resource/Factory/CAreaLoader.h index cd4d161f..539d1f34 100644 --- a/src/Core/Resource/Factory/CAreaLoader.h +++ b/src/Core/Resource/Factory/CAreaLoader.h @@ -77,8 +77,7 @@ class CAreaLoader void ReadPATH(); void ReadPTLA(); void ReadEGMC(); - void MergeGeneratedLayer(CScriptLayer *pLayer); - void SetUpObjects(); + void SetUpObjects(CScriptLayer *pGenLayer); public: static CGameArea* LoadMREA(IInputStream& rMREA, CResourceEntry *pEntry); diff --git a/src/Editor/CNodeCopyMimeData.h b/src/Editor/CNodeCopyMimeData.h index c4fe0490..f5eb7dd8 100644 --- a/src/Editor/CNodeCopyMimeData.h +++ b/src/Editor/CNodeCopyMimeData.h @@ -70,7 +70,9 @@ public: rNode.OriginalInstanceID = pInst->InstanceID(); CVectorOutStream Out(&rNode.InstanceData, IOUtil::eBigEndian); - CScriptCooker::CookInstance(eReturns, static_cast(*It)->Instance(), Out); + + CScriptCooker Cooker(mGame); + Cooker.WriteInstance(Out, static_cast(*It)->Instance()); // Replace instance ID with 0xFFFFFFFF to force it to generate a new one. Out.Seek(0x6, SEEK_SET); diff --git a/src/Editor/Undo/CDeleteSelectionCommand.cpp b/src/Editor/Undo/CDeleteSelectionCommand.cpp index ec901aa0..cb51b899 100644 --- a/src/Editor/Undo/CDeleteSelectionCommand.cpp +++ b/src/Editor/Undo/CDeleteSelectionCommand.cpp @@ -64,7 +64,8 @@ CDeleteSelectionCommand::CDeleteSelectionCommand(CWorldEditor *pEditor, const QS } CVectorOutStream PropertyDataOut(&rNode.InstanceData, IOUtil::eBigEndian); - CScriptCooker::CookInstance(eReturns, pInst, PropertyDataOut); + CScriptCooker Cooker(pEditor->CurrentGame()); + Cooker.WriteInstance(PropertyDataOut, pInst); } else