diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 061238bb..6af25035 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -395,6 +395,7 @@ set(PRP_MOD_SOURCES PRP/Modifier/plAliasModifier.cpp PRP/Modifier/plAnimEventModifier.cpp PRP/Modifier/plAxisAnimModifier.cpp + PRP/Modifier/plCloneSpawnModifier.cpp PRP/Modifier/plExcludeRegionModifier.cpp PRP/Modifier/plFollowMod.cpp PRP/Modifier/plGameMarkerModifier.cpp @@ -417,6 +418,7 @@ set(PRP_MOD_HEADERS PRP/Modifier/plAliasModifier.h PRP/Modifier/plAnimEventModifier.h PRP/Modifier/plAxisAnimModifier.h + PRP/Modifier/plCloneSpawnModifier.h PRP/Modifier/plExcludeRegionModifier.h PRP/Modifier/plFollowMod.h PRP/Modifier/plGameMarkerModifier.h diff --git a/core/PRP/Avatar/plAGAnim.cpp b/core/PRP/Avatar/plAGAnim.cpp index 39cea704..5e7a110a 100644 --- a/core/PRP/Avatar/plAGAnim.cpp +++ b/core/PRP/Avatar/plAGAnim.cpp @@ -31,6 +31,12 @@ void plAGAnim::read(hsStream* S, plResManager* mgr) fStart = S->readFloat(); fEnd = S->readFloat(); + // MQO data + if (S->getVer().isMoul() && pdUnifiedTypeMap::CurrentVersion(this->ClassIndex()) >= 5) { + fUnknownMQO1 = S->readFloat(); + fUnknownMQO2 = S->readFloat(); + } + clearApplicators(); fApps.resize(S->readInt()); for (size_t i=0; iwriteFloat(fStart); S->writeFloat(fEnd); + // MQO data + if (S->getVer().isMoul() && pdUnifiedTypeMap::CurrentVersion(this->ClassIndex()) >= 5) { + S->writeFloat(fUnknownMQO1); + S->writeFloat(fUnknownMQO2); + } + S->writeInt(fApps.size()); for (size_t i=0; iWriteCreatable(S, fApps[i]); @@ -70,6 +82,8 @@ void plAGAnim::IPrcWrite(pfPrcHelper* prc) prc->writeParam("Name", fName); prc->writeParam("Start", fStart); prc->writeParam("End", fEnd); + prc->writeParam("UnknownMQO1", fUnknownMQO1); + prc->writeParam("UnknownMQO2", fUnknownMQO2); prc->writeParam("EoaFlag", fEoaFlag); prc->endTag(true); @@ -93,6 +107,8 @@ void plAGAnim::IPrcParse(const pfPrcTag* tag, plResManager* mgr) fName = tag->getParam("Name", ""); fStart = tag->getParam("Start", "0").to_float(); fEnd = tag->getParam("End", "0").to_float(); + fUnknownMQO1 = tag->getParam("UnknownMQO1", "0").to_float(); + fUnknownMQO2 = tag->getParam("UnknownMQO2", "0").to_float(); fEoaFlag = tag->getParam("EoaFlag", "0").to_uint(); } else if (tag->getName() == "Applicators") { clearApplicators(); diff --git a/core/PRP/Avatar/plAGAnim.h b/core/PRP/Avatar/plAGAnim.h index 0a45b685..30978aa5 100644 --- a/core/PRP/Avatar/plAGAnim.h +++ b/core/PRP/Avatar/plAGAnim.h @@ -33,11 +33,12 @@ class HSPLASMA_EXPORT plAGAnim : public plSynchedObject protected: std::vector fApps; float fBlend, fStart, fEnd; + float fUnknownMQO1, fUnknownMQO2; ST::string fName; unsigned char fEoaFlag; public: - plAGAnim() : fBlend(), fStart(), fEnd(), fEoaFlag() { } + plAGAnim() : fBlend(), fStart(), fEnd(), fUnknownMQO1(), fUnknownMQO2(), fEoaFlag() { } ~plAGAnim(); void read(hsStream* S, plResManager* mgr) HS_OVERRIDE; diff --git a/core/PRP/Avatar/plMultistageBehMod.cpp b/core/PRP/Avatar/plMultistageBehMod.cpp index 3e2a9691..3ff3c7fe 100644 --- a/core/PRP/Avatar/plMultistageBehMod.cpp +++ b/core/PRP/Avatar/plMultistageBehMod.cpp @@ -28,7 +28,10 @@ void plMultistageBehMod::read(hsStream* S, plResManager* mgr) fFreezePhys = S->readBool(); fSmartSeek = S->readBool(); - fReverseFBControlsOnRelease = S->readBool(); + + if (S->getVer() >= MAKE_VERSION(2, 0, 62, 12)) { + fReverseFBControlsOnRelease = S->readBool(); + } clearStages(); fStages.resize(S->readInt()); diff --git a/core/PRP/KeyedObject/plLoadMask.cpp b/core/PRP/KeyedObject/plLoadMask.cpp index f3136957..eca58a53 100644 --- a/core/PRP/KeyedObject/plLoadMask.cpp +++ b/core/PRP/KeyedObject/plLoadMask.cpp @@ -18,9 +18,13 @@ void plLoadMask::read(hsStream* S) { - unsigned char m = S->readByte(); - fQuality[0] = (m >> 4) | 0xF0; - fQuality[1] = m | 0xF0; + if (S->getVer() < MAKE_VERSION(2, 0, 63, 0) && S->getVer().isValid()) { + S->readInt(); + } else { + unsigned char m = S->readByte(); + fQuality[0] = (m >> 4) | 0xF0; + fQuality[1] = m | 0xF0; + } } void plLoadMask::write(hsStream* S) diff --git a/core/PRP/KeyedObject/plUoid.cpp b/core/PRP/KeyedObject/plUoid.cpp index c0ef5b9e..92f3df9e 100644 --- a/core/PRP/KeyedObject/plUoid.cpp +++ b/core/PRP/KeyedObject/plUoid.cpp @@ -49,6 +49,10 @@ void plUoid::read(hsStream* S) unsigned char contents = 0; if (S->getVer() < MAKE_VERSION(2, 0, 63, 0) && S->getVer().isValid()) { contents = kHasCloneIDs; + + if (S->getVer() > MAKE_VERSION(2, 0, 62, 0)) { + contents |= kHasLoadMask; + } } else { contents = S->readByte(); } diff --git a/core/PRP/Message/plCameraMsg.cpp b/core/PRP/Message/plCameraMsg.cpp index 0e4035f1..430b0cf9 100644 --- a/core/PRP/Message/plCameraMsg.cpp +++ b/core/PRP/Message/plCameraMsg.cpp @@ -154,6 +154,10 @@ void plCameraMsg::read(hsStream* S, plResManager* mgr) fNewCam = mgr->readKey(S); fTriggerer = mgr->readKey(S); fConfig.read(S); + + if (S->getVer().isMqo()) { + fUnknownMQO.read(S); + } } void plCameraMsg::write(hsStream* S, plResManager* mgr) @@ -166,6 +170,10 @@ void plCameraMsg::write(hsStream* S, plResManager* mgr) mgr->writeKey(S, fNewCam); mgr->writeKey(S, fTriggerer); fConfig.write(S); + + if (S->getVer().isMqo()) { + fUnknownMQO.write(S); + } } void plCameraMsg::IPrcWrite(pfPrcHelper* prc) @@ -189,6 +197,10 @@ void plCameraMsg::IPrcWrite(pfPrcHelper* prc) prc->closeTag(); fConfig.prcWrite(prc); + + prc->writeSimpleTag("UnknownMQO"); + fUnknownMQO.prcWrite(prc); + prc->closeTag(); } void plCameraMsg::IPrcParse(const pfPrcTag* tag, plResManager* mgr) @@ -207,6 +219,9 @@ void plCameraMsg::IPrcParse(const pfPrcTag* tag, plResManager* mgr) fTriggerer = mgr->prcParseKey(tag->getFirstChild()); } else if (tag->getName() == "plCameraConfig") { fConfig.prcParse(tag); + } else if (tag->getName() == "UnknownMQO") { + if (tag->hasChildren()) + fUnknownMQO.prcParse(tag->getFirstChild()); } else { plMessage::IPrcParse(tag, mgr); } diff --git a/core/PRP/Message/plCameraMsg.h b/core/PRP/Message/plCameraMsg.h index 68fbb1b2..0162bef9 100644 --- a/core/PRP/Message/plCameraMsg.h +++ b/core/PRP/Message/plCameraMsg.h @@ -98,6 +98,7 @@ class HSPLASMA_EXPORT plCameraMsg : public plMessage plCameraConfig fConfig; bool fActivated; hsBitVector fCmd; + hsVector3 fUnknownMQO; public: plCameraMsg(); diff --git a/core/PRP/Modifier/plCloneSpawnModifier.cpp b/core/PRP/Modifier/plCloneSpawnModifier.cpp new file mode 100644 index 00000000..a7457219 --- /dev/null +++ b/core/PRP/Modifier/plCloneSpawnModifier.cpp @@ -0,0 +1,64 @@ +/* This file is part of HSPlasma. + * + * HSPlasma is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * HSPlasma is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with HSPlasma. If not, see . + */ + +#include "plCloneSpawnModifier.h" + +void plCloneSpawnModifier::read(hsStream* S, plResManager* mgr) +{ + if (S->getVer().isMoul()) { + plSingleModifier::read(S, mgr); + fTemplateName = S->readSafeStr(); + fUserData = S->readInt(); + } else { + // This is bad, but also never used in earlier Plasma versions + fTemplateName = S->readSafeStr(); + plSingleModifier::read(S, mgr); + } +} + +void plCloneSpawnModifier::write(hsStream* S, plResManager* mgr) +{ + if (S->getVer().isMoul()) { + plSingleModifier::write(S, mgr); + S->writeSafeStr(fTemplateName); + S->writeInt(fUserData); + } else { + // This is bad, but also never used in earlier Plasma versions + S->writeSafeStr(fTemplateName); + plSingleModifier::write(S, mgr); + } +} + +void plCloneSpawnModifier::IPrcWrite(pfPrcHelper* prc) +{ + plSingleModifier::IPrcWrite(prc); + + prc->startTag("SpawnParams"); + prc->writeParam("TemplateName", fTemplateName); + prc->writeParam("UserData", fUserData); + prc->endTag(true); +} + +void plCloneSpawnModifier::IPrcParse(const pfPrcTag* tag, plResManager* mgr) +{ + if (tag->getName() == "SpawnParams") { + fTemplateName = tag->getParam("TemplateName", ""); + fUserData = tag->getParam("UserData", "0").to_uint(); + } else { + plSingleModifier::IPrcParse(tag, mgr); + } +} + diff --git a/core/PRP/Modifier/plCloneSpawnModifier.h b/core/PRP/Modifier/plCloneSpawnModifier.h new file mode 100644 index 00000000..72c33274 --- /dev/null +++ b/core/PRP/Modifier/plCloneSpawnModifier.h @@ -0,0 +1,41 @@ +/* This file is part of HSPlasma. + * + * HSPlasma is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * HSPlasma is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with HSPlasma. If not, see . + */ + +#ifndef _PLCLONESPAWNMODIFIER_H +#define _PLCLONESPAWNMODIFIER_H + +#include "plModifier.h" + +class HSPLASMA_EXPORT plCloneSpawnModifier : public plSingleModifier +{ + CREATABLE(plCloneSpawnModifier, kCloneSpawnModifier, plSingleModifier) + +protected: + ST::string fTemplateName; + uint32_t fUserData; + +public: + plCloneSpawnModifier() : fUserData() { } + + void read(hsStream* S, plResManager* mgr) HS_OVERRIDE; + void write(hsStream* S, plResManager* mgr) HS_OVERRIDE; + +protected: + void IPrcWrite(pfPrcHelper* prc) HS_OVERRIDE; + void IPrcParse(const pfPrcTag* tag, plResManager* mgr) HS_OVERRIDE; +}; + +#endif diff --git a/core/PRP/Modifier/plOneShotMod.cpp b/core/PRP/Modifier/plOneShotMod.cpp index db7553ef..dfa1d77e 100644 --- a/core/PRP/Modifier/plOneShotMod.cpp +++ b/core/PRP/Modifier/plOneShotMod.cpp @@ -26,9 +26,13 @@ void plOneShotMod::read(hsStream* S, plResManager* mgr) fReversable = S->readBool(); fSmartSeek = S->readBool(); - if (S->getVer() > 0x02006304) { /*TODO: Verify! */ + if (S->getVer() > MAKE_VERSION(2, 0, 63, 4)) { fNoSeek = S->readBool(); } + + if (S->getVer().isMoul() && pdUnifiedTypeMap::CurrentVersion(this->ClassIndex()) >= 3) { + fUnknownMQO = S->readBool(); + } } void plOneShotMod::write(hsStream* S, plResManager* mgr) @@ -41,6 +45,10 @@ void plOneShotMod::write(hsStream* S, plResManager* mgr) S->writeBool(fReversable); S->writeBool(fSmartSeek); S->writeBool(fNoSeek); + + if (S->getVer().isMoul() && pdUnifiedTypeMap::CurrentVersion(this->ClassIndex()) >= 3) { + S->writeBool(fUnknownMQO); + } } void plOneShotMod::IPrcWrite(pfPrcHelper* prc) @@ -54,6 +62,7 @@ void plOneShotMod::IPrcWrite(pfPrcHelper* prc) prc->writeParam("Reversable", fReversable); prc->writeParam("SmartSeek", fSmartSeek); prc->writeParam("NoSeek", fNoSeek); + prc->writeParam("UnknownMQO", fUnknownMQO); prc->endTag(true); } @@ -66,6 +75,7 @@ void plOneShotMod::IPrcParse(const pfPrcTag* tag, plResManager* mgr) fReversable = tag->getParam("Reversable", "false").to_bool(); fSmartSeek = tag->getParam("SmartSeek", "false").to_bool(); fNoSeek = tag->getParam("NoSeek", "true").to_bool(); + fUnknownMQO = tag->getParam("UnknownMQO", "false").to_bool(); } else { plMultiModifier::IPrcParse(tag, mgr); } diff --git a/core/PRP/Modifier/plOneShotMod.h b/core/PRP/Modifier/plOneShotMod.h index 098785a6..e11993b4 100644 --- a/core/PRP/Modifier/plOneShotMod.h +++ b/core/PRP/Modifier/plOneShotMod.h @@ -25,13 +25,13 @@ class HSPLASMA_EXPORT plOneShotMod : public plMultiModifier protected: ST::string fAnimName; - bool fDrivable, fReversable, fSmartSeek, fNoSeek; + bool fDrivable, fReversable, fSmartSeek, fNoSeek, fUnknownMQO; float fSeekDuration; public: plOneShotMod() : fDrivable(), fReversable(), fSmartSeek(), fNoSeek(true), - fSeekDuration() { } + fUnknownMQO(), fSeekDuration() { } void read(hsStream* S, plResManager* mgr) HS_OVERRIDE; void write(hsStream* S, plResManager* mgr) HS_OVERRIDE; diff --git a/core/PRP/Physics/plGenericPhysical.cpp b/core/PRP/Physics/plGenericPhysical.cpp index 3472f2a3..b9df24d3 100644 --- a/core/PRP/Physics/plGenericPhysical.cpp +++ b/core/PRP/Physics/plGenericPhysical.cpp @@ -99,6 +99,10 @@ void plGenericPhysical::read(hsStream* S, plResManager* mgr) { plSynchedObject::read(S, mgr); + if (S->getVer() < MAKE_VERSION(2, 0, 63, 0)) { + return; + } + if (S->getVer().isUniversal()) fInternalType = (PhysType)S->readInt(); else if (S->getVer().isNewPlasma()) diff --git a/core/PRP/plPageInfo.cpp b/core/PRP/plPageInfo.cpp index 89f3b9c2..b637311f 100644 --- a/core/PRP/plPageInfo.cpp +++ b/core/PRP/plPageInfo.cpp @@ -133,6 +133,25 @@ void plPageInfo::read(hsStream* S) pdUnifiedTypeMap::ClassVersion(type, S->getVer()), ver); } } + + // We have a slight problem with MQO data, because it is effectively + // indistinguishable from MOUL data. Most classes correctly use the + // type map to indicate when they have been updated, with the exception + // of plMessages embedded within ResponderModifiers. We have no way of + // knowing when there is extra data to read in those messages, and + // failing to read it correctly will corrupt the stream and probably + // crash. + // + // Since there is a finite set of MQO files, we're going to hackily + // check what Age we're reading here and compare the Age name and + // sequence prefix against known MQO Ages, and then set our stream + // version to pvMqo (which isn't actually accurate, but we need *some* + // way to track this when reading) + if ((fAge == "Courtyard" && fLocation.getSeqPrefix() == 3) + || (fAge == "Forest" && fLocation.getSeqPrefix() == 5) + || (fAge == "PortalWell" && fLocation.getSeqPrefix() == 1)) { + S->setVer(PlasmaVer::pvMqo); + } } plDebug::Debug("* Loading: {} ({})\n" diff --git a/core/ResManager/pdUnifiedTypeMap.cpp b/core/ResManager/pdUnifiedTypeMap.cpp index 03255fec..3e9cb260 100644 --- a/core/ResManager/pdUnifiedTypeMap.cpp +++ b/core/ResManager/pdUnifiedTypeMap.cpp @@ -2187,6 +2187,7 @@ short pdUnifiedTypeMap::PlasmaToMapped(short typeIdx, PlasmaVer ver) case PlasmaVer::pvPots: return fPotSP2MTable[typeIdx]; case PlasmaVer::pvMoul: + case PlasmaVer::pvMqo: return fLiveP2MTable[typeIdx]; case PlasmaVer::pvEoa: return fEoaP2MTable[typeIdx]; @@ -2214,6 +2215,7 @@ short pdUnifiedTypeMap::MappedToPlasma(short typeIdx, PlasmaVer ver) case PlasmaVer::pvPots: return fPotSM2PTable[typeIdx]; case PlasmaVer::pvMoul: + case PlasmaVer::pvMqo: return fLiveM2PTable[typeIdx]; case PlasmaVer::pvEoa: return fEoaM2PTable[typeIdx]; @@ -2233,6 +2235,7 @@ short pdUnifiedTypeMap::ClassVersion(short typeIdx, PlasmaVer ver) switch (ver) { case PlasmaVer::pvMoul: + case PlasmaVer::pvMqo: return fLiveVerTable[PlasmaToMapped(typeIdx, ver)]; case PlasmaVer::pvEoa: return fEoaVerTable[PlasmaToMapped(typeIdx, ver)]; @@ -2259,6 +2262,7 @@ void pdUnifiedTypeMap::SetCurrentVersionBase(PlasmaVer ver) switch (ver) { case PlasmaVer::pvMoul: + case PlasmaVer::pvMqo: base = fLiveVerTable; break; case PlasmaVer::pvEoa: diff --git a/core/ResManager/plFactory.cpp b/core/ResManager/plFactory.cpp index b20fe20c..f6be2519 100644 --- a/core/ResManager/plFactory.cpp +++ b/core/ResManager/plFactory.cpp @@ -116,6 +116,7 @@ #include "PRP/Modifier/plAliasModifier.h" #include "PRP/Modifier/plAnimEventModifier.h" #include "PRP/Modifier/plAxisAnimModifier.h" +#include "PRP/Modifier/plCloneSpawnModifier.h" #include "PRP/Modifier/plExcludeRegionModifier.h" #include "PRP/Modifier/plFollowMod.h" #include "PRP/Modifier/plGameMarkerModifier.h" @@ -381,7 +382,7 @@ plCreatable* plFactory::Create(short typeIdx) case kGUIKnobCtrl: return new pfGUIKnobCtrl(); case kAvLadderMod: return new plAvLadderMod(); case kCameraBrain1_FirstPerson: return new plCameraBrain1_FirstPerson(); - //case kCloneSpawnModifier: return new plCloneSpawnModifier(); + case kCloneSpawnModifier: return new plCloneSpawnModifier(); case kClothingItem: return new plClothingItem(); case kClothingOutfit: return new plClothingOutfit(); case kClothingBase: return new plClothingBase(); diff --git a/core/Util/PlasmaVersions.cpp b/core/Util/PlasmaVersions.cpp index 7f5f64d4..5f618465 100644 --- a/core/Util/PlasmaVersions.cpp +++ b/core/Util/PlasmaVersions.cpp @@ -24,7 +24,9 @@ const char* PlasmaVer::GetVersionName(PlasmaVer ver) case pvPots: return "PotS/CC"; case pvMoul: - return "MOUL/MQO"; + return "MOUL"; + case pvMqo: + return "MQO"; case pvEoa: return "Myst V/Crowthistle"; case pvHex: @@ -49,6 +51,8 @@ PlasmaVer PlasmaVer::GetSafestVersion(PlasmaVer ver) return pvPots; else if (ver <= pvMoul) return pvMoul; + else if (ver == pvMqo) + return pvMqo; else if (ver == pvEoa) return pvEoa; else if (ver == pvHex) diff --git a/core/Util/PlasmaVersions.h b/core/Util/PlasmaVersions.h index 27fcdfb1..12881982 100644 --- a/core/Util/PlasmaVersions.h +++ b/core/Util/PlasmaVersions.h @@ -85,6 +85,7 @@ class HSPLASMA_EXPORT PlasmaVer pvPrime = MAKE_VERSION(2, 0, 63, 11), pvPots = MAKE_VERSION(2, 0, 63, 12), pvMoul = MAKE_VERSION(2, 0, 70, 0), + pvMqo = MAKE_VERSION(2, 0, 70, 1), pvEoa = MAKE_VERSION(2, 1, 6, 10), pvHex = MAKE_VERSION(3, 0, 0, 0), pvUniversal = -1, @@ -115,7 +116,8 @@ class HSPLASMA_EXPORT PlasmaVer bool isPrime() const { return fVersion == pvPrime; } bool isPots() const { return fVersion == pvPots; } - bool isMoul() const { return fVersion == pvMoul; } + bool isMoul() const { return fVersion == pvMoul || fVersion == pvMqo; } + bool isMqo() const { return fVersion == pvMqo; } bool isEoa() const { return fVersion == pvEoa; } bool isHexIsle() const { return fVersion == pvHex; } @@ -127,7 +129,7 @@ class HSPLASMA_EXPORT PlasmaVer bool isSafeVer() const { return fVersion == pvPrime || fVersion == pvPots || fVersion == pvMoul - || fVersion == pvEoa || fVersion == pvHex; + || fVersion == pvMqo || fVersion == pvEoa || fVersion == pvHex; } static const char* GetVersionName(PlasmaVer ver);