1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-03-17 10:21:11 +00:00

Merge pull request #14 from cc9cii/minor-enhancements

Minor enhancements and performance improvements
This commit is contained in:
cc9cii 2015-12-19 19:25:40 +11:00
commit 40bacc04c5
63 changed files with 2616 additions and 1130 deletions

View File

@ -576,6 +576,7 @@ add_subdirectory (extern/shiny)
add_subdirectory (extern/ogre-ffmpeg-videoplayer) add_subdirectory (extern/ogre-ffmpeg-videoplayer)
add_subdirectory (extern/oics) add_subdirectory (extern/oics)
add_subdirectory (extern/sdl4ogre) add_subdirectory (extern/sdl4ogre)
add_subdirectory (extern/murmurhash)
# Components # Components
add_subdirectory (components) add_subdirectory (components)
@ -704,6 +705,17 @@ if (WIN32)
4800 # Boolean optimization warning, e.g. myBool = (myInt != 0) instead of myBool = myInt 4800 # Boolean optimization warning, e.g. myBool = (myInt != 0) instead of myBool = myInt
) )
# MSVC 2015
if (MSVC_VERSION GREATER 1899)
set(WARNINGS_DISABLE ${WARNINGS_DISABLE}
4464 # relative include path contains '..'
5026 # move constructor was implicitly defined as deleted
5027 # move assignment operator was implicitly defined as deleted
5031 # #pragma warning(pop): likely mismatch, popping warning state pushed in different file
5032 # detected #pragma warning(push) with no corresponding #pragma warning(pop)
)
endif()
foreach(d ${WARNINGS_DISABLE}) foreach(d ${WARNINGS_DISABLE})
set(WARNINGS "${WARNINGS} /wd${d}") set(WARNINGS "${WARNINGS} /wd${d}")
endforeach(d) endforeach(d)

View File

@ -19,7 +19,7 @@ opencs_hdrs_noqt (model/doc
opencs_units (model/world opencs_units (model/world
idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree infotableproxymodel idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree infotableproxymodel
pathgridcommands pathgridcommands npcautocalc
) )
@ -36,7 +36,7 @@ opencs_hdrs_noqt (model/world
opencs_units (model/tools opencs_units (model/tools
tools reportmodel mergeoperation tools reportmodel mergeoperation
) )
opencs_units_noqt (model/tools opencs_units_noqt (model/tools
@ -211,6 +211,7 @@ target_link_libraries(openmw-cs
${OGRE_Overlay_LIBRARIES} ${OGRE_Overlay_LIBRARIES}
${OGRE_STATIC_PLUGINS} ${OGRE_STATIC_PLUGINS}
${SHINY_LIBRARIES} ${SHINY_LIBRARIES}
${MURMURHASH_LIBRARIES}
${Boost_SYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY}
${Boost_FILESYSTEM_LIBRARY} ${Boost_FILESYSTEM_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY}

View File

@ -2,6 +2,7 @@
#include <cassert> #include <cassert>
#include <fstream> #include <fstream>
#include <memory>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
@ -278,264 +279,264 @@ void CSMDoc::Document::addGmsts()
static const float gmstFloatsValues[] = static const float gmstFloatsValues[] =
{ {
0.3, // fAIFleeFleeMult 0.3f, // fAIFleeFleeMult
7.0, // fAIFleeHealthMult 7.0f, // fAIFleeHealthMult
3.0, // fAIMagicSpellMult 3.0f, // fAIMagicSpellMult
1.0, // fAIMeleeArmorMult 1.0f, // fAIMeleeArmorMult
1.0, // fAIMeleeSummWeaponMult 1.0f, // fAIMeleeSummWeaponMult
2.0, // fAIMeleeWeaponMult 2.0f, // fAIMeleeWeaponMult
5.0, // fAIRangeMagicSpellMult 5.0f, // fAIRangeMagicSpellMult
5.0, // fAIRangeMeleeWeaponMult 5.0f, // fAIRangeMeleeWeaponMult
2000.0, // fAlarmRadius 2000.0f, // fAlarmRadius
1.0, // fAthleticsRunBonus 1.0f, // fAthleticsRunBonus
40.0, // fAudioDefaultMaxDistance 40.0f, // fAudioDefaultMaxDistance
5.0, // fAudioDefaultMinDistance 5.0f, // fAudioDefaultMinDistance
50.0, // fAudioMaxDistanceMult 50.0f, // fAudioMaxDistanceMult
20.0, // fAudioMinDistanceMult 20.0f, // fAudioMinDistanceMult
60.0, // fAudioVoiceDefaultMaxDistance 60.0f, // fAudioVoiceDefaultMaxDistance
10.0, // fAudioVoiceDefaultMinDistance 10.0f, // fAudioVoiceDefaultMinDistance
50.0, // fAutoPCSpellChance 50.0f, // fAutoPCSpellChance
80.0, // fAutoSpellChance 80.0f, // fAutoSpellChance
50.0, // fBargainOfferBase 50.0f, // fBargainOfferBase
-4.0, // fBargainOfferMulti -4.0f, // fBargainOfferMulti
24.0, // fBarterGoldResetDelay 24.0f, // fBarterGoldResetDelay
1.75, // fBaseRunMultiplier 1.75f, // fBaseRunMultiplier
1.25, // fBlockStillBonus 1.25f, // fBlockStillBonus
150.0, // fBribe1000Mod 150.0f, // fBribe1000Mod
75.0, // fBribe100Mod 75.0f, // fBribe100Mod
35.0, // fBribe10Mod 35.0f, // fBribe10Mod
60.0, // fCombatAngleXY 60.0f, // fCombatAngleXY
60.0, // fCombatAngleZ 60.0f, // fCombatAngleZ
0.25, // fCombatArmorMinMult 0.25f, // fCombatArmorMinMult
-90.0, // fCombatBlockLeftAngle -90.0f, // fCombatBlockLeftAngle
30.0, // fCombatBlockRightAngle 30.0f, // fCombatBlockRightAngle
4.0, // fCombatCriticalStrikeMult 4.0f, // fCombatCriticalStrikeMult
0.1, // fCombatDelayCreature 0.1f, // fCombatDelayCreature
0.1, // fCombatDelayNPC 0.1f, // fCombatDelayNPC
128.0, // fCombatDistance 128.0f, // fCombatDistance
0.3, // fCombatDistanceWerewolfMod 0.3f, // fCombatDistanceWerewolfMod
30.0, // fCombatForceSideAngle 30.0f, // fCombatForceSideAngle
0.2, // fCombatInvisoMult 0.2f, // fCombatInvisoMult
1.5, // fCombatKODamageMult 1.5f, // fCombatKODamageMult
45.0, // fCombatTorsoSideAngle 45.0f, // fCombatTorsoSideAngle
0.3, // fCombatTorsoStartPercent 0.3f, // fCombatTorsoStartPercent
0.8, // fCombatTorsoStopPercent 0.8f, // fCombatTorsoStopPercent
15.0, // fConstantEffectMult 15.0f, // fConstantEffectMult
72.0, // fCorpseClearDelay 72.0f, // fCorpseClearDelay
72.0, // fCorpseRespawnDelay 72.0f, // fCorpseRespawnDelay
0.5, // fCrimeGoldDiscountMult 0.5f, // fCrimeGoldDiscountMult
0.9, // fCrimeGoldTurnInMult 0.9f, // fCrimeGoldTurnInMult
1.0, // fCrimeStealing 1.0f, // fCrimeStealing
0.5, // fDamageStrengthBase 0.5f, // fDamageStrengthBase
0.1, // fDamageStrengthMult 0.1f, // fDamageStrengthMult
5.0, // fDifficultyMult 5.0f, // fDifficultyMult
2.5, // fDiseaseXferChance 2.5f, // fDiseaseXferChance
-10.0, // fDispAttacking -10.0f, // fDispAttacking
-1.0, // fDispBargainFailMod -1.0f, // fDispBargainFailMod
1.0, // fDispBargainSuccessMod 1.0f, // fDispBargainSuccessMod
0.0, // fDispCrimeMod 0.0f, // fDispCrimeMod
-10.0, // fDispDiseaseMod -10.0f, // fDispDiseaseMod
3.0, // fDispFactionMod 3.0f, // fDispFactionMod
1.0, // fDispFactionRankBase 1.0f, // fDispFactionRankBase
0.5, // fDispFactionRankMult 0.5f, // fDispFactionRankMult
1.0, // fDispositionMod 1.0f, // fDispositionMod
50.0, // fDispPersonalityBase 50.0f, // fDispPersonalityBase
0.5, // fDispPersonalityMult 0.5f, // fDispPersonalityMult
-25.0, // fDispPickPocketMod -25.0f, // fDispPickPocketMod
5.0, // fDispRaceMod 5.0f, // fDispRaceMod
-0.5, // fDispStealing -0.5f, // fDispStealing
-5.0, // fDispWeaponDrawn -5.0f, // fDispWeaponDrawn
0.5, // fEffectCostMult 0.5f, // fEffectCostMult
0.1, // fElementalShieldMult 0.1f, // fElementalShieldMult
3.0, // fEnchantmentChanceMult 3.0f, // fEnchantmentChanceMult
0.5, // fEnchantmentConstantChanceMult 0.5f, // fEnchantmentConstantChanceMult
100.0, // fEnchantmentConstantDurationMult 100.0f, // fEnchantmentConstantDurationMult
0.1, // fEnchantmentMult 0.1f, // fEnchantmentMult
1000.0, // fEnchantmentValueMult 1000.0f, // fEnchantmentValueMult
0.3, // fEncumberedMoveEffect 0.3f, // fEncumberedMoveEffect
5.0, // fEncumbranceStrMult 5.0f, // fEncumbranceStrMult
0.04, // fEndFatigueMult 0.04f, // fEndFatigueMult
0.25, // fFallAcroBase 0.25f, // fFallAcroBase
0.01, // fFallAcroMult 0.01f, // fFallAcroMult
400.0, // fFallDamageDistanceMin 400.0f, // fFallDamageDistanceMin
0.0, // fFallDistanceBase 0.0f, // fFallDistanceBase
0.07, // fFallDistanceMult 0.07f, // fFallDistanceMult
2.0, // fFatigueAttackBase 2.0f, // fFatigueAttackBase
0.0, // fFatigueAttackMult 0.0f, // fFatigueAttackMult
1.25, // fFatigueBase 1.25f, // fFatigueBase
4.0, // fFatigueBlockBase 4.0f, // fFatigueBlockBase
0.0, // fFatigueBlockMult 0.0f, // fFatigueBlockMult
5.0, // fFatigueJumpBase 5.0f, // fFatigueJumpBase
0.0, // fFatigueJumpMult 0.0f, // fFatigueJumpMult
0.5, // fFatigueMult 0.5f, // fFatigueMult
2.5, // fFatigueReturnBase 2.5f, // fFatigueReturnBase
0.02, // fFatigueReturnMult 0.02f, // fFatigueReturnMult
5.0, // fFatigueRunBase 5.0f, // fFatigueRunBase
2.0, // fFatigueRunMult 2.0f, // fFatigueRunMult
1.5, // fFatigueSneakBase 1.5f, // fFatigueSneakBase
1.5, // fFatigueSneakMult 1.5f, // fFatigueSneakMult
0.0, // fFatigueSpellBase 0.0f, // fFatigueSpellBase
0.0, // fFatigueSpellCostMult 0.0f, // fFatigueSpellCostMult
0.0, // fFatigueSpellMult 0.0f, // fFatigueSpellMult
7.0, // fFatigueSwimRunBase 7.0f, // fFatigueSwimRunBase
0.0, // fFatigueSwimRunMult 0.0f, // fFatigueSwimRunMult
2.5, // fFatigueSwimWalkBase 2.5f, // fFatigueSwimWalkBase
0.0, // fFatigueSwimWalkMult 0.0f, // fFatigueSwimWalkMult
0.2, // fFightDispMult 0.2f, // fFightDispMult
0.005, // fFightDistanceMultiplier 0.005f, // fFightDistanceMultiplier
50.0, // fFightStealing 50.0f, // fFightStealing
3000.0, // fFleeDistance 3000.0f, // fFleeDistance
512.0, // fGreetDistanceReset 512.0f, // fGreetDistanceReset
0.1, // fHandtoHandHealthPer 0.1f, // fHandtoHandHealthPer
1.0, // fHandToHandReach 1.0f, // fHandToHandReach
0.5, // fHoldBreathEndMult 0.5f, // fHoldBreathEndMult
20.0, // fHoldBreathTime 20.0f, // fHoldBreathTime
0.75, // fIdleChanceMultiplier 0.75f, // fIdleChanceMultiplier
1.0, // fIngredientMult 1.0f, // fIngredientMult
0.5, // fInteriorHeadTrackMult 0.5f, // fInteriorHeadTrackMult
128.0, // fJumpAcrobaticsBase 128.0f, // fJumpAcrobaticsBase
4.0, // fJumpAcroMultiplier 4.0f, // fJumpAcroMultiplier
0.5, // fJumpEncumbranceBase 0.5f, // fJumpEncumbranceBase
1.0, // fJumpEncumbranceMultiplier 1.0f, // fJumpEncumbranceMultiplier
0.5, // fJumpMoveBase 0.5f, // fJumpMoveBase
0.5, // fJumpMoveMult 0.5f, // fJumpMoveMult
1.0, // fJumpRunMultiplier 1.0f, // fJumpRunMultiplier
0.5, // fKnockDownMult 0.5f, // fKnockDownMult
5.0, // fLevelMod 5.0f, // fLevelMod
0.1, // fLevelUpHealthEndMult 0.1f, // fLevelUpHealthEndMult
0.6, // fLightMaxMod 0.6f, // fLightMaxMod
10.0, // fLuckMod 10.0f, // fLuckMod
10.0, // fMagesGuildTravel 10.0f, // fMagesGuildTravel
1.5, // fMagicCreatureCastDelay 1.5f, // fMagicCreatureCastDelay
0.0167, // fMagicDetectRefreshRate 0.0167f, // fMagicDetectRefreshRate
1.0, // fMagicItemConstantMult 1.0f, // fMagicItemConstantMult
1.0, // fMagicItemCostMult 1.0f, // fMagicItemCostMult
1.0, // fMagicItemOnceMult 1.0f, // fMagicItemOnceMult
1.0, // fMagicItemPriceMult 1.0f, // fMagicItemPriceMult
0.05, // fMagicItemRechargePerSecond 0.05f, // fMagicItemRechargePerSecond
1.0, // fMagicItemStrikeMult 1.0f, // fMagicItemStrikeMult
1.0, // fMagicItemUsedMult 1.0f, // fMagicItemUsedMult
3.0, // fMagicStartIconBlink 3.0f, // fMagicStartIconBlink
0.5, // fMagicSunBlockedMult 0.5f, // fMagicSunBlockedMult
0.75, // fMajorSkillBonus 0.75f, // fMajorSkillBonus
300.0, // fMaxFlySpeed 300.0f, // fMaxFlySpeed
0.5, // fMaxHandToHandMult 0.5f, // fMaxHandToHandMult
400.0, // fMaxHeadTrackDistance 400.0f, // fMaxHeadTrackDistance
200.0, // fMaxWalkSpeed 200.0f, // fMaxWalkSpeed
300.0, // fMaxWalkSpeedCreature 300.0f, // fMaxWalkSpeedCreature
0.9, // fMedMaxMod 0.9f, // fMedMaxMod
0.1, // fMessageTimePerChar 0.1f, // fMessageTimePerChar
5.0, // fMinFlySpeed 5.0f, // fMinFlySpeed
0.1, // fMinHandToHandMult 0.1f, // fMinHandToHandMult
1.0, // fMinorSkillBonus 1.0f, // fMinorSkillBonus
100.0, // fMinWalkSpeed 100.0f, // fMinWalkSpeed
5.0, // fMinWalkSpeedCreature 5.0f, // fMinWalkSpeedCreature
1.25, // fMiscSkillBonus 1.25f, // fMiscSkillBonus
2.0, // fNPCbaseMagickaMult 2.0f, // fNPCbaseMagickaMult
0.5, // fNPCHealthBarFade 0.5f, // fNPCHealthBarFade
3.0, // fNPCHealthBarTime 3.0f, // fNPCHealthBarTime
1.0, // fPCbaseMagickaMult 1.0f, // fPCbaseMagickaMult
0.3, // fPerDieRollMult 0.3f, // fPerDieRollMult
5.0, // fPersonalityMod 5.0f, // fPersonalityMod
1.0, // fPerTempMult 1.0f, // fPerTempMult
-1.0, // fPickLockMult -1.0f, // fPickLockMult
0.3, // fPickPocketMod 0.3f, // fPickPocketMod
20.0, // fPotionMinUsefulDuration 20.0f, // fPotionMinUsefulDuration
0.5, // fPotionStrengthMult 0.5f, // fPotionStrengthMult
0.5, // fPotionT1DurMult 0.5f, // fPotionT1DurMult
1.5, // fPotionT1MagMult 1.5f, // fPotionT1MagMult
20.0, // fPotionT4BaseStrengthMult 20.0f, // fPotionT4BaseStrengthMult
12.0, // fPotionT4EquipStrengthMult 12.0f, // fPotionT4EquipStrengthMult
3000.0, // fProjectileMaxSpeed 3000.0f, // fProjectileMaxSpeed
400.0, // fProjectileMinSpeed 400.0f, // fProjectileMinSpeed
25.0, // fProjectileThrownStoreChance 25.0f, // fProjectileThrownStoreChance
3.0, // fRepairAmountMult 3.0f, // fRepairAmountMult
1.0, // fRepairMult 1.0f, // fRepairMult
1.0, // fReputationMod 1.0f, // fReputationMod
0.15, // fRestMagicMult 0.15f, // fRestMagicMult
0.0, // fSeriousWoundMult 0.0f, // fSeriousWoundMult
0.25, // fSleepRandMod 0.25f, // fSleepRandMod
0.3, // fSleepRestMod 0.3f, // fSleepRestMod
-1.0, // fSneakBootMult -1.0f, // fSneakBootMult
0.5, // fSneakDistanceBase 0.5f, // fSneakDistanceBase
0.002, // fSneakDistanceMultiplier 0.002f, // fSneakDistanceMultiplier
0.5, // fSneakNoViewMult 0.5f, // fSneakNoViewMult
1.0, // fSneakSkillMult 1.0f, // fSneakSkillMult
0.75, // fSneakSpeedMultiplier 0.75f, // fSneakSpeedMultiplier
1.0, // fSneakUseDelay 1.0f, // fSneakUseDelay
500.0, // fSneakUseDist 500.0f, // fSneakUseDist
1.5, // fSneakViewMult 1.5f, // fSneakViewMult
3.0, // fSoulGemMult 3.0f, // fSoulGemMult
0.8, // fSpecialSkillBonus 0.8f, // fSpecialSkillBonus
7.0, // fSpellMakingValueMult 7.0f, // fSpellMakingValueMult
2.0, // fSpellPriceMult 2.0f, // fSpellPriceMult
10.0, // fSpellValueMult 10.0f, // fSpellValueMult
0.25, // fStromWalkMult 0.25f, // fStromWalkMult
0.7, // fStromWindSpeed 0.7f, // fStromWindSpeed
3.0, // fSuffocationDamage 3.0f, // fSuffocationDamage
0.9, // fSwimHeightScale 0.9f, // fSwimHeightScale
0.1, // fSwimRunAthleticsMult 0.1f, // fSwimRunAthleticsMult
0.5, // fSwimRunBase 0.5f, // fSwimRunBase
0.02, // fSwimWalkAthleticsMult 0.02f, // fSwimWalkAthleticsMult
0.5, // fSwimWalkBase 0.5f, // fSwimWalkBase
1.0, // fSwingBlockBase 1.0f, // fSwingBlockBase
1.0, // fSwingBlockMult 1.0f, // fSwingBlockMult
1000.0, // fTargetSpellMaxSpeed 1000.0f, // fTargetSpellMaxSpeed
1000.0, // fThrownWeaponMaxSpeed 1000.0f, // fThrownWeaponMaxSpeed
300.0, // fThrownWeaponMinSpeed 300.0f, // fThrownWeaponMinSpeed
0.0, // fTrapCostMult 0.0f, // fTrapCostMult
4000.0, // fTravelMult 4000.0f, // fTravelMult
16000.0,// fTravelTimeMult 16000.0f,// fTravelTimeMult
0.1, // fUnarmoredBase1 0.1f, // fUnarmoredBase1
0.065, // fUnarmoredBase2 0.065f, // fUnarmoredBase2
30.0, // fVanityDelay 30.0f, // fVanityDelay
10.0, // fVoiceIdleOdds 10.0f, // fVoiceIdleOdds
0.0, // fWaterReflectUpdateAlways 0.0f, // fWaterReflectUpdateAlways
10.0, // fWaterReflectUpdateSeldom 10.0f, // fWaterReflectUpdateSeldom
0.1, // fWeaponDamageMult 0.1f, // fWeaponDamageMult
1.0, // fWeaponFatigueBlockMult 1.0f, // fWeaponFatigueBlockMult
0.25, // fWeaponFatigueMult 0.25f, // fWeaponFatigueMult
150.0, // fWereWolfAcrobatics 150.0f, // fWereWolfAcrobatics
150.0, // fWereWolfAgility 150.0f, // fWereWolfAgility
1.0, // fWereWolfAlchemy 1.0f, // fWereWolfAlchemy
1.0, // fWereWolfAlteration 1.0f, // fWereWolfAlteration
1.0, // fWereWolfArmorer 1.0f, // fWereWolfArmorer
150.0, // fWereWolfAthletics 150.0f, // fWereWolfAthletics
1.0, // fWereWolfAxe 1.0f, // fWereWolfAxe
1.0, // fWereWolfBlock 1.0f, // fWereWolfBlock
1.0, // fWereWolfBluntWeapon 1.0f, // fWereWolfBluntWeapon
1.0, // fWereWolfConjuration 1.0f, // fWereWolfConjuration
1.0, // fWereWolfDestruction 1.0f, // fWereWolfDestruction
1.0, // fWereWolfEnchant 1.0f, // fWereWolfEnchant
150.0, // fWereWolfEndurance 150.0f, // fWereWolfEndurance
400.0, // fWereWolfFatigue 400.0f, // fWereWolfFatigue
100.0, // fWereWolfHandtoHand 100.0f, // fWereWolfHandtoHand
2.0, // fWereWolfHealth 2.0f, // fWereWolfHealth
1.0, // fWereWolfHeavyArmor 1.0f, // fWereWolfHeavyArmor
1.0, // fWereWolfIllusion 1.0f, // fWereWolfIllusion
1.0, // fWereWolfIntellegence 1.0f, // fWereWolfIntellegence
1.0, // fWereWolfLightArmor 1.0f, // fWereWolfLightArmor
1.0, // fWereWolfLongBlade 1.0f, // fWereWolfLongBlade
1.0, // fWereWolfLuck 1.0f, // fWereWolfLuck
100.0, // fWereWolfMagicka 100.0f, // fWereWolfMagicka
1.0, // fWereWolfMarksman 1.0f, // fWereWolfMarksman
1.0, // fWereWolfMediumArmor 1.0f, // fWereWolfMediumArmor
1.0, // fWereWolfMerchantile 1.0f, // fWereWolfMerchantile
1.0, // fWereWolfMysticism 1.0f, // fWereWolfMysticism
1.0, // fWereWolfPersonality 1.0f, // fWereWolfPersonality
1.0, // fWereWolfRestoration 1.0f, // fWereWolfRestoration
1.5, // fWereWolfRunMult 1.5f, // fWereWolfRunMult
1.0, // fWereWolfSecurity 1.0f, // fWereWolfSecurity
1.0, // fWereWolfShortBlade 1.0f, // fWereWolfShortBlade
1.5, // fWereWolfSilverWeaponDamageMult 1.5f, // fWereWolfSilverWeaponDamageMult
1.0, // fWereWolfSneak 1.0f, // fWereWolfSneak
1.0, // fWereWolfSpear 1.0f, // fWereWolfSpear
1.0, // fWereWolfSpeechcraft 1.0f, // fWereWolfSpeechcraft
150.0, // fWereWolfSpeed 150.0f, // fWereWolfSpeed
150.0, // fWereWolfStrength 150.0f, // fWereWolfStrength
100.0, // fWereWolfUnarmored 100.0f, // fWereWolfUnarmored
1.0, // fWereWolfWillPower 1.0f, // fWereWolfWillPower
15.0, // fWortChanceValue 15.0f, // fWortChanceValue
}; };
static const char *gmstIntegers[] = static const char *gmstIntegers[] =
@ -2091,10 +2092,10 @@ void CSMDoc::Document::addOptionalGmst (const ESM::GameSetting& gmst)
{ {
if (getData().getGmsts().searchId (gmst.mId)==-1) if (getData().getGmsts().searchId (gmst.mId)==-1)
{ {
CSMWorld::Record<ESM::GameSetting> record; std::unique_ptr<CSMWorld::Record<ESM::GameSetting> > record(new CSMWorld::Record<ESM::GameSetting>);
record.mBase = gmst; record->mBase = gmst;
record.mState = CSMWorld::RecordBase::State_BaseOnly; record->mState = CSMWorld::RecordBase::State_BaseOnly;
getData().getGmsts().appendRecord (record); getData().getGmsts().appendRecord (std::move(record));
} }
} }
@ -2102,10 +2103,10 @@ void CSMDoc::Document::addOptionalGlobal (const ESM::Global& global)
{ {
if (getData().getGlobals().searchId (global.mId)==-1) if (getData().getGlobals().searchId (global.mId)==-1)
{ {
CSMWorld::Record<ESM::Global> record; std::unique_ptr<CSMWorld::Record<ESM::Global> > record(new CSMWorld::Record<ESM::Global>);
record.mBase = global; record->mBase = global;
record.mState = CSMWorld::RecordBase::State_BaseOnly; record->mState = CSMWorld::RecordBase::State_BaseOnly;
getData().getGlobals().appendRecord (record); getData().getGlobals().appendRecord (std::move(record));
} }
} }
@ -2113,10 +2114,10 @@ void CSMDoc::Document::addOptionalMagicEffect (const ESM::MagicEffect& magicEffe
{ {
if (getData().getMagicEffects().searchId (magicEffect.mId)==-1) if (getData().getMagicEffects().searchId (magicEffect.mId)==-1)
{ {
CSMWorld::Record<ESM::MagicEffect> record; std::unique_ptr<CSMWorld::Record<ESM::MagicEffect> > record(new CSMWorld::Record<ESM::MagicEffect>);
record.mBase = magicEffect; record->mBase = magicEffect;
record.mState = CSMWorld::RecordBase::State_BaseOnly; record->mState = CSMWorld::RecordBase::State_BaseOnly;
getData().getMagicEffects().appendRecord (record); getData().getMagicEffects().appendRecord (std::move(record));
} }
} }

View File

@ -64,11 +64,11 @@ void CSMDoc::Loader::load()
CSMWorld::UniversalId log (CSMWorld::UniversalId::Type_LoadErrorLog, 0); CSMWorld::UniversalId log (CSMWorld::UniversalId::Type_LoadErrorLog, 0);
{ // silence a g++ warning { // silence a g++ warning
for (CSMDoc::Messages::Iterator iter (messages.begin()); for (CSMDoc::Messages::Iterator iter2 (messages.begin());
iter!=messages.end(); ++iter) iter2!=messages.end(); ++iter2)
{ {
document->getReport (log)->add (*iter); document->getReport (log)->add (*iter2);
emit loadMessage (document, iter->mMessage); emit loadMessage (document, iter2->mMessage);
} }
} }
@ -77,19 +77,19 @@ void CSMDoc::Loader::load()
return; return;
} }
if (iter->second.mFile<size) if (iter->second.mFile<size) // start loading the files
{ {
boost::filesystem::path path = document->getContentFiles()[iter->second.mFile]; boost::filesystem::path path = document->getContentFiles()[iter->second.mFile];
int steps = document->getData().startLoading (path, iter->second.mFile!=editedIndex, false); int steps = document->getData().startLoading (path, iter->second.mFile!=editedIndex, /*project*/false);
iter->second.mRecordsLeft = true; iter->second.mRecordsLeft = true;
iter->second.mRecordsLoaded = 0; iter->second.mRecordsLoaded = 0;
emit nextStage (document, path.filename().string(), steps); emit nextStage (document, path.filename().string(), steps);
} }
else if (iter->second.mFile==size) else if (iter->second.mFile==size) // start loading the last (project) file
{ {
int steps = document->getData().startLoading (document->getProjectPath(), false, true); int steps = document->getData().startLoading (document->getProjectPath(), /*base*/false, true);
iter->second.mRecordsLeft = true; iter->second.mRecordsLeft = true;
iter->second.mRecordsLoaded = 0; iter->second.mRecordsLoaded = 0;

View File

@ -118,7 +118,7 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, Messages& message
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter) for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter)
{ {
if (iter->isModified() || iter->mState == CSMWorld::RecordBase::State_Deleted) if ((*iter)->isModified() || (*iter)->mState == CSMWorld::RecordBase::State_Deleted)
{ {
infoModified = true; infoModified = true;
break; break;
@ -144,9 +144,9 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, Messages& message
// write modified selected info records // write modified selected info records
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter) for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter)
{ {
if (iter->isModified() || iter->mState == CSMWorld::RecordBase::State_Deleted) if ((*iter)->isModified() || (*iter)->mState == CSMWorld::RecordBase::State_Deleted)
{ {
ESM::DialInfo info = iter->get(); ESM::DialInfo info = (*iter)->get();
info.mId = info.mId.substr (info.mId.find_last_of ('#')+1); info.mId = info.mId.substr (info.mId.find_last_of ('#')+1);
info.mPrev = ""; info.mPrev = "";
@ -155,7 +155,7 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, Messages& message
CSMWorld::InfoCollection::RecordConstIterator prev = iter; CSMWorld::InfoCollection::RecordConstIterator prev = iter;
--prev; --prev;
info.mPrev = prev->get().mId.substr (prev->get().mId.find_last_of ('#')+1); info.mPrev = (*prev)->get().mId.substr ((*prev)->get().mId.find_last_of ('#')+1);
} }
CSMWorld::InfoCollection::RecordConstIterator next = iter; CSMWorld::InfoCollection::RecordConstIterator next = iter;
@ -164,11 +164,11 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, Messages& message
info.mNext = ""; info.mNext = "";
if (next!=range.second) if (next!=range.second)
{ {
info.mNext = next->get().mId.substr (next->get().mId.find_last_of ('#')+1); info.mNext = (*next)->get().mId.substr ((*next)->get().mId.find_last_of ('#')+1);
} }
writer.startRecord (info.sRecordId); writer.startRecord (info.sRecordId);
info.save (writer, iter->mState == CSMWorld::RecordBase::State_Deleted); info.save (writer, (*iter)->mState == CSMWorld::RecordBase::State_Deleted);
writer.endRecord (info.sRecordId); writer.endRecord (info.sRecordId);
} }
} }

View File

@ -313,15 +313,15 @@ boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseNAry (const Token& ke
nodes.push_back (node); nodes.push_back (node);
Token token = getNextToken(); Token token2 = getNextToken();
if (!token || (token.mType!=Token::Type_Close && token.mType!=Token::Type_Comma)) if (!token2 || (token2.mType!=Token::Type_Close && token2.mType!=Token::Type_Comma))
{ {
error(); error();
return boost::shared_ptr<Node>(); return boost::shared_ptr<Node>();
} }
if (token.mType==Token::Type_Close) if (token2.mType==Token::Type_Close)
break; break;
} }

View File

@ -100,10 +100,9 @@ void CSMTools::MergeReferencesStage::perform (int stage, CSMDoc::Messages& messa
ref.mRefNum.mIndex = mIndex[Misc::StringUtils::lowerCase (ref.mCell)]++; ref.mRefNum.mIndex = mIndex[Misc::StringUtils::lowerCase (ref.mCell)]++;
ref.mRefNum.mContentFile = 0; ref.mRefNum.mContentFile = 0;
CSMWorld::Record<CSMWorld::CellRef> newRecord ( mState.mTarget->getData().getReferences().appendRecord (
CSMWorld::RecordBase::State_ModifiedOnly, 0, &ref); std::make_unique<CSMWorld::Record<CSMWorld::CellRef> >(
CSMWorld::Record<CSMWorld::CellRef>(CSMWorld::RecordBase::State_ModifiedOnly, 0, &ref)));
mState.mTarget->getData().getReferences().appendRecord (newRecord);
} }
} }
@ -183,16 +182,15 @@ void CSMTools::MergeLandTexturesStage::perform (int stage, CSMDoc::Messages& mes
CSMWorld::LandTexture texture = CSMWorld::LandTexture texture =
mState.mSource.getData().getLandTextures().getRecord (index).get(); mState.mSource.getData().getLandTextures().getRecord (index).get();
std::ostringstream stream; std::ostringstream stream2;
stream << mNext->second-1 << "_0"; stream2 << mNext->second-1 << "_0";
texture.mIndex = mNext->second-1; texture.mIndex = mNext->second-1;
texture.mId = stream.str(); texture.mId = stream2.str();
CSMWorld::Record<CSMWorld::LandTexture> newRecord ( mState.mTarget->getData().getLandTextures().appendRecord (
CSMWorld::RecordBase::State_ModifiedOnly, 0, &texture); std::make_unique<CSMWorld::Record<CSMWorld::LandTexture> >(
CSMWorld::Record<CSMWorld::LandTexture>(CSMWorld::RecordBase::State_ModifiedOnly, 0, &texture)));
mState.mTarget->getData().getLandTextures().appendRecord (newRecord);
found = true; found = true;
} }
@ -250,9 +248,8 @@ void CSMTools::MergeLandStage::perform (int stage, CSMDoc::Messages& messages)
} }
} }
CSMWorld::Record<CSMWorld::Land> newRecord ( mState.mTarget->getData().getLand().appendRecord (
CSMWorld::RecordBase::State_ModifiedOnly, 0, &newLand); std::make_unique<CSMWorld::Record<CSMWorld::Land> >(
CSMWorld::Record<CSMWorld::Land>(CSMWorld::RecordBase::State_ModifiedOnly, 0, &newLand)));
mState.mTarget->getData().getLand().appendRecord (newRecord);
} }
} }

View File

@ -3,6 +3,7 @@
#include <algorithm> #include <algorithm>
#include <map> #include <map>
#include <memory>
#include <components/to_utf8/to_utf8.hpp> #include <components/to_utf8/to_utf8.hpp>
@ -82,7 +83,8 @@ namespace CSMTools
const CSMWorld::Record<RecordType>& record = source.getRecord (stage); const CSMWorld::Record<RecordType>& record = source.getRecord (stage);
if (!record.isDeleted()) if (!record.isDeleted())
target.appendRecord (CSMWorld::Record<RecordType> (CSMWorld::RecordBase::State_ModifiedOnly, 0, &record.get())); target.appendRecord (std::make_unique<CSMWorld::Record<RecordType> >(
CSMWorld::Record<RecordType>(CSMWorld::RecordBase::State_ModifiedOnly, 0, &record.get())));
} }
class MergeRefIdsStage : public CSMDoc::Stage class MergeRefIdsStage : public CSMDoc::Stage

View File

@ -7,6 +7,7 @@
#include <cctype> #include <cctype>
#include <stdexcept> #include <stdexcept>
#include <functional> #include <functional>
#include <memory>
#include <QVariant> #include <QVariant>
@ -49,7 +50,7 @@ namespace CSMWorld
private: private:
std::vector<Record<ESXRecordT> > mRecords; std::vector<std::unique_ptr<Record<ESXRecordT> > > mRecords;
std::map<std::string, int> mIndex; std::map<std::string, int> mIndex;
std::vector<Column<ESXRecordT> *> mColumns; std::vector<Column<ESXRecordT> *> mColumns;
@ -59,9 +60,7 @@ namespace CSMWorld
protected: protected:
const std::map<std::string, int>& getIdMap() const; const std::vector<std::unique_ptr<Record<ESXRecordT> > >& getRecords() const;
const std::vector<Record<ESXRecordT> >& getRecords() const;
bool reorderRowsImp (int baseIndex, const std::vector<int>& newOrder); bool reorderRowsImp (int baseIndex, const std::vector<int>& newOrder);
///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices
@ -112,12 +111,12 @@ namespace CSMWorld
////< Search record with \a id. ////< Search record with \a id.
/// \return index of record (if found) or -1 (not found) /// \return index of record (if found) or -1 (not found)
virtual void replace (int index, const RecordBase& record); virtual void replace (int index, std::unique_ptr<RecordBase> record);
///< If the record type does not match, an exception is thrown. ///< If the record type does not match, an exception is thrown.
/// ///
/// \attention \a record must not change the ID. /// \attention \a record must not change the ID.
virtual void appendRecord (const RecordBase& record, virtual void appendRecord (std::unique_ptr<RecordBase> record,
UniversalId::Type type = UniversalId::Type_None); UniversalId::Type type = UniversalId::Type_None);
///< If the record type does not match, an exception is thrown. ///< If the record type does not match, an exception is thrown.
///< \param type Will be ignored, unless the collection supports multiple record types ///< \param type Will be ignored, unless the collection supports multiple record types
@ -135,7 +134,7 @@ namespace CSMWorld
/// ///
/// \param listDeleted include deleted record in the list /// \param listDeleted include deleted record in the list
virtual void insertRecord (const RecordBase& record, int index, virtual void insertRecord (std::unique_ptr<RecordBase> record, int index,
UniversalId::Type type = UniversalId::Type_None); UniversalId::Type type = UniversalId::Type_None);
///< Insert record before index. ///< Insert record before index.
/// ///
@ -152,20 +151,14 @@ namespace CSMWorld
void addColumn (Column<ESXRecordT> *column); void addColumn (Column<ESXRecordT> *column);
void setRecord (int index, const Record<ESXRecordT>& record); void setRecord (int index, std::unique_ptr<Record<ESXRecordT> > record);
///< \attention This function must not change the ID. ///< \attention This function must not change the ID.
NestableColumn *getNestableColumn (int column) const; NestableColumn *getNestableColumn (int column) const;
}; };
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
const std::map<std::string, int>& Collection<ESXRecordT, IdAccessorT>::getIdMap() const const std::vector<std::unique_ptr<Record<ESXRecordT> > >& Collection<ESXRecordT, IdAccessorT>::getRecords() const
{
return mIndex;
}
template<typename ESXRecordT, typename IdAccessorT>
const std::vector<Record<ESXRecordT> >& Collection<ESXRecordT, IdAccessorT>::getRecords() const
{ {
return mRecords; return mRecords;
} }
@ -185,15 +178,15 @@ namespace CSMWorld
return false; return false;
// reorder records // reorder records
std::vector<Record<ESXRecordT> > buffer (size); std::vector<std::unique_ptr<Record<ESXRecordT> > > buffer (size);
for (int i=0; i<size; ++i) for (int i=0; i<size; ++i)
{ {
buffer[newOrder[i]] = mRecords [baseIndex+i]; buffer[newOrder[i]] = std::move(mRecords [baseIndex+i]);
buffer[newOrder[i]].setModified (buffer[newOrder[i]].get()); buffer[newOrder[i]]->setModified (buffer[newOrder[i]]->get());
} }
std::copy (buffer.begin(), buffer.end(), mRecords.begin()+baseIndex); std::move (buffer.begin(), buffer.end(), mRecords.begin()+baseIndex);
// adjust index // adjust index
for (std::map<std::string, int>::iterator iter (mIndex.begin()); iter!=mIndex.end(); for (std::map<std::string, int>::iterator iter (mIndex.begin()); iter!=mIndex.end();
@ -210,12 +203,12 @@ namespace CSMWorld
const std::string& destination, const std::string& destination,
const UniversalId::Type type) const UniversalId::Type type)
{ {
Record<ESXRecordT> copy; std::unique_ptr<Record<ESXRecordT> > copy(new Record<ESXRecordT>);
copy.mModified = getRecord(origin).get(); copy->mModified = getRecord(origin).get();
copy.mState = RecordBase::State_ModifiedOnly; copy->mState = RecordBase::State_ModifiedOnly;
copy.get().mId = destination; copy->get().mId = destination;
insertRecord(copy, getAppendIndex(destination, type)); insertRecord(std::move(copy), getAppendIndex(destination, type));
} }
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
@ -238,15 +231,15 @@ namespace CSMWorld
if (iter==mIndex.end()) if (iter==mIndex.end())
{ {
Record<ESXRecordT> record2; std::unique_ptr<Record<ESXRecordT> > record2(new Record<ESXRecordT>);
record2.mState = Record<ESXRecordT>::State_ModifiedOnly; record2->mState = Record<ESXRecordT>::State_ModifiedOnly;
record2.mModified = record; record2->mModified = record;
insertRecord (record2, getAppendIndex (id)); insertRecord (std::move(record2), getAppendIndex (id));
} }
else else
{ {
mRecords[iter->second].setModified (record); mRecords[iter->second]->setModified (record);
} }
} }
@ -259,7 +252,7 @@ namespace CSMWorld
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
std::string Collection<ESXRecordT, IdAccessorT>::getId (int index) const std::string Collection<ESXRecordT, IdAccessorT>::getId (int index) const
{ {
return IdAccessorT().getId (mRecords.at (index).get()); return IdAccessorT().getId (mRecords.at (index)->get());
} }
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
@ -282,13 +275,13 @@ namespace CSMWorld
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
QVariant Collection<ESXRecordT, IdAccessorT>::getData (int index, int column) const QVariant Collection<ESXRecordT, IdAccessorT>::getData (int index, int column) const
{ {
return mColumns.at (column)->get (mRecords.at (index)); return mColumns.at (column)->get (*mRecords.at (index));
} }
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::setData (int index, int column, const QVariant& data) void Collection<ESXRecordT, IdAccessorT>::setData (int index, int column, const QVariant& data)
{ {
return mColumns.at (column)->set (mRecords.at (index), data); return mColumns.at (column)->set (*mRecords.at (index), data);
} }
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
@ -315,8 +308,8 @@ namespace CSMWorld
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::merge() void Collection<ESXRecordT, IdAccessorT>::merge()
{ {
for (typename std::vector<Record<ESXRecordT> >::iterator iter (mRecords.begin()); iter!=mRecords.end(); ++iter) for (typename std::vector<std::unique_ptr<Record<ESXRecordT> > >::iterator iter (mRecords.begin()); iter!=mRecords.end(); ++iter)
iter->merge(); (*iter)->merge();
purge(); purge();
} }
@ -328,7 +321,7 @@ namespace CSMWorld
while (i<static_cast<int> (mRecords.size())) while (i<static_cast<int> (mRecords.size()))
{ {
if (mRecords[i].isErased()) if (mRecords[i]->isErased())
removeRows (i, 1); removeRows (i, 1);
else else
++i; ++i;
@ -369,11 +362,11 @@ namespace CSMWorld
IdAccessorT().getId (record) = id; IdAccessorT().getId (record) = id;
record.blank(); record.blank();
Record<ESXRecordT> record2; std::unique_ptr<Record<ESXRecordT> > record2(new Record<ESXRecordT>);
record2.mState = Record<ESXRecordT>::State_ModifiedOnly; record2->mState = Record<ESXRecordT>::State_ModifiedOnly;
record2.mModified = record; record2->mModified = record;
insertRecord (record2, getAppendIndex (id, type), type); insertRecord (std::move(record2), getAppendIndex (id, type), type);
} }
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
@ -390,18 +383,19 @@ namespace CSMWorld
} }
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::replace (int index, const RecordBase& record) void Collection<ESXRecordT, IdAccessorT>::replace (int index, std::unique_ptr<RecordBase> record)
{ {
mRecords.at (index) = dynamic_cast<const Record<ESXRecordT>&> (record); std::unique_ptr<Record<ESXRecordT> > tmp(static_cast<Record<ESXRecordT>*>(record.release()));
mRecords.at (index) = std::move(tmp);
} }
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::appendRecord (const RecordBase& record, void Collection<ESXRecordT, IdAccessorT>::appendRecord (std::unique_ptr<RecordBase> record,
UniversalId::Type type) UniversalId::Type type)
{ {
insertRecord (record, int index =
getAppendIndex (IdAccessorT().getId ( getAppendIndex(IdAccessorT().getId(static_cast<Record<ESXRecordT>*>(record.get())->get()), type);
dynamic_cast<const Record<ESXRecordT>&> (record).get()), type), type); insertRecord (std::move(record), index, type);
} }
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
@ -419,8 +413,8 @@ namespace CSMWorld
for (typename std::map<std::string, int>::const_iterator iter = mIndex.begin(); for (typename std::map<std::string, int>::const_iterator iter = mIndex.begin();
iter!=mIndex.end(); ++iter) iter!=mIndex.end(); ++iter)
{ {
if (listDeleted || !mRecords[iter->second].isDeleted()) if (listDeleted || !mRecords[iter->second]->isDeleted())
ids.push_back (IdAccessorT().getId (mRecords[iter->second].get())); ids.push_back (IdAccessorT().getId (mRecords[iter->second]->get()));
} }
return ids; return ids;
@ -430,46 +424,52 @@ namespace CSMWorld
const Record<ESXRecordT>& Collection<ESXRecordT, IdAccessorT>::getRecord (const std::string& id) const const Record<ESXRecordT>& Collection<ESXRecordT, IdAccessorT>::getRecord (const std::string& id) const
{ {
int index = getIndex (id); int index = getIndex (id);
return mRecords.at (index); return *mRecords.at (index);
} }
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
const Record<ESXRecordT>& Collection<ESXRecordT, IdAccessorT>::getRecord (int index) const const Record<ESXRecordT>& Collection<ESXRecordT, IdAccessorT>::getRecord (int index) const
{ {
return mRecords.at (index); return *mRecords.at (index);
} }
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::insertRecord (const RecordBase& record, int index, void Collection<ESXRecordT, IdAccessorT>::insertRecord (std::unique_ptr<RecordBase> record, int index,
UniversalId::Type type) UniversalId::Type type)
{ {
if (index<0 || index>static_cast<int> (mRecords.size())) int size = static_cast<int>(mRecords.size());
if (index < 0 || index > size)
throw std::runtime_error ("index out of range"); throw std::runtime_error ("index out of range");
const Record<ESXRecordT>& record2 = dynamic_cast<const Record<ESXRecordT>&> (record); std::unique_ptr<Record<ESXRecordT> > record2(static_cast<Record<ESXRecordT>*>(record.release()));
std::string lowerId = Misc::StringUtils::lowerCase(IdAccessorT().getId(record2->get()));
mRecords.insert (mRecords.begin()+index, record2); if (index == size)
mRecords.push_back (std::move(record2));
else
mRecords.insert (mRecords.begin()+index, std::move(record2));
if (index<static_cast<int> (mRecords.size())-1) if (index < size-1)
{ {
for (std::map<std::string, int>::iterator iter (mIndex.begin()); iter!=mIndex.end(); for (std::map<std::string, int>::iterator iter (mIndex.begin()); iter!=mIndex.end(); ++iter)
++iter) {
if (iter->second>=index) if (iter->second >= index)
++(iter->second); ++(iter->second);
}
} }
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (IdAccessorT().getId ( mIndex.insert (std::make_pair (lowerId, index));
record2.get())), index));
} }
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::setRecord (int index, const Record<ESXRecordT>& record) void Collection<ESXRecordT, IdAccessorT>::setRecord (int index,
std::unique_ptr<Record<ESXRecordT> > record)
{ {
if (Misc::StringUtils::lowerCase (IdAccessorT().getId (mRecords.at (index).get()))!= if (Misc::StringUtils::lowerCase (IdAccessorT().getId (mRecords.at (index)->get())) !=
Misc::StringUtils::lowerCase (IdAccessorT().getId (record.get()))) Misc::StringUtils::lowerCase (IdAccessorT().getId (record->get())))
throw std::runtime_error ("attempt to change the ID of a record"); throw std::runtime_error ("attempt to change the ID of a record");
mRecords.at (index) = record; mRecords.at (index) = std::move(record);
} }
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>

View File

@ -8,6 +8,11 @@ CSMWorld::CollectionBase::CollectionBase() {}
CSMWorld::CollectionBase::~CollectionBase() {} CSMWorld::CollectionBase::~CollectionBase() {}
int CSMWorld::CollectionBase::getInsertIndex (const std::string& id, UniversalId::Type type, RecordBase *record) const
{
return getAppendIndex(id, type);
}
int CSMWorld::CollectionBase::searchColumnIndex (Columns::ColumnId id) const int CSMWorld::CollectionBase::searchColumnIndex (Columns::ColumnId id) const
{ {
int columns = getColumns(); int columns = getColumns();

View File

@ -3,6 +3,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <memory>
#include "universalid.hpp" #include "universalid.hpp"
#include "columns.hpp" #include "columns.hpp"
@ -64,13 +65,13 @@ namespace CSMWorld
////< Search record with \a id. ////< Search record with \a id.
/// \return index of record (if found) or -1 (not found) /// \return index of record (if found) or -1 (not found)
virtual void replace (int index, const RecordBase& record) = 0; virtual void replace (int index, std::unique_ptr<RecordBase> record) = 0;
///< If the record type does not match, an exception is thrown. ///< If the record type does not match, an exception is thrown.
/// ///
/// \attention \a record must not change the ID. /// \attention \a record must not change the ID.
///< \param type Will be ignored, unless the collection supports multiple record types ///< \param type Will be ignored, unless the collection supports multiple record types
virtual void appendRecord (const RecordBase& record, virtual void appendRecord (std::unique_ptr<RecordBase> record,
UniversalId::Type type = UniversalId::Type_None) = 0; UniversalId::Type type = UniversalId::Type_None) = 0;
///< If the record type does not match, an exception is thrown. ///< If the record type does not match, an exception is thrown.
@ -97,6 +98,12 @@ namespace CSMWorld
/// ///
/// \return Success? /// \return Success?
virtual int getInsertIndex (const std::string& id,
UniversalId::Type type = UniversalId::Type_None,
RecordBase *record = nullptr) const;
///< Works like getAppendIndex unless an overloaded method uses the record pointer
/// to get additional info about the record that results in an alternative index.
int searchColumnIndex (Columns::ColumnId id) const; int searchColumnIndex (Columns::ColumnId id) const;
///< Return index of column with the given \a id. If no such column exists, -1 is returned. ///< Return index of column with the given \a id. If no such column exists, -1 is returned.

View File

@ -124,16 +124,15 @@ void CSMWorld::CreateCommand::undo()
} }
CSMWorld::RevertCommand::RevertCommand (IdTable& model, const std::string& id, QUndoCommand* parent) CSMWorld::RevertCommand::RevertCommand (IdTable& model, const std::string& id, QUndoCommand* parent)
: QUndoCommand (parent), mModel (model), mId (id), mOld (0) : QUndoCommand (parent), mModel (model), mId (id)
{ {
setText (("Revert record " + id).c_str()); setText (("Revert record " + id).c_str());
mOld = model.getRecord (id).clone(); mOld = std::move(model.getRecord (id).clone());
} }
CSMWorld::RevertCommand::~RevertCommand() CSMWorld::RevertCommand::~RevertCommand()
{ {
delete mOld;
} }
void CSMWorld::RevertCommand::redo() void CSMWorld::RevertCommand::redo()
@ -145,6 +144,7 @@ void CSMWorld::RevertCommand::redo()
if (state==RecordBase::State_ModifiedOnly) if (state==RecordBase::State_ModifiedOnly)
{ {
mOld = std::move(mModel.getRecord (mId).clone());
mModel.removeRows (index.row(), 1); mModel.removeRows (index.row(), 1);
} }
else else
@ -155,21 +155,20 @@ void CSMWorld::RevertCommand::redo()
void CSMWorld::RevertCommand::undo() void CSMWorld::RevertCommand::undo()
{ {
mModel.setRecord (mId, *mOld); mModel.setRecord (mId, std::move(mOld));
} }
CSMWorld::DeleteCommand::DeleteCommand (IdTable& model, CSMWorld::DeleteCommand::DeleteCommand (IdTable& model,
const std::string& id, CSMWorld::UniversalId::Type type, QUndoCommand* parent) const std::string& id, CSMWorld::UniversalId::Type type, QUndoCommand* parent)
: QUndoCommand (parent), mModel (model), mId (id), mOld (0), mType(type) : QUndoCommand (parent), mModel (model), mId (id), mType(type)
{ {
setText (("Delete record " + id).c_str()); setText (("Delete record " + id).c_str());
mOld = model.getRecord (id).clone(); mOld = std::move(model.getRecord (id).clone());
} }
CSMWorld::DeleteCommand::~DeleteCommand() CSMWorld::DeleteCommand::~DeleteCommand()
{ {
delete mOld;
} }
void CSMWorld::DeleteCommand::redo() void CSMWorld::DeleteCommand::redo()
@ -181,6 +180,7 @@ void CSMWorld::DeleteCommand::redo()
if (state==RecordBase::State_ModifiedOnly) if (state==RecordBase::State_ModifiedOnly)
{ {
mOld = std::move(mModel.getRecord (mId).clone());
mModel.removeRows (index.row(), 1); mModel.removeRows (index.row(), 1);
} }
else else
@ -191,7 +191,7 @@ void CSMWorld::DeleteCommand::redo()
void CSMWorld::DeleteCommand::undo() void CSMWorld::DeleteCommand::undo()
{ {
mModel.setRecord (mId, *mOld, mType); mModel.setRecord (mId, std::move(mOld), mType);
} }

View File

@ -6,6 +6,7 @@
#include <string> #include <string>
#include <map> #include <map>
#include <vector> #include <vector>
#include <memory>
#include <QVariant> #include <QVariant>
#include <QUndoCommand> #include <QUndoCommand>
@ -97,7 +98,7 @@ namespace CSMWorld
{ {
IdTable& mModel; IdTable& mModel;
std::string mId; std::string mId;
RecordBase *mOld; std::unique_ptr<RecordBase> mOld;
// not implemented // not implemented
RevertCommand (const RevertCommand&); RevertCommand (const RevertCommand&);
@ -118,7 +119,7 @@ namespace CSMWorld
{ {
IdTable& mModel; IdTable& mModel;
std::string mId; std::string mId;
RecordBase *mOld; std::unique_ptr<RecordBase> mOld;
UniversalId::Type mType; UniversalId::Type mType;
// not implemented // not implemented

View File

@ -10,10 +10,6 @@
#include <components/esm/loadglob.hpp> #include <components/esm/loadglob.hpp>
#include <components/esm/cellref.hpp> #include <components/esm/cellref.hpp>
#include <components/autocalc/autocalc.hpp>
#include <components/autocalc/autocalcspell.hpp>
#include <components/autocalc/store.hpp>
#include "idtable.hpp" #include "idtable.hpp"
#include "idtree.hpp" #include "idtree.hpp"
#include "columnimp.hpp" #include "columnimp.hpp"
@ -22,71 +18,7 @@
#include "resourcesmanager.hpp" #include "resourcesmanager.hpp"
#include "resourcetable.hpp" #include "resourcetable.hpp"
#include "nestedcoladapterimp.hpp" #include "nestedcoladapterimp.hpp"
#include "npcstats.hpp" #include "npcautocalc.hpp"
namespace
{
class CSStore : public AutoCalc::StoreCommon
{
const CSMWorld::IdCollection<ESM::GameSetting>& mGmstTable;
const CSMWorld::IdCollection<ESM::Skill>& mSkillTable;
const CSMWorld::IdCollection<ESM::MagicEffect>& mMagicEffectTable;
const CSMWorld::NestedIdCollection<ESM::Spell>& mSpells;
public:
CSStore(const CSMWorld::IdCollection<ESM::GameSetting>& gmst,
const CSMWorld::IdCollection<ESM::Skill>& skills,
const CSMWorld::IdCollection<ESM::MagicEffect>& magicEffects,
const CSMWorld::NestedIdCollection<ESM::Spell>& spells)
: mGmstTable(gmst), mSkillTable(skills), mMagicEffectTable(magicEffects), mSpells(spells)
{ }
~CSStore() {}
virtual int findGmstInt(const std::string& name) const
{
return mGmstTable.getRecord(name).get().getInt();
}
virtual float findGmstFloat(const std::string& name) const
{
return mGmstTable.getRecord(name).get().getFloat();
}
virtual const ESM::Skill *findSkill(int index) const
{
// if the skill does not exist, throws std::runtime_error ("invalid ID: " + id)
return &mSkillTable.getRecord(ESM::Skill::indexToId(index)).get();
}
virtual const ESM::MagicEffect* findMagicEffect(int id) const
{
// if the magic effect does not exist, throws std::runtime_error ("invalid ID: " + id)
return &mMagicEffectTable.getRecord(ESM::MagicEffect::indexToId((short)id)).get();
}
virtual void getSpells(std::vector<ESM::Spell*>& spells)
{
// prepare data in a format used by OpenMW store
for (int index = 0; index < mSpells.getSize(); ++index)
spells.push_back(const_cast<ESM::Spell *>(&mSpells.getRecord(index).get()));
}
};
unsigned short autoCalculateMana(AutoCalc::StatsBase& stats)
{
return stats.getBaseAttribute(ESM::Attribute::Intelligence) * 2;
}
unsigned short autoCalculateFatigue(AutoCalc::StatsBase& stats)
{
return stats.getBaseAttribute(ESM::Attribute::Strength)
+ stats.getBaseAttribute(ESM::Attribute::Willpower)
+ stats.getBaseAttribute(ESM::Attribute::Agility)
+ stats.getBaseAttribute(ESM::Attribute::Endurance);
}
}
void CSMWorld::Data::addModel (QAbstractItemModel *model, UniversalId::Type type, bool update) void CSMWorld::Data::addModel (QAbstractItemModel *model, UniversalId::Type type, bool update)
{ {
@ -129,8 +61,9 @@ int CSMWorld::Data::count (RecordBase::State state, const CollectionBase& collec
} }
CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager) CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager)
: mEncoder (encoding), mPathgrids (mCells), mReferenceables(self()), mRefs (mCells), : mEncoder (encoding), mPathgrids (mCells), mReferenceables (self()), mRefs (mCells),
mResourcesManager (resourcesManager), mReader (0), mDialogue (0), mReaderIndex(0) mResourcesManager (resourcesManager), mReader (0), mDialogue (0), mReaderIndex(0),
mNpcAutoCalc (0)
{ {
int index = 0; int index = 0;
@ -621,32 +554,18 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
CSMWorld::IdTree *objects = CSMWorld::IdTree *objects =
static_cast<CSMWorld::IdTree*>(getTableModel(UniversalId::Type_Referenceable)); static_cast<CSMWorld::IdTree*>(getTableModel(UniversalId::Type_Referenceable));
connect (gmsts, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), mNpcAutoCalc = new NpcAutoCalc (self(), gmsts, skills, classes, races, objects);
this, SLOT (gmstDataChanged (const QModelIndex&, const QModelIndex&)));
connect (skills, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
this, SLOT (skillDataChanged (const QModelIndex&, const QModelIndex&)));
connect (classes, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
this, SLOT (classDataChanged (const QModelIndex&, const QModelIndex&)));
connect (races, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
this, SLOT (raceDataChanged (const QModelIndex&, const QModelIndex&)));
connect (objects, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
this, SLOT (npcDataChanged (const QModelIndex&, const QModelIndex&)));
connect (this, SIGNAL (updateNpcAutocalc (int, const std::string&)),
objects, SLOT (updateNpcAutocalc (int, const std::string&)));
connect (this, SIGNAL (cacheNpcStats (const std::string&, NpcStats*)),
this, SLOT (cacheNpcStatsEvent (const std::string&, NpcStats*)));
mRefLoadCache.clear(); // clear here rather than startLoading() and continueLoading() for multiple content files mRefLoadCache.clear(); // clear here rather than startLoading() and continueLoading() for multiple content files
} }
CSMWorld::Data::~Data() CSMWorld::Data::~Data()
{ {
clearNpcStatsCache();
for (std::vector<QAbstractItemModel *>::iterator iter (mModels.begin()); iter!=mModels.end(); ++iter) for (std::vector<QAbstractItemModel *>::iterator iter (mModels.begin()); iter!=mModels.end(); ++iter)
delete *iter; delete *iter;
delete mReader; delete mReader;
delete mNpcAutoCalc;
} }
const CSMWorld::IdCollection<ESM::Global>& CSMWorld::Data::getGlobals() const const CSMWorld::IdCollection<ESM::Global>& CSMWorld::Data::getGlobals() const
@ -942,8 +861,13 @@ const CSMWorld::MetaData& CSMWorld::Data::getMetaData() const
void CSMWorld::Data::setMetaData (const MetaData& metaData) void CSMWorld::Data::setMetaData (const MetaData& metaData)
{ {
Record<MetaData> record (RecordBase::State_ModifiedOnly, 0, &metaData); mMetaData.setRecord (0, std::make_unique<Record<MetaData> >(
mMetaData.setRecord (0, record); Record<MetaData>(RecordBase::State_ModifiedOnly, 0, &metaData)));
}
const CSMWorld::NpcAutoCalc& CSMWorld::Data::getNpcAutoCalc() const
{
return *mNpcAutoCalc;
} }
QAbstractItemModel *CSMWorld::Data::getTableModel (const CSMWorld::UniversalId& id) QAbstractItemModel *CSMWorld::Data::getTableModel (const CSMWorld::UniversalId& id)
@ -973,6 +897,22 @@ void CSMWorld::Data::merge()
mGlobals.merge(); mGlobals.merge();
} }
int CSMWorld::Data::getTotalRecords (const std::vector<boost::filesystem::path>& files)
{
int records = 0;
std::unique_ptr<ESM::ESMReader> reader = std::unique_ptr<ESM::ESMReader>(new ESM::ESMReader);
for (unsigned int i = 0; i < files.size(); ++i)
{
reader->open(files[i].string());
records += reader->getRecordCount();
reader->close();
}
return records;
}
int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base, bool project) int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base, bool project)
{ {
// Don't delete the Reader yet. Some record types store a reference to the Reader to handle on-demand loading // Don't delete the Reader yet. Some record types store a reference to the Reader to handle on-demand loading
@ -987,6 +927,35 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base
mReader->setIndex(mReaderIndex++); mReader->setIndex(mReaderIndex++);
mReader->open (path.string()); mReader->open (path.string());
mLoadedFiles.push_back(path.filename().string());
// at this point mReader->mHeader.mMaster have been populated for the file being loaded
for (size_t f = 0; f < mReader->getGameFiles().size(); ++f)
{
ESM::Header::MasterData& m = const_cast<ESM::Header::MasterData&>(mReader->getGameFiles().at(f));
int index = -1;
for (size_t i = 0; i < mLoadedFiles.size()-1; ++i) // -1 to ignore the current file
{
if (Misc::StringUtils::ciEqual(m.name, mLoadedFiles.at(i)))
{
index = static_cast<int>(i);
break;
}
}
if (index == -1)
{
// Tried to load a parent file that has not been loaded yet. This is bad,
// the launcher should have taken care of this.
std::string fstring = "File " + mReader->getName() + " asks for parent file " + m.name
+ ", but it has not been loaded yet. Please check your load order.";
mReader->fail(fstring);
}
m.index = index;
}
mBase = base; mBase = base;
mProject = project; mProject = project;
@ -996,7 +965,8 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base
metaData.mId = "sys::meta"; metaData.mId = "sys::meta";
metaData.load (*mReader); metaData.load (*mReader);
mMetaData.setRecord (0, Record<MetaData> (RecordBase::State_ModifiedOnly, 0, &metaData)); mMetaData.setRecord (0, std::make_unique<Record<MetaData> >(
Record<MetaData> (RecordBase::State_ModifiedOnly, 0, &metaData)));
} }
return mReader->getRecordCount(); return mReader->getRecordCount();
@ -1299,271 +1269,3 @@ const CSMWorld::Data& CSMWorld::Data::self ()
{ {
return *this; return *this;
} }
void CSMWorld::Data::skillDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
{
// mData.mAttribute (affects attributes skill bonus autocalc)
// mData.mSpecialization (affects skills autocalc)
CSMWorld::IdTable *skillModel =
static_cast<CSMWorld::IdTable*>(getTableModel(CSMWorld::UniversalId::Type_Skill));
int attributeColumn = skillModel->findColumnIndex(CSMWorld::Columns::ColumnId_Attribute);
int specialisationColumn = skillModel->findColumnIndex(CSMWorld::Columns::ColumnId_Specialisation);
if ((topLeft.column() <= attributeColumn && attributeColumn <= bottomRight.column())
|| (topLeft.column() <= specialisationColumn && specialisationColumn <= bottomRight.column()))
{
clearNpcStatsCache();
std::string empty;
emit updateNpcAutocalc(0/*all*/, empty);
}
}
void CSMWorld::Data::classDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
{
// update autocalculated attributes/skills of every NPC with matching class
// - mData.mAttribute[2]
// - mData.mSkills[5][2]
// - mData.mSpecialization
CSMWorld::IdTable *classModel =
static_cast<CSMWorld::IdTable*>(getTableModel(CSMWorld::UniversalId::Type_Class));
int attribute1Column = classModel->findColumnIndex(CSMWorld::Columns::ColumnId_Attribute1); // +1
int majorSkill1Column = classModel->findColumnIndex(CSMWorld::Columns::ColumnId_MajorSkill1); // +4
int minorSkill1Column = classModel->findColumnIndex(CSMWorld::Columns::ColumnId_MinorSkill1); // +4
int specialisationColumn = classModel->findColumnIndex(CSMWorld::Columns::ColumnId_Specialisation);
if ((topLeft.column() > attribute1Column+1 || attribute1Column > bottomRight.column())
&& (topLeft.column() > majorSkill1Column+4 || majorSkill1Column > bottomRight.column())
&& (topLeft.column() > minorSkill1Column+4 || minorSkill1Column > bottomRight.column())
&& (topLeft.column() > specialisationColumn || specialisationColumn > bottomRight.column()))
{
return;
}
// get the affected class
int idColumn = classModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id);
for (int classRow = topLeft.row(); classRow <= bottomRight.row(); ++classRow)
{
clearNpcStatsCache();
std::string classId =
classModel->data(classModel->index(classRow, idColumn)).toString().toUtf8().constData();
emit updateNpcAutocalc(1/*class*/, classId);
}
}
void CSMWorld::Data::raceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
{
// affects racial bonus attributes & skills
// - mData.mAttributeValues[]
// - mData.mBonus[].mBonus
// - mPowers.mList[]
CSMWorld::IdTree *raceModel =
static_cast<CSMWorld::IdTree*>(getTableModel(CSMWorld::UniversalId::Type_Race));
int attrColumn = raceModel->findColumnIndex(CSMWorld::Columns::ColumnId_RaceAttributes);
int bonusColumn = raceModel->findColumnIndex(CSMWorld::Columns::ColumnId_RaceSkillBonus);
int powersColumn = raceModel->findColumnIndex(CSMWorld::Columns::ColumnId_PowerList);
bool match = false;
int raceRow = topLeft.row();
int raceEnd = bottomRight.row();
if (topLeft.parent().isValid() && bottomRight.parent().isValid())
{
if ((topLeft.parent().column() <= attrColumn && attrColumn <= bottomRight.parent().column())
|| (topLeft.parent().column() <= bonusColumn && bonusColumn <= bottomRight.parent().column())
|| (topLeft.parent().column() <= powersColumn && powersColumn <= bottomRight.parent().column()))
{
match = true; // TODO: check for specific nested column?
raceRow = topLeft.parent().row();
raceEnd = bottomRight.parent().row();
}
}
else
{
if ((topLeft.column() <= attrColumn && attrColumn <= bottomRight.column())
|| (topLeft.column() <= bonusColumn && bonusColumn <= bottomRight.column())
|| (topLeft.column() <= powersColumn && powersColumn <= bottomRight.column()))
{
match = true; // maybe the whole table changed
}
}
if (!match)
return;
// update autocalculated attributes/skills of every NPC with matching race
int idColumn = raceModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id);
for (; raceRow <= raceEnd; ++raceRow)
{
clearNpcStatsCache();
std::string raceId =
raceModel->data(raceModel->index(raceRow, idColumn)).toString().toUtf8().constData();
emit updateNpcAutocalc(2/*race*/, raceId);
}
}
void CSMWorld::Data::npcDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
{
// TODO: for now always recalculate
clearNpcStatsCache();
}
void CSMWorld::Data::gmstDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
{
static const QStringList gmsts(QStringList()<< "fNPCbaseMagickaMult" << "fAutoSpellChance"
<< "fEffectCostMult" << "iAutoSpellAlterationMax" << "iAutoSpellConjurationMax"
<< "iAutoSpellDestructionMax" << "iAutoSpellIllusionMax" << "iAutoSpellMysticismMax"
<< "iAutoSpellRestorationMax" << "iAutoSpellTimesCanCast" << "iAutoSpellAttSkillMin");
bool match = false;
for (int row = topLeft.row(); row <= bottomRight.row(); ++row)
{
if (gmsts.contains(mGmsts.getRecord(row).get().mId.c_str()))
{
match = true;
break;
}
}
if (!match)
return;
clearNpcStatsCache();
std::string empty;
emit updateNpcAutocalc(0/*all*/, empty);
}
void CSMWorld::Data::clearNpcStatsCache ()
{
for (std::map<std::string, CSMWorld::NpcStats*>::iterator it (mNpcStatCache.begin());
it != mNpcStatCache.end(); ++it)
delete it->second;
mNpcStatCache.clear();
}
CSMWorld::NpcStats* CSMWorld::Data::npcAutoCalculate(const ESM::NPC& npc) const
{
CSMWorld::NpcStats * cachedStats = getCachedNpcData (npc.mId);
if (cachedStats)
return cachedStats;
int raceIndex = mRaces.searchId(npc.mRace);
int classIndex = mClasses.searchId(npc.mClass);
// this can happen when creating a new game from scratch
if (raceIndex == -1 || classIndex == -1)
return 0;
const ESM::Race *race = &mRaces.getRecord(raceIndex).get();
const ESM::Class *class_ = &mClasses.getRecord(classIndex).get();
bool autoCalc = npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS;
short level = npc.mNpdt52.mLevel;
if (autoCalc)
level = npc.mNpdt12.mLevel;
std::auto_ptr<CSMWorld::NpcStats> stats (new CSMWorld::NpcStats());
CSStore store(mGmsts, mSkills, mMagicEffects, mSpells);
if (autoCalc)
{
AutoCalc::autoCalcAttributesImpl (&npc, race, class_, level, *stats, &store);
stats->setHealth(autoCalculateHealth(level, class_, *stats));
stats->setMana(autoCalculateMana(*stats));
stats->setFatigue(autoCalculateFatigue(*stats));
AutoCalc::autoCalcSkillsImpl(&npc, race, class_, level, *stats, &store);
AutoCalc::autoCalculateSpells(race, *stats, &store);
}
else
{
for (std::vector<std::string>::const_iterator it = npc.mSpells.mList.begin();
it != npc.mSpells.mList.end(); ++it)
{
stats->addSpell(*it);
}
}
// update spell info
const std::vector<std::string> &racePowers = race->mPowers.mList;
for (unsigned int i = 0; i < racePowers.size(); ++i)
{
int type = -1;
int spellIndex = mSpells.searchId(racePowers[i]);
if (spellIndex != -1)
type = mSpells.getRecord(spellIndex).get().mData.mType;
stats->addPowers(racePowers[i], type);
}
// cost/chance
int skills[ESM::Skill::Length];
if (autoCalc)
for (int i = 0; i< ESM::Skill::Length; ++i)
skills[i] = stats->getBaseSkill(i);
else
for (int i = 0; i< ESM::Skill::Length; ++i)
skills[i] = npc.mNpdt52.mSkills[i];
int attributes[ESM::Attribute::Length];
if (autoCalc)
for (int i = 0; i< ESM::Attribute::Length; ++i)
attributes[i] = stats->getBaseAttribute(i);
else
{
attributes[ESM::Attribute::Strength] = npc.mNpdt52.mStrength;
attributes[ESM::Attribute::Willpower] = npc.mNpdt52.mWillpower;
attributes[ESM::Attribute::Agility] = npc.mNpdt52.mAgility;
attributes[ESM::Attribute::Speed] = npc.mNpdt52.mSpeed;
attributes[ESM::Attribute::Endurance] = npc.mNpdt52.mEndurance;
attributes[ESM::Attribute::Personality] = npc.mNpdt52.mPersonality;
attributes[ESM::Attribute::Luck] = npc.mNpdt52.mLuck;
}
const std::vector<CSMWorld::SpellInfo>& spells = stats->spells();
for (std::vector<SpellInfo>::const_iterator it = spells.begin(); it != spells.end(); ++it)
{
int cost = -1;
int spellIndex = mSpells.searchId((*it).mName);
const ESM::Spell* spell = 0;
if (spellIndex != -1)
{
spell = &mSpells.getRecord(spellIndex).get();
cost = spell->mData.mCost;
int school;
float skillTerm;
AutoCalc::calcWeakestSchool(spell, skills, school, skillTerm, &store);
float chance = calcAutoCastChance(spell, skills, attributes, school, &store);
stats->addCostAndChance((*it).mName, cost, (int)ceil(chance)); // percent
}
}
if (stats.get() == 0)
return 0;
CSMWorld::NpcStats *result = stats.release();
emit cacheNpcStats (npc.mId, result);
return result;
}
void CSMWorld::Data::cacheNpcStatsEvent (const std::string& id, CSMWorld::NpcStats *stats)
{
mNpcStatCache[id] = stats;
}
CSMWorld::NpcStats* CSMWorld::Data::getCachedNpcData (const std::string& id) const
{
std::map<std::string, CSMWorld::NpcStats*>::const_iterator it = mNpcStatCache.find(id);
if (it != mNpcStatCache.end())
return it->second;
else
return 0;
}

View File

@ -61,7 +61,7 @@ namespace CSMWorld
{ {
class ResourcesManager; class ResourcesManager;
class Resources; class Resources;
class NpcStats; class NpcAutoCalc;
class Data : public QObject class Data : public QObject
{ {
@ -104,12 +104,13 @@ namespace CSMWorld
const ESM::Dialogue *mDialogue; // last loaded dialogue const ESM::Dialogue *mDialogue; // last loaded dialogue
bool mBase; bool mBase;
bool mProject; bool mProject;
std::map<std::string, std::map<ESM::RefNum, std::string> > mRefLoadCache; std::map<std::string, std::map<unsigned int, unsigned int> > mRefLoadCache;
int mReaderIndex; int mReaderIndex;
std::vector<std::string> mLoadedFiles;
std::vector<boost::shared_ptr<ESM::ESMReader> > mReaders; std::vector<boost::shared_ptr<ESM::ESMReader> > mReaders;
std::map<std::string, NpcStats*> mNpcStatCache; NpcAutoCalc *mNpcAutoCalc;
// not implemented // not implemented
Data (const Data&); Data (const Data&);
@ -126,8 +127,6 @@ namespace CSMWorld
const Data& self (); const Data& self ();
void clearNpcStatsCache ();
public: public:
Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager); Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager);
@ -262,6 +261,8 @@ namespace CSMWorld
void merge(); void merge();
///< Merge modified into base. ///< Merge modified into base.
int getTotalRecords (const std::vector<boost::filesystem::path>& files); // for better loading bar
int startLoading (const boost::filesystem::path& path, bool base, bool project); int startLoading (const boost::filesystem::path& path, bool base, bool project);
///< Begin merging content of a file into base or modified. ///< Begin merging content of a file into base or modified.
/// ///
@ -282,37 +283,17 @@ namespace CSMWorld
int count (RecordBase::State state) const; int count (RecordBase::State state) const;
///< Return number of top-level records with the given \a state. ///< Return number of top-level records with the given \a state.
NpcStats* npcAutoCalculate (const ESM::NPC& npc) const; const NpcAutoCalc& getNpcAutoCalc() const;
NpcStats* getCachedNpcData (const std::string& id) const;
signals: signals:
void idListChanged(); void idListChanged();
// refresh NPC dialogue subviews via object table model
void updateNpcAutocalc (int type, const std::string& id);
void cacheNpcStats (const std::string& id, NpcStats *stats) const;
private slots: private slots:
void dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); void dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
void rowsChanged (const QModelIndex& parent, int start, int end); void rowsChanged (const QModelIndex& parent, int start, int end);
// for autocalc updates when gmst/race/class/skils tables change
void gmstDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
void raceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
void classDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
void skillDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
void npcDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
void cacheNpcStatsEvent (const std::string& id, NpcStats *stats);
}; };
} }

View File

@ -66,9 +66,9 @@ namespace CSMWorld
return -1; return -1;
} }
Record<ESXRecordT> baseRecord = this->getRecord (index); std::unique_ptr<Record<ESXRecordT> > baseRecord(new Record<ESXRecordT>(this->getRecord(index)));
baseRecord.mState = RecordBase::State_Deleted; baseRecord->mState = RecordBase::State_Deleted;
this->setRecord (index, baseRecord); this->setRecord(index, std::move(baseRecord));
return index; return index;
} }
@ -79,30 +79,31 @@ namespace CSMWorld
int IdCollection<ESXRecordT, IdAccessorT>::load (const ESXRecordT& record, bool base, int IdCollection<ESXRecordT, IdAccessorT>::load (const ESXRecordT& record, bool base,
int index) int index)
{ {
if (index==-2) if (index==-2) // index unknown
index = this->searchId (IdAccessorT().getId (record)); index = this->searchId (IdAccessorT().getId (record));
if (index==-1) if (index==-1)
{ {
// new record // new record
Record<ESXRecordT> record2; std::unique_ptr<Record<ESXRecordT> > record2(new Record<ESXRecordT>);
record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; record2->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
(base ? record2.mBase : record2.mModified) = record; (base ? record2->mBase : record2->mModified) = record;
index = this->getSize(); index = this->getSize();
this->appendRecord (record2); this->appendRecord(std::move(record2));
} }
else else
{ {
// old record // old record
Record<ESXRecordT> record2 = Collection<ESXRecordT, IdAccessorT>::getRecord (index); std::unique_ptr<Record<ESXRecordT> > record2(
new Record<ESXRecordT>(Collection<ESXRecordT, IdAccessorT>::getRecord(index)));
if (base) if (base)
record2.mBase = record; record2->mBase = record;
else else
record2.setModified (record); record2->setModified(record);
this->setRecord (index, record2); this->setRecord(index, std::move(record2));
} }
return index; return index;
@ -116,7 +117,7 @@ namespace CSMWorld
if (index==-1) if (index==-1)
return false; return false;
Record<ESXRecordT> record = Collection<ESXRecordT, IdAccessorT>::getRecord (index); const Record<ESXRecordT>& record = Collection<ESXRecordT, IdAccessorT>::getRecord (index);
if (record.isDeleted()) if (record.isDeleted())
return false; return false;
@ -127,8 +128,10 @@ namespace CSMWorld
} }
else else
{ {
record.mState = RecordBase::State_Deleted; std::unique_ptr<Record<ESXRecordT> > record2(
this->setRecord (index, record); new Record<ESXRecordT>(Collection<ESXRecordT, IdAccessorT>::getRecord(index)));
record2->mState = RecordBase::State_Deleted;
this->setRecord(index, std::move(record2));
} }
return true; return true;

View File

@ -163,26 +163,37 @@ void CSMWorld::IdTable::cloneRecord(const std::string& origin,
///This method can return only indexes to the top level table cells ///This method can return only indexes to the top level table cells
QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) const QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) const
{ {
return index(mIdCollection->getIndex (id), column); int row = mIdCollection->searchId (id);
if (row != -1)
return index(row, column);
else
return QModelIndex();
} }
void CSMWorld::IdTable::setRecord (const std::string& id, const RecordBase& record, CSMWorld::UniversalId::Type type) void CSMWorld::IdTable::setRecord (const std::string& id,
std::unique_ptr<RecordBase> record, CSMWorld::UniversalId::Type type)
{ {
int index = mIdCollection->searchId (id); int index = mIdCollection->searchId (id);
if (index==-1) if (index==-1)
{ {
int index = mIdCollection->getAppendIndex (id, type); // For info records, appendRecord may use a different index than the one returned by
// getAppendIndex (because of prev/next links). This can result in the display not
// updating correctly after an undo
//
// Use an alternative method to get the correct index. For non-Info records the
// record pointer is ignored and internally calls getAppendIndex.
int index2 = mIdCollection->getInsertIndex (id, type, record.get());
beginInsertRows (QModelIndex(), index, index); beginInsertRows (QModelIndex(), index2, index2);
mIdCollection->appendRecord (record, type); mIdCollection->appendRecord (std::move(record), type);
endInsertRows(); endInsertRows();
} }
else else
{ {
mIdCollection->replace (index, record); mIdCollection->replace (index, std::move(record));
emit dataChanged (CSMWorld::IdTable::index (index, 0), emit dataChanged (CSMWorld::IdTable::index (index, 0),
CSMWorld::IdTable::index (index, mIdCollection->getColumns()-1)); CSMWorld::IdTable::index (index, mIdCollection->getColumns()-1));
} }

View File

@ -2,6 +2,7 @@
#define CSM_WOLRD_IDTABLE_H #define CSM_WOLRD_IDTABLE_H
#include <vector> #include <vector>
#include <memory>
#include "idtablebase.hpp" #include "idtablebase.hpp"
#include "universalid.hpp" #include "universalid.hpp"
@ -59,7 +60,7 @@ namespace CSMWorld
virtual QModelIndex getModelIndex (const std::string& id, int column) const; virtual QModelIndex getModelIndex (const std::string& id, int column) const;
void setRecord (const std::string& id, const RecordBase& record, void setRecord (const std::string& id, std::unique_ptr<RecordBase> record,
UniversalId::Type type = UniversalId::Type_None); UniversalId::Type type = UniversalId::Type_None);
///< Add record or overwrite existing recrod. ///< Add record or overwrite existing recrod.

View File

@ -8,83 +8,165 @@
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
namespace CSMWorld
{
template<>
void Collection<Info, IdAccessor<Info> >::removeRows (int index, int count)
{
mRecords.erase(mRecords.begin()+index, mRecords.begin()+index+count);
// index map is updated in InfoCollection::removeRows()
}
template<>
void Collection<Info, IdAccessor<Info> >::insertRecord (std::unique_ptr<RecordBase> record,
int index, UniversalId::Type type)
{
int size = static_cast<int>(mRecords.size());
if (index < 0 || index > size)
throw std::runtime_error("index out of range");
std::unique_ptr<Record<Info> > record2(static_cast<Record<Info>*>(record.release()));
if (index == size)
mRecords.push_back(std::move(record2));
else
mRecords.insert(mRecords.begin()+index, std::move(record2));
// index map is updated in InfoCollection::insertRecord()
}
template<>
bool Collection<Info, IdAccessor<Info> >::reorderRowsImp (int baseIndex,
const std::vector<int>& newOrder)
{
if (!newOrder.empty())
{
int size = static_cast<int>(newOrder.size());
// check that all indices are present
std::vector<int> test(newOrder);
std::sort(test.begin(), test.end());
if (*test.begin() != 0 || *--test.end() != size-1)
return false;
// reorder records
std::vector<std::unique_ptr<Record<Info> > > buffer(size);
// FIXME: BUG: undo does not remove modified flag
for (int i = 0; i < size; ++i)
{
buffer[newOrder[i]] = std::move(mRecords[baseIndex+i]);
buffer[newOrder[i]]->setModified(buffer[newOrder[i]]->get());
}
std::move(buffer.begin(), buffer.end(), mRecords.begin()+baseIndex);
// index map is updated in InfoCollection::reorderRows()
}
return true;
}
}
void CSMWorld::InfoCollection::load (const Info& record, bool base) void CSMWorld::InfoCollection::load (const Info& record, bool base)
{ {
int index = searchId (record.mId); int index = searchId(record.mId);
if (index==-1) if (index == -1)
{ {
// new record // new record
Record<Info> record2; std::unique_ptr<Record<Info> > record2(new Record<Info>);
record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; record2->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
(base ? record2.mBase : record2.mModified) = record; (base ? record2->mBase : record2->mModified) = record;
int index = -1; appendRecord(std::move(record2));
std::string topic = Misc::StringUtils::lowerCase (record2.get().mTopicId);
if (!record2.get().mPrev.empty())
{
index = getInfoIndex (record2.get().mPrev, topic);
if (index!=-1)
++index;
}
if (index==-1 && !record2.get().mNext.empty())
{
index = getInfoIndex (record2.get().mNext, topic);
}
if (index==-1)
{
Range range = getTopicRange (topic);
index = std::distance (getRecords().begin(), range.second);
}
insertRecord (record2, index);
} }
else else
{ {
// old record // old record
Record<Info> record2 = getRecord (index); std::unique_ptr<Record<Info> > record2(new Record<Info>(getRecord(index)));
if (base) if (base)
record2.mBase = record; record2->mBase = record;
else else
record2.setModified (record); record2->setModified (record);
setRecord (index, record2); setRecord (index, std::move(record2));
} }
} }
int CSMWorld::InfoCollection::getInfoIndex (const std::string& id, const std::string& topic) const int CSMWorld::InfoCollection::getInfoIndex (const std::string& id, const std::string& topic) const
{ {
std::string fullId = Misc::StringUtils::lowerCase (topic) + "#" + id; // find the topic first
std::map<StringHash, std::vector<std::pair<std::string, int> > >::const_iterator iter
= mInfoIndex.find(StringHash(std::make_shared<std::string>(Misc::StringUtils::lowerCase(topic))));
std::pair<RecordConstIterator, RecordConstIterator> range = getTopicRange (topic); if (iter == mInfoIndex.end())
return -1;
for (; range.first!=range.second; ++range.first) // brute force loop
if (Misc::StringUtils::ciEqual(range.first->get().mId, fullId)) std::string lowerId = Misc::StringUtils::lowerCase (id);
return std::distance (getRecords().begin(), range.first); for (std::vector<std::pair<std::string, int> >::const_iterator it = iter->second.begin();
it != iter->second.end(); ++it)
{
if (Misc::StringUtils::cEqual(it->first, lowerId))
return it->second;
}
return -1; return -1;
} }
int CSMWorld::InfoCollection::getAppendIndex (const std::string& id, UniversalId::Type type) const // Calling insertRecord() using index from getInsertIndex() needs to take into account of
// prev/next records; an example is deleting a record then undo
int CSMWorld::InfoCollection::getInsertIndex (const std::string& id,
UniversalId::Type type, RecordBase *record) const
{ {
std::string::size_type separator = id.find_last_of ('#'); if (record == nullptr)
{
std::string::size_type separator = id.find_last_of('#');
if (separator==std::string::npos) if (separator == std::string::npos)
throw std::runtime_error ("invalid info ID: " + id); throw std::runtime_error("invalid info ID: " + id);
std::pair<RecordConstIterator, RecordConstIterator> range = getTopicRange (id.substr (0, separator)); std::pair<RecordConstIterator, RecordConstIterator> range = getTopicRange(id.substr(0, separator));
if (range.first==range.second) if (range.first == range.second)
return Collection<Info, IdAccessor<Info> >::getAppendIndex (id, type); return Collection<Info, IdAccessor<Info> >::getAppendIndex(id, type);
return std::distance (getRecords().begin(), range.second); return std::distance(getRecords().begin(), range.second);
}
int index = -1;
const Info& info = static_cast<Record<Info>*>(record)->get();
std::string topic = info.mTopicId;
// if the record has a prev, find its index value
if (!info.mPrev.empty())
{
index = getInfoIndex(info.mPrev, topic);
if (index != -1)
++index; // if prev exists, set current index to one above prev
}
// if prev doesn't exist or not found and the record has a next, find its index value
if (index == -1 && !info.mNext.empty())
{
// if next exists, use its index as the current index
index = getInfoIndex(info.mNext, topic);
}
// if next doesn't exist or not found (i.e. neither exist yet) then start a new one
if (index == -1)
{
Range range = getTopicRange(topic); // getTopicRange converts topic to lower case first
index = std::distance(getRecords().begin(), range.second);
}
return index;
} }
bool CSMWorld::InfoCollection::reorderRows (int baseIndex, const std::vector<int>& newOrder) bool CSMWorld::InfoCollection::reorderRows (int baseIndex, const std::vector<int>& newOrder)
@ -101,7 +183,23 @@ bool CSMWorld::InfoCollection::reorderRows (int baseIndex, const std::vector<int
return false; return false;
// reorder // reorder
return reorderRowsImp (baseIndex, newOrder); if (!Collection<Info, IdAccessor<Info> >::reorderRowsImp(baseIndex, newOrder))
return false;
// adjust index
int size = static_cast<int>(newOrder.size());
for (std::map<StringHash, std::vector<std::pair<std::string, int> > >::iterator iter
= mInfoIndex.begin(); iter != mInfoIndex.end(); ++iter)
{
for (std::vector<std::pair<std::string, int> >::iterator it = iter->second.begin();
it != iter->second.end(); ++it)
{
if (it->second >= baseIndex && it->second < baseIndex+size)
it->second = newOrder.at(it->second-baseIndex)+baseIndex;
}
}
return true;
} }
void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue) void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue)
@ -116,7 +214,7 @@ void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ES
{ {
int index = searchId (id); int index = searchId (id);
if (index==-1) if (index == -1)
{ {
// deleting a record that does not exist // deleting a record that does not exist
// ignore it for now // ignore it for now
@ -128,9 +226,9 @@ void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ES
} }
else else
{ {
Record<Info> record = getRecord (index); std::unique_ptr<Record<Info> > record(new Record<Info>(getRecord(index)));
record.mState = RecordBase::State_Deleted; record->mState = RecordBase::State_Deleted;
setRecord (index, record); setRecord (index, std::move(record));
} }
} }
else else
@ -144,73 +242,54 @@ void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ES
CSMWorld::InfoCollection::Range CSMWorld::InfoCollection::getTopicRange (const std::string& topic) CSMWorld::InfoCollection::Range CSMWorld::InfoCollection::getTopicRange (const std::string& topic)
const const
{ {
std::string topic2 = Misc::StringUtils::lowerCase (topic); std::string lowerTopic = Misc::StringUtils::lowerCase (topic);
std::map<std::string, int>::const_iterator iter = getIdMap().lower_bound (topic2); // find the topic
std::map<StringHash, std::vector<std::pair<std::string, int> > >::const_iterator iter
= mInfoIndex.find(StringHash(std::make_shared<std::string>(lowerTopic)));
// Skip invalid records: The beginning of a topic string could be identical to another topic if (iter == mInfoIndex.end())
// string.
for (; iter!=getIdMap().end(); ++iter)
{
std::string testTopicId =
Misc::StringUtils::lowerCase (getRecord (iter->second).get().mTopicId);
if (testTopicId==topic2)
break;
std::size_t size = topic2.size();
if (testTopicId.size()<size || testTopicId.substr (0, size)!=topic2)
return Range (getRecords().end(), getRecords().end());
}
if (iter==getIdMap().end())
return Range (getRecords().end(), getRecords().end()); return Range (getRecords().end(), getRecords().end());
RecordConstIterator begin = getRecords().begin()+iter->second; // topic found, find the starting index
int low = INT_MAX;
while (begin != getRecords().begin()) for (std::vector<std::pair<std::string, int> >::const_iterator it = iter->second.begin();
it != iter->second.end(); ++it)
{ {
if (!Misc::StringUtils::ciEqual(begin->get().mTopicId, topic2)) low = std::min(low, it->second);
{
// we've gone one too far, go back
++begin;
break;
}
--begin;
} }
// Find end RecordConstIterator begin = getRecords().begin() + low;
RecordConstIterator end = begin;
for (; end!=getRecords().end(); ++end) // Find end (one past the range)
if (!Misc::StringUtils::ciEqual(end->get().mTopicId, topic2)) RecordConstIterator end = begin + iter->second.size();
break; if (end != getRecords().end())
++end;
return Range (begin, end); return Range (begin, end);
} }
void CSMWorld::InfoCollection::removeDialogueInfos(const std::string& dialogueId) void CSMWorld::InfoCollection::removeDialogueInfos(const std::string& dialogueId)
{ {
std::string id = Misc::StringUtils::lowerCase(dialogueId);
std::vector<int> erasedRecords; std::vector<int> erasedRecords;
std::map<std::string, int>::const_iterator current = getIdMap().lower_bound(id); Range range = getTopicRange(dialogueId); // getTopicRange converts dialogueId to lower case first
std::map<std::string, int>::const_iterator end = getIdMap().end();
for (; current != end; ++current) for (; range.first != range.second; ++range.first)
{ {
Record<Info> record = getRecord(current->second); const Record<Info>& record = **range.first;
if (Misc::StringUtils::ciEqual(dialogueId, record.get().mTopicId)) if (Misc::StringUtils::ciEqual(dialogueId, record.get().mTopicId))
{ {
if (record.mState == RecordBase::State_ModifiedOnly) if (record.mState == RecordBase::State_ModifiedOnly)
{ {
erasedRecords.push_back(current->second); erasedRecords.push_back(range.first - getRecords().begin());
} }
else else
{ {
record.mState = RecordBase::State_Deleted; std::unique_ptr<Record<Info> > record2(new Record<Info>(record));
setRecord(current->second, record); record2->mState = RecordBase::State_Deleted;
setRecord(range.first - getRecords().begin(), std::move(record2));
} }
} }
else else
@ -225,3 +304,105 @@ void CSMWorld::InfoCollection::removeDialogueInfos(const std::string& dialogueId
erasedRecords.pop_back(); erasedRecords.pop_back();
} }
} }
// FIXME: removing a record should adjust prev/next and mark those records as modified
// accordingly (also consider undo)
void CSMWorld::InfoCollection::removeRows (int index, int count)
{
Collection<Info, IdAccessor<Info> >::removeRows(index, count); // erase records only
for (std::map<StringHash, std::vector<std::pair<std::string, int> > >::iterator iter
= mInfoIndex.begin(); iter != mInfoIndex.end();)
{
for (std::vector<std::pair<std::string, int> >::iterator it = iter->second.begin();
it != iter->second.end();)
{
if (it->second >= index)
{
if (it->second >= index+count)
{
it->second -= count;
++it;
}
else
iter->second.erase(it);
}
else
++it;
}
// check for an empty vector
if (iter->second.empty())
mInfoIndex.erase(iter++);
else
++iter;
}
}
void CSMWorld::InfoCollection::appendBlankRecord (const std::string& id, UniversalId::Type type)
{
std::unique_ptr<Record<Info> > record2(new Record<Info>);
record2->mState = Record<Info>::State_ModifiedOnly;
record2->mModified.blank();
record2->get().mId = id;
insertRecord(std::move(record2), getInsertIndex(id, type, nullptr), type); // call InfoCollection::insertRecord()
}
int CSMWorld::InfoCollection::searchId (const std::string& id) const
{
std::string::size_type separator = id.find_last_of('#');
if (separator == std::string::npos)
throw std::runtime_error("invalid info ID: " + id);
return getInfoIndex(id.substr(separator+1), id.substr(0, separator));
}
void CSMWorld::InfoCollection::appendRecord (std::unique_ptr<RecordBase> record, UniversalId::Type type)
{
int index = getInsertIndex(static_cast<Record<Info>*>(record.get())->get().mId, type, record.get());
insertRecord(std::move(record), index, type);
}
void CSMWorld::InfoCollection::insertRecord (std::unique_ptr<RecordBase> record, int index,
UniversalId::Type type)
{
int size = static_cast<int>(getRecords().size());
std::string id = static_cast<Record<Info>*>(record.get())->get().mId;
std::string::size_type separator = id.find_last_of('#');
if (separator == std::string::npos)
throw std::runtime_error("invalid info ID: " + id);
Collection<Info, IdAccessor<Info> >::insertRecord(std::move(record), index, type); // add records only
// adjust index
if (index < size-1)
{
for (std::map<StringHash, std::vector<std::pair<std::string, int> > >::iterator iter
= mInfoIndex.begin(); iter != mInfoIndex.end(); ++iter)
{
for (std::vector<std::pair<std::string, int> >::iterator it = iter->second.begin();
it != iter->second.end(); ++it)
{
if (it->second >= index)
++(it->second);
}
}
}
// get iterator for existing topic or a new topic
std::string lowerId = Misc::StringUtils::lowerCase(id);
std::pair<std::map<StringHash, std::vector<std::pair<std::string, int> > >::iterator, bool> res
= mInfoIndex.insert(
std::make_pair(StringHash(std::make_shared<std::string>(lowerId.substr(0, separator))),
std::vector<std::pair<std::string, int> >())); // empty vector
// insert info and index
res.first->second.push_back(std::make_pair(lowerId.substr(separator+1), index));
}

View File

@ -1,6 +1,8 @@
#ifndef CSM_WOLRD_INFOCOLLECTION_H #ifndef CSM_WOLRD_INFOCOLLECTION_H
#define CSM_WOLRD_INFOCOLLECTION_H #define CSM_WOLRD_INFOCOLLECTION_H
#include <extern/murmurhash/MurmurHash2.h>
#include "collection.hpp" #include "collection.hpp"
#include "info.hpp" #include "info.hpp"
@ -11,27 +13,83 @@ namespace ESM
namespace CSMWorld namespace CSMWorld
{ {
struct StringHash
{
uint64_t mHash;
std::shared_ptr<std::string> mString;
StringHash (std::shared_ptr<std::string> str) : mString(str)
{
mHash = MurmurHash64A(str->c_str(), str->size(), /*seed*/1);
}
};
}
namespace std
{
template<> struct less<CSMWorld::StringHash>
{
bool operator() (const CSMWorld::StringHash& lhs, const CSMWorld::StringHash& rhs) const
{
if (lhs.mHash < rhs.mHash)
return true;
if (lhs.mHash > rhs.mHash)
return false;
return *lhs.mString < *rhs.mString;
}
};
}
namespace CSMWorld
{
template<>
void Collection<Info, IdAccessor<Info> >::removeRows (int index, int count);
template<>
void Collection<Info, IdAccessor<Info> >::insertRecord (std::unique_ptr<RecordBase> record,
int index, UniversalId::Type type);
template<>
bool Collection<Info, IdAccessor<Info> >::reorderRowsImp (int baseIndex,
const std::vector<int>& newOrder);
class InfoCollection : public Collection<Info, IdAccessor<Info> > class InfoCollection : public Collection<Info, IdAccessor<Info> >
{ {
public: public:
typedef std::vector<Record<Info> >::const_iterator RecordConstIterator; typedef std::vector<std::unique_ptr<Record<Info> > >::const_iterator RecordConstIterator;
typedef std::pair<RecordConstIterator, RecordConstIterator> Range; typedef std::pair<RecordConstIterator, RecordConstIterator> Range;
private: private:
// The general strategy is to keep the records in Collection kept in order (within
// a topic group) while the index lookup maps are not ordered. It is assumed that
// each topic has a small number of infos, which allows the use of vectors for
// iterating through them without too much penalty.
//
// NOTE: hashed topic string as well as id string are stored in lower case.
std::map<StringHash, std::vector<std::pair<std::string, int> > > mInfoIndex;
void load (const Info& record, bool base); void load (const Info& record, bool base);
int getInfoIndex (const std::string& id, const std::string& topic) const; int getInfoIndex (const std::string& id, const std::string& topic) const;
///< Return index for record \a id or -1 (if not present; deleted records are considered) ///< Return index for record \a id or -1 (if not present; deleted records are considered)
/// ///
/// \param id info ID without topic prefix /// \param id info ID without topic prefix
//
/// \attention id and topic are assumed to be in lower case
public: public:
virtual int getAppendIndex (const std::string& id, virtual int getInsertIndex (const std::string& id,
UniversalId::Type type = UniversalId::Type_None) const; UniversalId::Type type = UniversalId::Type_None,
RecordBase *record = nullptr) const;
///< \param type Will be ignored, unless the collection supports multiple record types ///< \param type Will be ignored, unless the collection supports multiple record types
///
/// Works like getAppendIndex unless an overloaded method uses the record pointer
/// to get additional info about the record that results in an alternative index.
virtual bool reorderRows (int baseIndex, const std::vector<int>& newOrder); virtual bool reorderRows (int baseIndex, const std::vector<int>& newOrder);
///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices
@ -46,6 +104,20 @@ namespace CSMWorld
/// the given topic. /// the given topic.
void removeDialogueInfos(const std::string& dialogueId); void removeDialogueInfos(const std::string& dialogueId);
virtual void removeRows (int index, int count);
virtual void appendBlankRecord (const std::string& id,
UniversalId::Type type = UniversalId::Type_None);
virtual int searchId (const std::string& id) const;
virtual void appendRecord (std::unique_ptr<RecordBase> record,
UniversalId::Type type = UniversalId::Type_None);
virtual void insertRecord (std::unique_ptr<RecordBase> record,
int index,
UniversalId::Type type = UniversalId::Type_None);
}; };
} }

View File

@ -90,23 +90,23 @@ namespace CSMWorld
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
void NestedIdCollection<ESXRecordT, IdAccessorT>::addNestedRow(int row, int column, int position) void NestedIdCollection<ESXRecordT, IdAccessorT>::addNestedRow(int row, int column, int position)
{ {
Record<ESXRecordT> record; std::unique_ptr<Record<ESXRecordT> > record(new Record<ESXRecordT>);
record.assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row)); record->assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).addRow(record, position); getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).addRow(*record, position);
Collection<ESXRecordT, IdAccessorT>::setRecord(row, record); Collection<ESXRecordT, IdAccessorT>::setRecord(row, std::move(record));
} }
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
void NestedIdCollection<ESXRecordT, IdAccessorT>::removeNestedRows(int row, int column, int subRow) void NestedIdCollection<ESXRecordT, IdAccessorT>::removeNestedRows(int row, int column, int subRow)
{ {
Record<ESXRecordT> record; std::unique_ptr<Record<ESXRecordT> > record(new Record<ESXRecordT>);
record.assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row)); record->assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).removeRow(record, subRow); getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).removeRow(*record, subRow);
Collection<ESXRecordT, IdAccessorT>::setRecord(row, record); Collection<ESXRecordT, IdAccessorT>::setRecord(row, std::move(record));
} }
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
@ -121,13 +121,13 @@ namespace CSMWorld
void NestedIdCollection<ESXRecordT, IdAccessorT>::setNestedData(int row, void NestedIdCollection<ESXRecordT, IdAccessorT>::setNestedData(int row,
int column, const QVariant& data, int subRow, int subColumn) int column, const QVariant& data, int subRow, int subColumn)
{ {
Record<ESXRecordT> record; std::unique_ptr<Record<ESXRecordT> > record(new Record<ESXRecordT>);
record.assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row)); record->assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).setData( getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).setData(
record, data, subRow, subColumn); *record, data, subRow, subColumn);
Collection<ESXRecordT, IdAccessorT>::setRecord(row, record); Collection<ESXRecordT, IdAccessorT>::setRecord(row, std::move(record));
} }
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
@ -142,13 +142,13 @@ namespace CSMWorld
void NestedIdCollection<ESXRecordT, IdAccessorT>::setNestedTable(int row, void NestedIdCollection<ESXRecordT, IdAccessorT>::setNestedTable(int row,
int column, const CSMWorld::NestedTableWrapperBase& nestedTable) int column, const CSMWorld::NestedTableWrapperBase& nestedTable)
{ {
Record<ESXRecordT> record; std::unique_ptr<Record<ESXRecordT> > record(new Record<ESXRecordT>);
record.assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row)); record->assign(Collection<ESXRecordT, IdAccessorT>::getRecord(row));
getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).setTable( getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).setTable(
record, nestedTable); *record, nestedTable);
Collection<ESXRecordT, IdAccessorT>::setRecord(row, record); Collection<ESXRecordT, IdAccessorT>::setRecord(row, std::move(record));
} }
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>

View File

@ -35,22 +35,22 @@ namespace CSMWorld
void NestedInfoCollection::addNestedRow(int row, int column, int position) void NestedInfoCollection::addNestedRow(int row, int column, int position)
{ {
Record<Info> record; std::unique_ptr<Record<Info> > record(new Record<Info>);
record.assign(Collection<Info, IdAccessor<Info> >::getRecord(row)); record->assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).addRow(record, position); getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).addRow(*record, position);
Collection<Info, IdAccessor<Info> >::setRecord(row, record); Collection<Info, IdAccessor<Info> >::setRecord(row, std::move(record));
} }
void NestedInfoCollection::removeNestedRows(int row, int column, int subRow) void NestedInfoCollection::removeNestedRows(int row, int column, int subRow)
{ {
Record<Info> record; std::unique_ptr<Record<Info> > record(new Record<Info>);
record.assign(Collection<Info, IdAccessor<Info> >::getRecord(row)); record->assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).removeRow(record, subRow); getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).removeRow(*record, subRow);
Collection<Info, IdAccessor<Info> >::setRecord(row, record); Collection<Info, IdAccessor<Info> >::setRecord(row, std::move(record));
} }
QVariant NestedInfoCollection::getNestedData (int row, QVariant NestedInfoCollection::getNestedData (int row,
@ -63,13 +63,13 @@ namespace CSMWorld
void NestedInfoCollection::setNestedData(int row, void NestedInfoCollection::setNestedData(int row,
int column, const QVariant& data, int subRow, int subColumn) int column, const QVariant& data, int subRow, int subColumn)
{ {
Record<Info> record; std::unique_ptr<Record<Info> > record(new Record<Info>);
record.assign(Collection<Info, IdAccessor<Info> >::getRecord(row)); record->assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).setData( getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).setData(
record, data, subRow, subColumn); *record, data, subRow, subColumn);
Collection<Info, IdAccessor<Info> >::setRecord(row, record); Collection<Info, IdAccessor<Info> >::setRecord(row, std::move(record));
} }
CSMWorld::NestedTableWrapperBase* NestedInfoCollection::nestedTable(int row, CSMWorld::NestedTableWrapperBase* NestedInfoCollection::nestedTable(int row,
@ -82,13 +82,13 @@ namespace CSMWorld
void NestedInfoCollection::setNestedTable(int row, void NestedInfoCollection::setNestedTable(int row,
int column, const CSMWorld::NestedTableWrapperBase& nestedTable) int column, const CSMWorld::NestedTableWrapperBase& nestedTable)
{ {
Record<Info> record; std::unique_ptr<Record<Info> > record(new Record<Info>);
record.assign(Collection<Info, IdAccessor<Info> >::getRecord(row)); record->assign(Collection<Info, IdAccessor<Info> >::getRecord(row));
getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).setTable( getAdapter(Collection<Info, IdAccessor<Info> >::getColumn(column)).setTable(
record, nestedTable); *record, nestedTable);
Collection<Info, IdAccessor<Info> >::setRecord(row, record); Collection<Info, IdAccessor<Info> >::setRecord(row, std::move(record));
} }
int NestedInfoCollection::getNestedRowsCount(int row, int column) const int NestedInfoCollection::getNestedRowsCount(int row, int column) const

View File

@ -0,0 +1,367 @@
#include "npcautocalc.hpp"
#include <QStringList>
#include <components/autocalc/autocalc.hpp>
#include <components/autocalc/autocalcspell.hpp>
#include <components/autocalc/store.hpp>
#include "npcstats.hpp"
#include "data.hpp"
#include "idtable.hpp"
#include "idtree.hpp"
namespace
{
class CSStore : public AutoCalc::StoreCommon
{
const CSMWorld::IdCollection<ESM::GameSetting>& mGmstTable;
const CSMWorld::IdCollection<ESM::Skill>& mSkillTable;
const CSMWorld::IdCollection<ESM::MagicEffect>& mMagicEffectTable;
const CSMWorld::NestedIdCollection<ESM::Spell>& mSpells;
public:
CSStore(const CSMWorld::IdCollection<ESM::GameSetting>& gmst,
const CSMWorld::IdCollection<ESM::Skill>& skills,
const CSMWorld::IdCollection<ESM::MagicEffect>& magicEffects,
const CSMWorld::NestedIdCollection<ESM::Spell>& spells)
: mGmstTable(gmst), mSkillTable(skills), mMagicEffectTable(magicEffects), mSpells(spells)
{}
~CSStore() {}
virtual int findGmstInt(const std::string& name) const
{
return mGmstTable.getRecord(name).get().getInt();
}
virtual float findGmstFloat(const std::string& name) const
{
return mGmstTable.getRecord(name).get().getFloat();
}
virtual const ESM::Skill *findSkill(int index) const
{
// if the skill does not exist, throws std::runtime_error ("invalid ID: " + id)
return &mSkillTable.getRecord(ESM::Skill::indexToId(index)).get();
}
virtual const ESM::MagicEffect* findMagicEffect(int id) const
{
// if the magic effect does not exist, throws std::runtime_error ("invalid ID: " + id)
return &mMagicEffectTable.getRecord(ESM::MagicEffect::indexToId((short)id)).get();
}
virtual void getSpells(std::vector<ESM::Spell*>& spells)
{
// prepare data in a format used by OpenMW store
for (int index = 0; index < mSpells.getSize(); ++index)
spells.push_back(const_cast<ESM::Spell *>(&mSpells.getRecord(index).get()));
}
};
unsigned short autoCalculateMana(const AutoCalc::StatsBase& stats)
{
return stats.getBaseAttribute(ESM::Attribute::Intelligence) * 2;
}
unsigned short autoCalculateFatigue(const AutoCalc::StatsBase& stats)
{
return stats.getBaseAttribute(ESM::Attribute::Strength)
+ stats.getBaseAttribute(ESM::Attribute::Willpower)
+ stats.getBaseAttribute(ESM::Attribute::Agility)
+ stats.getBaseAttribute(ESM::Attribute::Endurance);
}
}
CSMWorld::NpcAutoCalc::NpcAutoCalc (const Data& data,
const IdTable *gmsts, const IdTable *skills, const IdTable *classes, const IdTree *races, const IdTree *objects)
: mData(data), mSkillModel(skills), mClassModel(classes), mRaceModel(races)
{
// for autocalc updates when gmst/race/class/skils tables change
connect (gmsts, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
this, SLOT (gmstDataChanged (const QModelIndex&, const QModelIndex&)));
connect (mSkillModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
this, SLOT (skillDataChanged (const QModelIndex&, const QModelIndex&)));
connect (mClassModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
this, SLOT (classDataChanged (const QModelIndex&, const QModelIndex&)));
connect (mRaceModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
this, SLOT (raceDataChanged (const QModelIndex&, const QModelIndex&)));
connect (objects, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
this, SLOT (npcDataChanged (const QModelIndex&, const QModelIndex&)));
connect (this, SIGNAL (updateNpcAutocalc (int, const std::string&)),
objects, SLOT (updateNpcAutocalc (int, const std::string&)));
//connect (this, SIGNAL (cacheNpcStats (const std::string&, NpcStats*)),
//this, SLOT (cacheNpcStatsEvent (const std::string&, NpcStats*)));
}
CSMWorld::NpcAutoCalc::~NpcAutoCalc()
{
clearNpcStatsCache();
}
void CSMWorld::NpcAutoCalc::skillDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
{
// mData.mAttribute (affects attributes skill bonus autocalc)
// mData.mSpecialization (affects skills autocalc)
int attributeColumn = mSkillModel->findColumnIndex(CSMWorld::Columns::ColumnId_Attribute);
int specialisationColumn = mSkillModel->findColumnIndex(CSMWorld::Columns::ColumnId_Specialisation);
if ((topLeft.column() <= attributeColumn && attributeColumn <= bottomRight.column())
|| (topLeft.column() <= specialisationColumn && specialisationColumn <= bottomRight.column()))
{
clearNpcStatsCache();
std::string empty;
emit updateNpcAutocalc(0/*all*/, empty);
}
}
void CSMWorld::NpcAutoCalc::classDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
{
// update autocalculated attributes/skills of every NPC with matching class
// - mData.mAttribute[2]
// - mData.mSkills[5][2]
// - mData.mSpecialization
int attribute1Column = mClassModel->findColumnIndex(CSMWorld::Columns::ColumnId_Attribute1); // +1
int majorSkill1Column = mClassModel->findColumnIndex(CSMWorld::Columns::ColumnId_MajorSkill1); // +4
int minorSkill1Column = mClassModel->findColumnIndex(CSMWorld::Columns::ColumnId_MinorSkill1); // +4
int specialisationColumn = mClassModel->findColumnIndex(CSMWorld::Columns::ColumnId_Specialisation);
if ((topLeft.column() > attribute1Column+1 || attribute1Column > bottomRight.column())
&& (topLeft.column() > majorSkill1Column+4 || majorSkill1Column > bottomRight.column())
&& (topLeft.column() > minorSkill1Column+4 || minorSkill1Column > bottomRight.column())
&& (topLeft.column() > specialisationColumn || specialisationColumn > bottomRight.column()))
{
return;
}
// get the affected class
int idColumn = mClassModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id);
for (int classRow = topLeft.row(); classRow <= bottomRight.row(); ++classRow)
{
clearNpcStatsCache();
std::string classId =
mClassModel->data(mClassModel->index(classRow, idColumn)).toString().toUtf8().constData();
emit updateNpcAutocalc(1/*class*/, classId);
}
}
void CSMWorld::NpcAutoCalc::raceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
{
// affects racial bonus attributes & skills
// - mData.mAttributeValues[]
// - mData.mBonus[].mBonus
// - mPowers.mList[]
int attrColumn = mRaceModel->findColumnIndex(CSMWorld::Columns::ColumnId_RaceAttributes);
int bonusColumn = mRaceModel->findColumnIndex(CSMWorld::Columns::ColumnId_RaceSkillBonus);
int powersColumn = mRaceModel->findColumnIndex(CSMWorld::Columns::ColumnId_PowerList);
bool match = false;
int raceRow = topLeft.row();
int raceEnd = bottomRight.row();
if (topLeft.parent().isValid() && bottomRight.parent().isValid())
{
if ((topLeft.parent().column() <= attrColumn && attrColumn <= bottomRight.parent().column())
|| (topLeft.parent().column() <= bonusColumn && bonusColumn <= bottomRight.parent().column())
|| (topLeft.parent().column() <= powersColumn && powersColumn <= bottomRight.parent().column()))
{
match = true; // TODO: check for specific nested column?
raceRow = topLeft.parent().row();
raceEnd = bottomRight.parent().row();
}
}
else
{
if ((topLeft.column() <= attrColumn && attrColumn <= bottomRight.column())
|| (topLeft.column() <= bonusColumn && bonusColumn <= bottomRight.column())
|| (topLeft.column() <= powersColumn && powersColumn <= bottomRight.column()))
{
match = true; // maybe the whole table changed
}
}
if (!match)
return;
// update autocalculated attributes/skills of every NPC with matching race
int idColumn = mRaceModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id);
for (; raceRow <= raceEnd; ++raceRow)
{
clearNpcStatsCache();
std::string raceId =
mRaceModel->data(mRaceModel->index(raceRow, idColumn)).toString().toUtf8().constData();
emit updateNpcAutocalc(2/*race*/, raceId);
}
}
void CSMWorld::NpcAutoCalc::npcDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
{
// TODO: for now always recalculate
clearNpcStatsCache();
// TODO: check if below signal slows things down
std::string empty;
emit updateNpcAutocalc(0/*all*/, empty);
}
void CSMWorld::NpcAutoCalc::gmstDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
{
static const QStringList gmsts(QStringList()<< "fNPCbaseMagickaMult" << "fAutoSpellChance"
<< "fEffectCostMult" << "iAutoSpellAlterationMax" << "iAutoSpellConjurationMax"
<< "iAutoSpellDestructionMax" << "iAutoSpellIllusionMax" << "iAutoSpellMysticismMax"
<< "iAutoSpellRestorationMax" << "iAutoSpellTimesCanCast" << "iAutoSpellAttSkillMin");
bool match = false;
for (int row = topLeft.row(); row <= bottomRight.row(); ++row)
{
if (gmsts.contains(mData.getGmsts().getRecord(row).get().mId.c_str()))
{
match = true;
break;
}
}
if (!match)
return;
clearNpcStatsCache();
std::string empty;
emit updateNpcAutocalc(0/*all*/, empty);
}
void CSMWorld::NpcAutoCalc::clearNpcStatsCache ()
{
for (std::map<const std::string, CSMWorld::NpcStats*>::iterator it (mNpcStatCache.begin());
it != mNpcStatCache.end(); ++it)
delete it->second;
mNpcStatCache.clear();
}
CSMWorld::NpcStats* CSMWorld::NpcAutoCalc::npcAutoCalculate(const ESM::NPC& npc) const
{
CSMWorld::NpcStats *cachedStats = getCachedNpcData (npc.mId);
if (cachedStats)
return cachedStats;
int raceIndex = mData.getRaces().searchId(npc.mRace);
int classIndex = mData.getClasses().searchId(npc.mClass);
// this can happen when creating a new game from scratch
if (raceIndex == -1 || classIndex == -1)
return 0;
const ESM::Race *race = &mData.getRaces().getRecord(raceIndex).get();
const ESM::Class *class_ = &mData.getClasses().getRecord(classIndex).get();
bool autoCalc = npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS;
short level = npc.mNpdt52.mLevel;
if (autoCalc)
level = npc.mNpdt12.mLevel;
std::auto_ptr<CSMWorld::NpcStats> stats (new CSMWorld::NpcStats());
CSStore store(mData.getGmsts(), mData.getSkills(), mData.getMagicEffects(), static_cast<const CSMWorld::NestedIdCollection<ESM::Spell>&>(mData.getSpells()));
if (autoCalc)
{
AutoCalc::autoCalcAttributesImpl (&npc, race, class_, level, *stats, &store);
stats->setHealth(autoCalculateHealth(level, class_, *stats));
stats->setMana(autoCalculateMana(*stats));
stats->setFatigue(autoCalculateFatigue(*stats));
AutoCalc::autoCalcSkillsImpl(&npc, race, class_, level, *stats, &store);
AutoCalc::autoCalculateSpells(race, *stats, &store);
}
else
{
for (std::vector<std::string>::const_iterator it = npc.mSpells.mList.begin();
it != npc.mSpells.mList.end(); ++it)
{
stats->addSpell(*it);
}
}
// update spell info
const std::vector<std::string> &racePowers = race->mPowers.mList;
for (unsigned int i = 0; i < racePowers.size(); ++i)
{
int type = -1;
int spellIndex = mData.getSpells().searchId(racePowers[i]);
if (spellIndex != -1)
type = mData.getSpells().getRecord(spellIndex).get().mData.mType;
stats->addPowers(racePowers[i], type);
}
// cost/chance
int skills[ESM::Skill::Length];
if (autoCalc)
for (int i = 0; i< ESM::Skill::Length; ++i)
skills[i] = stats->getBaseSkill(i);
else
for (int i = 0; i< ESM::Skill::Length; ++i)
skills[i] = npc.mNpdt52.mSkills[i];
int attributes[ESM::Attribute::Length];
if (autoCalc)
for (int i = 0; i< ESM::Attribute::Length; ++i)
attributes[i] = stats->getBaseAttribute(i);
else
{
attributes[ESM::Attribute::Strength] = npc.mNpdt52.mStrength;
attributes[ESM::Attribute::Willpower] = npc.mNpdt52.mWillpower;
attributes[ESM::Attribute::Agility] = npc.mNpdt52.mAgility;
attributes[ESM::Attribute::Speed] = npc.mNpdt52.mSpeed;
attributes[ESM::Attribute::Endurance] = npc.mNpdt52.mEndurance;
attributes[ESM::Attribute::Personality] = npc.mNpdt52.mPersonality;
attributes[ESM::Attribute::Luck] = npc.mNpdt52.mLuck;
}
const std::vector<CSMWorld::SpellInfo>& spells = stats->spells();
for (std::vector<SpellInfo>::const_iterator it = spells.begin(); it != spells.end(); ++it)
{
int cost = -1;
int spellIndex = mData.getSpells().searchId((*it).mName);
const ESM::Spell* spell = 0;
if (spellIndex != -1)
{
spell = &mData.getSpells().getRecord(spellIndex).get();
cost = spell->mData.mCost;
int school;
float skillTerm;
AutoCalc::calcWeakestSchool(spell, skills, school, skillTerm, &store);
float chance = calcAutoCastChance(spell, skills, attributes, school, &store);
stats->addCostAndChance((*it).mName, cost, (int)ceil(chance)); // percent
}
}
if (stats.get() == 0)
return 0;
CSMWorld::NpcStats *result = stats.release();
//emit cacheNpcStats (npc.mId, result);
mNpcStatCache[npc.mId] = result;
return result;
}
//void CSMWorld::NpcAutoCalc::cacheNpcStatsEvent (const std::string& id, CSMWorld::NpcStats *stats)
//{
//mNpcStatCache[id] = stats;
//}
CSMWorld::NpcStats* CSMWorld::NpcAutoCalc::getCachedNpcData (const std::string& id) const
{
std::map<const std::string, CSMWorld::NpcStats*>::const_iterator it = mNpcStatCache.find(id);
if (it != mNpcStatCache.end())
return it->second;
else
return 0;
}

View File

@ -0,0 +1,75 @@
#ifndef CSM_WORLD_NPCAUTOCALC_H
#define CSM_WORLD_NPCAUTOCALC_H
#include <string>
#include <map>
#include <QObject>
#include <QModelIndex>
namespace ESM
{
struct NPC;
}
namespace CSMWorld
{
class Data;
class NpcStats;
class IdTable;
class IdTree;
class NpcAutoCalc : public QObject
{
Q_OBJECT
const Data& mData;
const IdTable *mSkillModel;
const IdTable *mClassModel;
const IdTree *mRaceModel;
mutable std::map<const std::string, NpcStats*> mNpcStatCache;
public:
NpcAutoCalc (const Data& data, const IdTable *gmsts, const IdTable *skills, const IdTable *classes,
const IdTree *races, const IdTree *objects);
~NpcAutoCalc ();
NpcStats* npcAutoCalculate (const ESM::NPC& npc) const;
private:
// not implemented
NpcAutoCalc (const NpcAutoCalc&);
NpcAutoCalc& operator= (const NpcAutoCalc&);
NpcStats* getCachedNpcData (const std::string& id) const;
void clearNpcStatsCache ();
signals:
// refresh NPC dialogue subviews via object table model
void updateNpcAutocalc (int type, const std::string& id);
//void cacheNpcStats (const std::string& id, NpcStats *stats) const;
private slots:
// for autocalc updates when gmst/race/class/skils tables change
void gmstDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
void raceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
void classDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
void skillDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
void npcDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
//void cacheNpcStatsEvent (const std::string& id, NpcStats *stats);
};
}
#endif // CSM_WORLD_NPCAUTOCALC_H

View File

@ -1,6 +1,7 @@
#ifndef CSM_WOLRD_RECORD_H #ifndef CSM_WOLRD_RECORD_H
#define CSM_WOLRD_RECORD_H #define CSM_WOLRD_RECORD_H
#include <memory>
#include <stdexcept> #include <stdexcept>
namespace CSMWorld namespace CSMWorld
@ -20,9 +21,9 @@ namespace CSMWorld
virtual ~RecordBase(); virtual ~RecordBase();
virtual RecordBase *clone() const = 0; virtual std::unique_ptr<RecordBase> clone() const = 0;
virtual RecordBase *modifiedCopy() const = 0; virtual std::unique_ptr<RecordBase> modifiedCopy() const = 0;
virtual void assign (const RecordBase& record) = 0; virtual void assign (const RecordBase& record) = 0;
///< Will throw an exception if the types don't match. ///< Will throw an exception if the types don't match.
@ -45,9 +46,9 @@ namespace CSMWorld
Record(State state, Record(State state,
const ESXRecordT *base = 0, const ESXRecordT *modified = 0); const ESXRecordT *base = 0, const ESXRecordT *modified = 0);
virtual RecordBase *clone() const; virtual std::unique_ptr<RecordBase> clone() const;
virtual RecordBase *modifiedCopy() const; virtual std::unique_ptr<RecordBase> modifiedCopy() const;
virtual void assign (const RecordBase& record); virtual void assign (const RecordBase& record);
@ -85,15 +86,16 @@ namespace CSMWorld
} }
template <typename ESXRecordT> template <typename ESXRecordT>
RecordBase *Record<ESXRecordT>::modifiedCopy() const std::unique_ptr<RecordBase> Record<ESXRecordT>::modifiedCopy() const
{ {
return new Record<ESXRecordT> (State_ModifiedOnly, 0, &(this->get())); return std::make_unique<Record<ESXRecordT> >(
Record<ESXRecordT>(State_ModifiedOnly, 0, &(this->get())));
} }
template <typename ESXRecordT> template <typename ESXRecordT>
RecordBase *Record<ESXRecordT>::clone() const std::unique_ptr<RecordBase> Record<ESXRecordT>::clone() const
{ {
return new Record<ESXRecordT> (*this); return std::make_unique<Record<ESXRecordT> >(Record<ESXRecordT>(*this));
} }
template <typename ESXRecordT> template <typename ESXRecordT>

View File

@ -3,11 +3,35 @@
#include <cmath> #include <cmath>
CSMWorld::CellRef::CellRef() CSMWorld::CellRef::CellRef()
: mIdNum(0)
{ {
mId.clear();
mCell.clear();
mOriginalCell.clear();
mRefNum.mIndex = 0; mRefNum.mIndex = 0;
mRefNum.mContentFile = 0; mRefNum.mContentFile = 0;
} }
CSMWorld::CellRef::CellRef (CSMWorld::CellRef&& other) : ESM::CellRef (other)
{
*this = std::move(other);
}
CSMWorld::CellRef& CSMWorld::CellRef::operator= (CSMWorld::CellRef&& other)
{
if (this != &other)
{
ESM::CellRef::operator= (other);
mIdNum = other.mIdNum;
mId = std::move(other.mId);
mCell = std::move(other.mCell);
mOriginalCell = std::move(other.mOriginalCell);
}
return *this;
}
std::pair<int, int> CSMWorld::CellRef::getCellIndex() const std::pair<int, int> CSMWorld::CellRef::getCellIndex() const
{ {
const int cellSize = 8192; const int cellSize = 8192;

View File

@ -10,11 +10,18 @@ namespace CSMWorld
/// \brief Wrapper for CellRef sub record /// \brief Wrapper for CellRef sub record
struct CellRef : public ESM::CellRef struct CellRef : public ESM::CellRef
{ {
unsigned int mIdNum;
std::string mId; std::string mId;
std::string mCell; std::string mCell;
std::string mOriginalCell; std::string mOriginalCell;
CellRef(); CellRef();
CellRef(const CellRef&) = default;
CellRef& operator= (const CellRef&) = default;
CellRef (CellRef&& other);
CellRef& operator= (CellRef&& other);
/// Calculate cell index based on coordinates (x and y) /// Calculate cell index based on coordinates (x and y)
std::pair<int, int> getCellIndex() const; std::pair<int, int> getCellIndex() const;

View File

@ -1,20 +1,48 @@
#include "refcollection.hpp" #include "refcollection.hpp"
#include <sstream>
#include <iostream>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include <components/esm/loadcell.hpp> #include <components/esm/loadcell.hpp>
#include <libs/platform/strings.h>
#include "ref.hpp" #include "ref.hpp"
#include "cell.hpp" #include "cell.hpp"
#include "universalid.hpp" #include "universalid.hpp"
#include "record.hpp" #include "record.hpp"
void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool base, namespace CSMWorld
std::map<ESM::RefNum, std::string>& cache, CSMDoc::Messages& messages)
{ {
Record<Cell> cell = mCells.getRecord (cellIndex); template<>
void Collection<CellRef, IdAccessor<CellRef> >::removeRows (int index, int count)
{
mRecords.erase(mRecords.begin()+index, mRecords.begin()+index+count);
// index map is updated in RefCollection::removeRows()
}
template<>
void Collection<CellRef, IdAccessor<CellRef> >::insertRecord (std::unique_ptr<RecordBase> record, int index,
UniversalId::Type type)
{
int size = static_cast<int>(mRecords.size());
if (index < 0 || index > size)
throw std::runtime_error("index out of range");
std::unique_ptr<Record<CellRef> > record2(static_cast<Record<CellRef>*>(record.release()));
if (index == size)
mRecords.push_back(std::move(record2));
else
mRecords.insert(mRecords.begin()+index, std::move(record2));
// index map is updated in RefCollection::insertRecord()
}
}
void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool base,
std::map<unsigned int, unsigned int>& cache, CSMDoc::Messages& messages)
{
Record<Cell> cell = mCells.getRecord(cellIndex);
Cell& cell2 = base ? cell.mBase : cell.mModified; Cell& cell2 = base ? cell.mBase : cell.mModified;
@ -34,10 +62,12 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
// ignoring moved references sub-record; instead calculate cell from coordinates // ignoring moved references sub-record; instead calculate cell from coordinates
std::pair<int, int> index = ref.getCellIndex(); std::pair<int, int> index = ref.getCellIndex();
std::ostringstream stream; char buf[100];
stream << "#" << index.first << " " << index.second; int res = snprintf(buf, 100, "#%d %d", index.first, index.second);
if (res > 0 && res < 100)
ref.mCell = stream.str(); ref.mCell = std::string(buf);
else
throw std::runtime_error("getNewId possible buffer overflow");
if (!base && // don't try to update base records if (!base && // don't try to update base records
mref.mRefNum.mIndex != 0) // MVRF tag found mref.mRefNum.mIndex != 0) // MVRF tag found
@ -57,89 +87,225 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
// message // message
if (index.first != mref.mTarget[0] || index.second != mref.mTarget[1]) if (index.first != mref.mTarget[0] || index.second != mref.mTarget[1])
{ {
std::cerr << "The Position of moved ref " CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Cell, mCells.getId(cellIndex));
<< ref.mRefID << " does not match the target cell" << std::endl; messages.add(id, "The Position of moved ref " + ref.mRefID + " (#"
std::cerr << "Position: #" << index.first << " " << index.second + std::to_string(index.first) + " " + std::to_string(index.second)
<<", Target #"<< mref.mTarget[0] << " " << mref.mTarget[1] << std::endl; + ") does not match the target cell (#"
+ std::to_string(mref.mTarget[0]) + " " + std::to_string(mref.mTarget[1]) + ")",
/*hint*/"",
CSMDoc::Message::Severity_Warning);
std::ostringstream stream; // overwrite
stream << "#" << mref.mTarget[0] << " " << mref.mTarget[1]; ref.mCell = "#" + std::to_string(mref.mTarget[0]) + " " + std::to_string(mref.mTarget[1]);
ref.mCell = stream.str(); // overwrite
} }
} }
} }
else else
ref.mCell = cell2.mId; ref.mCell = cell2.mId;
// ignore content file number unsigned int refNum = (ref.mRefNum.mIndex & 0x00ffffff) |
std::map<ESM::RefNum, std::string>::iterator iter = cache.begin(); (ref.mRefNum.hasContentFile() ? ref.mRefNum.mContentFile : 0xff) << 24;
for (; iter != cache.end(); ++iter)
{ std::map<unsigned int, unsigned int>::iterator iter = cache.find(refNum);
if (ref.mRefNum.mIndex == iter->first.mIndex)
break;
}
if (isDeleted) if (isDeleted)
{ {
if (iter==cache.end()) if (iter == cache.end())
{ {
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Cell, CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Cell, mCells.getId(cellIndex));
mCells.getId (cellIndex)); messages.add(id, "Attempt to delete a non-existing reference - RefNum index "
+ std::to_string(ref.mRefNum.mIndex) + ", refID " + ref.mRefID + ", content file index "
messages.add (id, "Attempt to delete a non-existing reference"); + std::to_string(ref.mRefNum.mContentFile),
/*hint*/"",
CSMDoc::Message::Severity_Warning);
continue; continue;
} }
int index = getIndex (iter->second); int index = getIndex(iter->second);
Record<CellRef> record = getRecord (index);
if (base) if (base)
{ {
removeRows (index, 1); removeRows(index, 1);
cache.erase (iter); cache.erase(iter);
} }
else else
{ {
record.mState = RecordBase::State_Deleted; std::unique_ptr<Record<CellRef> > record2(new Record<CellRef>(getRecord(index)));
setRecord (index, record); record2->mState = RecordBase::State_Deleted;
setRecord(index, std::move(record2));
} }
continue; continue;
} }
if (iter==cache.end()) if (iter == cache.end())
{ {
// new reference // new reference
ref.mIdNum = mNextId; // FIXME: fragile
ref.mId = getNewId(); ref.mId = getNewId();
Record<CellRef> record; cache.insert(std::make_pair(refNum, ref.mIdNum));
record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
(base ? record.mBase : record.mModified) = ref;
appendRecord (record); std::unique_ptr<Record<CellRef> > record(new Record<CellRef>);
record->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
(base ? record->mBase : record->mModified) = std::move(ref);
cache.insert (std::make_pair (ref.mRefNum, ref.mId)); appendRecord(std::move(record));
} }
else else
{ {
// old reference -> merge // old reference -> merge
ref.mId = iter->second; int index = getIndex(iter->second);
#if 0
// ref.mRefNum.mIndex : the key
// iter->second : previously cached idNum for the key
// index : position of the record for that idNum
// getRecord(index).get() : record in the index position
assert(iter->second != getRecord(index).get().mIdNum); // sanity check
int index = getIndex (ref.mId); // check if the plugin used the same RefNum index for a different record
if (ref.mRefID != getRecord(index).get().mRefID)
{
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Cell, mCells.getId(cellIndex));
messages.add(id,
"RefNum renamed from RefID \"" + getRecord(index).get().mRefID + "\" to \""
+ ref.mRefID + "\" (RefNum index " + std::to_string(ref.mRefNum.mIndex) + ")",
/*hint*/"",
CSMDoc::Message::Severity_Info);
}
#endif
ref.mId = getRecord(index).get().mId;
ref.mIdNum = extractIdNum(ref.mId);
Record<CellRef> record = getRecord (index); std::unique_ptr<Record<CellRef> > record(new Record<CellRef>(getRecord(index)));
record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_Modified; record->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_Modified;
(base ? record.mBase : record.mModified) = ref; (base ? record->mBase : record->mModified) = std::move(ref);
setRecord (index, record); setRecord(index, std::move(record));
} }
} }
} }
std::string CSMWorld::RefCollection::getNewId() std::string CSMWorld::RefCollection::getNewId()
{ {
std::ostringstream stream; char buf[100];
stream << "ref#" << mNextId++; int res = snprintf(buf, 100, "ref#%d", mNextId++);
return stream.str(); if (res > 0 && res < 100)
return std::string(buf);
else
throw std::runtime_error("getNewId possible buffer overflow");
}
unsigned int CSMWorld::RefCollection::extractIdNum (const std::string& id) const
{
std::string::size_type separator = id.find_last_of('#');
if (separator == std::string::npos)
throw std::runtime_error("invalid ref ID: " + id);
return static_cast<unsigned int>(std::stoi(id.substr(separator+1)));
}
int CSMWorld::RefCollection::getIndex (unsigned int id) const
{
int index = searchId(id);
if (index == -1)
throw std::runtime_error("invalid RefNum: " + std::to_string(id));
return index;
}
int CSMWorld::RefCollection::searchId (unsigned int id) const
{
std::map<unsigned int, int>::const_iterator iter = mRefIndex.find(id);
if (iter == mRefIndex.end())
return -1;
return iter->second;
}
void CSMWorld::RefCollection::removeRows (int index, int count)
{
Collection<CellRef, IdAccessor<CellRef> >::removeRows(index, count); // erase records only
std::map<unsigned int, int>::iterator iter = mRefIndex.begin();
while (iter != mRefIndex.end())
{
if (iter->second>=index)
{
if (iter->second >= index+count)
{
iter->second -= count;
++iter;
}
else
mRefIndex.erase(iter++);
}
else
++iter;
}
}
void CSMWorld::RefCollection::appendBlankRecord (const std::string& id, UniversalId::Type type)
{
std::unique_ptr<Record<CellRef> > record2(new Record<CellRef>);
record2->mState = Record<CellRef>::State_ModifiedOnly;
record2->mModified.blank();
record2->get().mId = id;
record2->get().mIdNum = extractIdNum(id);
Collection<CellRef, IdAccessor<CellRef> >::appendRecord(std::move(record2));
}
void CSMWorld::RefCollection::cloneRecord (const std::string& origin,
const std::string& destination,
const UniversalId::Type type)
{
std::unique_ptr<Record<CellRef> > copy(new Record<CellRef>);
copy->mModified = getRecord(origin).get();
copy->mState = RecordBase::State_ModifiedOnly;
copy->get().mId = destination;
copy->get().mIdNum = extractIdNum(destination);
insertRecord(std::move(copy), getAppendIndex(destination, type)); // call RefCollection::insertRecord()
}
int CSMWorld::RefCollection::searchId (const std::string& id) const
{
return searchId(extractIdNum(id));
}
void CSMWorld::RefCollection::appendRecord (std::unique_ptr<RecordBase> record, UniversalId::Type type)
{
int index = getAppendIndex(/*id*/"", type); // for CellRef records id is ignored
mRefIndex.insert(std::make_pair(static_cast<Record<CellRef>*>(record.get())->get().mIdNum, index));
Collection<CellRef, IdAccessor<CellRef> >::insertRecord(std::move(record), index, type); // add records only
}
void CSMWorld::RefCollection::insertRecord (std::unique_ptr<RecordBase> record, int index,
UniversalId::Type type)
{
int size = getAppendIndex(/*id*/"", type); // for CellRef records id is ignored
unsigned int idNum = static_cast<Record<CellRef>*>(record.get())->get().mIdNum;
Collection<CellRef, IdAccessor<CellRef> >::insertRecord(std::move(record), index, type); // add records only
if (index < size-1)
{
for (std::map<unsigned int, int>::iterator iter(mRefIndex.begin()); iter != mRefIndex.end(); ++iter)
{
if (iter->second >= index)
++(iter->second);
}
}
mRefIndex.insert(std::make_pair(idNum, index));
} }

View File

@ -14,12 +14,27 @@ namespace CSMWorld
struct Cell; struct Cell;
class UniversalId; class UniversalId;
template<>
void Collection<CellRef, IdAccessor<CellRef> >::removeRows (int index, int count);
template<>
void Collection<CellRef, IdAccessor<CellRef> >::insertRecord (std::unique_ptr<RecordBase> record, int index,
UniversalId::Type type);
/// \brief References in cells /// \brief References in cells
class RefCollection : public Collection<CellRef> class RefCollection : public Collection<CellRef>
{ {
Collection<Cell>& mCells; Collection<Cell>& mCells;
std::map<unsigned int, int> mRefIndex;
int mNextId; int mNextId;
unsigned int extractIdNum(const std::string& id) const;
int getIndex (unsigned int id) const;
int searchId (unsigned int id) const;
public: public:
// MSVC needs the constructor for a class inheriting a template to be defined in header // MSVC needs the constructor for a class inheriting a template to be defined in header
RefCollection (Collection<Cell>& cells) RefCollection (Collection<Cell>& cells)
@ -27,10 +42,28 @@ namespace CSMWorld
{} {}
void load (ESM::ESMReader& reader, int cellIndex, bool base, void load (ESM::ESMReader& reader, int cellIndex, bool base,
std::map<ESM::RefNum, std::string>& cache, CSMDoc::Messages& messages); std::map<unsigned int, unsigned int>& cache, CSMDoc::Messages& messages);
///< Load a sequence of references. ///< Load a sequence of references.
std::string getNewId(); std::string getNewId();
virtual void removeRows (int index, int count);
virtual void appendBlankRecord (const std::string& id,
UniversalId::Type type = UniversalId::Type_None);
virtual void cloneRecord (const std::string& origin,
const std::string& destination,
const UniversalId::Type type);
virtual int searchId (const std::string& id) const;
virtual void appendRecord (std::unique_ptr<RecordBase> record,
UniversalId::Type type = UniversalId::Type_None);
virtual void insertRecord (std::unique_ptr<RecordBase> record,
int index,
UniversalId::Type type = UniversalId::Type_None);
}; };
} }

View File

@ -12,6 +12,7 @@
#include "usertype.hpp" #include "usertype.hpp"
#include "idtree.hpp" #include "idtree.hpp"
#include "npcstats.hpp" #include "npcstats.hpp"
#include "npcautocalc.hpp"
CSMWorld::PotionColumns::PotionColumns (const InventoryColumns& columns) CSMWorld::PotionColumns::PotionColumns (const InventoryColumns& columns)
: InventoryColumns (columns) {} : InventoryColumns (columns) {}
@ -777,7 +778,7 @@ void CSMWorld::NpcRefIdAdapter::setData (const RefIdColumn *column, RefIdData& d
{ {
if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
{ {
CSMWorld::NpcStats *stats = mData.npcAutoCalculate(npc); CSMWorld::NpcStats *stats = mData.getNpcAutoCalc().npcAutoCalculate(npc);
if (!stats) if (!stats)
{ {
record.setModified (npc); record.setModified (npc);
@ -817,7 +818,7 @@ void CSMWorld::NpcRefIdAdapter::setData (const RefIdColumn *column, RefIdData& d
{ {
npc.mNpdtType = ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS; npc.mNpdtType = ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS;
npc.mNpdt12.mLevel = npc.mNpdt52.mLevel; // for NPC's loaded as non-autocalc npc.mNpdt12.mLevel = npc.mNpdt52.mLevel; // for NPC's loaded as non-autocalc
mData.npcAutoCalculate(npc); mData.getNpcAutoCalc().npcAutoCalculate(npc);
} }
} }
} }
@ -888,7 +889,7 @@ QVariant CSMWorld::NpcAttributesRefIdAdapter::getNestedData (const RefIdColumn *
else if (subColIndex == 1) else if (subColIndex == 1)
if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
{ {
CSMWorld::NpcStats *stats = mData.npcAutoCalculate(npc); CSMWorld::NpcStats *stats = mData.getNpcAutoCalc().npcAutoCalculate(npc);
if (!stats) if (!stats)
return QVariant(); return QVariant();
@ -1020,7 +1021,7 @@ QVariant CSMWorld::NpcSkillsRefIdAdapter::getNestedData (const RefIdColumn *colu
{ {
if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
{ {
CSMWorld::NpcStats *stats = mData.npcAutoCalculate(npc); CSMWorld::NpcStats *stats = mData.getNpcAutoCalc().npcAutoCalculate(npc);
if (!stats) if (!stats)
return QVariant(); return QVariant();
@ -1108,7 +1109,7 @@ QVariant CSMWorld::NpcMiscRefIdAdapter::getNestedData (const RefIdColumn *column
if (autoCalc) if (autoCalc)
{ {
CSMWorld::NpcStats *stats = mData.npcAutoCalculate(npc); CSMWorld::NpcStats *stats = mData.getNpcAutoCalc().npcAutoCalculate(npc);
switch (subColIndex) switch (subColIndex)
{ {
@ -1732,7 +1733,7 @@ QVariant NestedSpellRefIdAdapter<ESM::NPC>::getNestedData (const RefIdColumn *co
const Record<ESM::NPC>& record = const Record<ESM::NPC>& record =
static_cast<const Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (index, mType))); static_cast<const Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));
CSMWorld::NpcStats *stats = mData.npcAutoCalculate(record.get()); CSMWorld::NpcStats *stats = mData.getNpcAutoCalc().npcAutoCalculate(record.get());
if (!stats) if (!stats)
return QVariant(); return QVariant();
@ -1766,7 +1767,7 @@ int NestedSpellRefIdAdapter<ESM::NPC>::getNestedRowsCount(const RefIdColumn *col
const Record<ESM::NPC>& record = const Record<ESM::NPC>& record =
static_cast<const Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (index, mType))); static_cast<const Record<ESM::NPC>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));
CSMWorld::NpcStats *stats = mData.npcAutoCalculate(record.get()); CSMWorld::NpcStats *stats = mData.getNpcAutoCalc().npcAutoCalculate(record.get());
if (!stats) if (!stats)
return 0; return 0;

View File

@ -810,30 +810,31 @@ int CSMWorld::RefIdCollection::searchId (const std::string& id) const
return mData.localToGlobalIndex (localIndex); return mData.localToGlobalIndex (localIndex);
} }
void CSMWorld::RefIdCollection::replace (int index, const RecordBase& record) void CSMWorld::RefIdCollection::replace (int index, std::unique_ptr<RecordBase> record)
{ {
mData.getRecord (mData.globalToLocalIndex (index)).assign (record); mData.getRecord (mData.globalToLocalIndex (index)).assign (*record.release());
} }
void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin, void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin,
const std::string& destination, const std::string& destination,
const CSMWorld::UniversalId::Type type) const CSMWorld::UniversalId::Type type)
{ {
std::auto_ptr<RecordBase> newRecord(mData.getRecord(mData.searchId(origin)).modifiedCopy()); std::unique_ptr<RecordBase> newRecord =
std::move(mData.getRecord(mData.searchId(origin)).modifiedCopy());
mAdapters.find(type)->second->setId(*newRecord, destination); mAdapters.find(type)->second->setId(*newRecord, destination);
mData.insertRecord(*newRecord, type, destination); mData.insertRecord(std::move(newRecord), type, destination);
} }
void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record, void CSMWorld::RefIdCollection::appendRecord (std::unique_ptr<RecordBase> record,
UniversalId::Type type) UniversalId::Type type)
{ {
std::string id = findAdapter (type).getId (record); std::string id = findAdapter (type).getId (*record.get());
int index = mData.getAppendIndex (type); int index = mData.getAppendIndex (type);
mData.appendRecord (type, id, false); mData.appendRecord (type, id, false);
mData.getRecord (mData.globalToLocalIndex (index)).assign (record); mData.getRecord (mData.globalToLocalIndex (index)).assign (*record.release());
} }
const CSMWorld::RecordBase& CSMWorld::RefIdCollection::getRecord (const std::string& id) const const CSMWorld::RecordBase& CSMWorld::RefIdCollection::getRecord (const std::string& id) const

View File

@ -89,12 +89,12 @@ namespace CSMWorld
////< Search record with \a id. ////< Search record with \a id.
/// \return index of record (if found) or -1 (not found) /// \return index of record (if found) or -1 (not found)
virtual void replace (int index, const RecordBase& record); virtual void replace (int index, std::unique_ptr<RecordBase> record);
///< If the record type does not match, an exception is thrown. ///< If the record type does not match, an exception is thrown.
/// ///
/// \attention \a record must not change the ID. /// \attention \a record must not change the ID.
virtual void appendRecord (const RecordBase& record, UniversalId::Type type); virtual void appendRecord (std::unique_ptr<RecordBase> record, UniversalId::Type type);
///< If the record type does not match, an exception is thrown. ///< If the record type does not match, an exception is thrown.
/// ///
///< \param type Will be ignored, unless the collection supports multiple record types ///< \param type Will be ignored, unless the collection supports multiple record types

View File

@ -367,7 +367,7 @@ const CSMWorld::RefIdDataContainer< ESM::Static >& CSMWorld::RefIdData::getStati
return mStatics; return mStatics;
} }
void CSMWorld::RefIdData::insertRecord (CSMWorld::RecordBase& record, CSMWorld::UniversalId::Type type, const std::string& id) void CSMWorld::RefIdData::insertRecord (std::unique_ptr<CSMWorld::RecordBase> record, CSMWorld::UniversalId::Type type, const std::string& id)
{ {
std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator iter = std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator iter =
mRecordContainers.find (type); mRecordContainers.find (type);
@ -375,7 +375,7 @@ void CSMWorld::RefIdData::insertRecord (CSMWorld::RecordBase& record, CSMWorld::
if (iter==mRecordContainers.end()) if (iter==mRecordContainers.end())
throw std::logic_error ("invalid local index type"); throw std::logic_error ("invalid local index type");
iter->second->insertRecord(record); iter->second->insertRecord(std::move(record));
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id), mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id),
LocalIndex (iter->second->getSize()-1, type))); LocalIndex (iter->second->getSize()-1, type)));
@ -387,9 +387,7 @@ void CSMWorld::RefIdData::copyTo (int index, RefIdData& target) const
RefIdDataContainerBase *source = mRecordContainers.find (localIndex.second)->second; RefIdDataContainerBase *source = mRecordContainers.find (localIndex.second)->second;
std::string id = source->getId (localIndex.first); target.insertRecord(source->getRecord(localIndex.first).modifiedCopy(),
localIndex.second,
std::auto_ptr<CSMWorld::RecordBase> newRecord (source->getRecord (localIndex.first).modifiedCopy()); source->getId(localIndex.first));
target.insertRecord (*newRecord, localIndex.second, id);
} }

View File

@ -3,6 +3,7 @@
#include <vector> #include <vector>
#include <map> #include <map>
#include <memory>
#include <components/esm/loadacti.hpp> #include <components/esm/loadacti.hpp>
#include <components/esm/loadalch.hpp> #include <components/esm/loadalch.hpp>
@ -49,7 +50,7 @@ namespace CSMWorld
virtual void appendRecord (const std::string& id, bool base) = 0; virtual void appendRecord (const std::string& id, bool base) = 0;
virtual void insertRecord (RecordBase& record) = 0; virtual void insertRecord (std::unique_ptr<RecordBase> record) = 0;
virtual int load (ESM::ESMReader& reader, bool base) = 0; virtual int load (ESM::ESMReader& reader, bool base) = 0;
///< \return index of a loaded record or -1 if no record was loaded ///< \return index of a loaded record or -1 if no record was loaded
@ -64,7 +65,7 @@ namespace CSMWorld
template<typename RecordT> template<typename RecordT>
struct RefIdDataContainer : public RefIdDataContainerBase struct RefIdDataContainer : public RefIdDataContainerBase
{ {
std::vector<Record<RecordT> > mContainer; std::vector<std::unique_ptr<Record<RecordT> > > mContainer;
virtual int getSize() const; virtual int getSize() const;
@ -74,7 +75,7 @@ namespace CSMWorld
virtual void appendRecord (const std::string& id, bool base); virtual void appendRecord (const std::string& id, bool base);
virtual void insertRecord (RecordBase& record); virtual void insertRecord (std::unique_ptr<RecordBase> record);
virtual int load (ESM::ESMReader& reader, bool base); virtual int load (ESM::ESMReader& reader, bool base);
///< \return index of a loaded record or -1 if no record was loaded ///< \return index of a loaded record or -1 if no record was loaded
@ -87,10 +88,18 @@ namespace CSMWorld
}; };
template<typename RecordT> template<typename RecordT>
void RefIdDataContainer<RecordT>::insertRecord(RecordBase& record) void RefIdDataContainer<RecordT>::insertRecord(std::unique_ptr<RecordBase> record)
{ {
Record<RecordT>& newRecord = dynamic_cast<Record<RecordT>& >(record); Record<RecordT> *tmp = dynamic_cast<Record<RecordT>*>(record.get());
mContainer.push_back(newRecord); if(tmp != nullptr)
{
record.release();
std::unique_ptr<Record<RecordT> > newRecord;
newRecord.reset(tmp);
mContainer.push_back(std::move(newRecord));
}
else
throw std::runtime_error ("invalid record for RefIdDataContainer");
} }
template<typename RecordT> template<typename RecordT>
@ -102,27 +111,27 @@ namespace CSMWorld
template<typename RecordT> template<typename RecordT>
const RecordBase& RefIdDataContainer<RecordT>::getRecord (int index) const const RecordBase& RefIdDataContainer<RecordT>::getRecord (int index) const
{ {
return mContainer.at (index); return *mContainer.at (index);
} }
template<typename RecordT> template<typename RecordT>
RecordBase& RefIdDataContainer<RecordT>::getRecord (int index) RecordBase& RefIdDataContainer<RecordT>::getRecord (int index)
{ {
return mContainer.at (index); return *mContainer.at (index);
} }
template<typename RecordT> template<typename RecordT>
void RefIdDataContainer<RecordT>::appendRecord (const std::string& id, bool base) void RefIdDataContainer<RecordT>::appendRecord (const std::string& id, bool base)
{ {
Record<RecordT> record; std::unique_ptr<Record<RecordT> > record(new Record<RecordT>);
record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; record->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
record.mBase.mId = id; record->mBase.mId = id;
record.mModified.mId = id; record->mModified.mId = id;
(base ? record.mBase : record.mModified).blank(); (base ? record->mBase : record->mModified).blank();
mContainer.push_back (record); mContainer.push_back (std::move(record));
} }
template<typename RecordT> template<typename RecordT>
@ -137,7 +146,7 @@ namespace CSMWorld
int numRecords = static_cast<int>(mContainer.size()); int numRecords = static_cast<int>(mContainer.size());
for (; index < numRecords; ++index) for (; index < numRecords; ++index)
{ {
if (Misc::StringUtils::ciEqual(mContainer[index].get().mId, record.mId)) if (Misc::StringUtils::ciEqual(mContainer[index]->get().mId, record.mId))
{ {
break; break;
} }
@ -155,7 +164,7 @@ namespace CSMWorld
// Flag the record as Deleted even for a base content file. // Flag the record as Deleted even for a base content file.
// RefIdData is responsible for its erasure. // RefIdData is responsible for its erasure.
mContainer[index].mState = RecordBase::State_Deleted; mContainer[index]->mState = RecordBase::State_Deleted;
} }
else else
{ {
@ -164,16 +173,16 @@ namespace CSMWorld
appendRecord(record.mId, base); appendRecord(record.mId, base);
if (base) if (base)
{ {
mContainer.back().mBase = record; mContainer.back()->mBase = record;
} }
else else
{ {
mContainer.back().mModified = record; mContainer.back()->mModified = record;
} }
} }
else if (!base) else if (!base)
{ {
mContainer[index].setModified(record); mContainer[index]->setModified(record);
} }
} }
@ -192,13 +201,13 @@ namespace CSMWorld
template<typename RecordT> template<typename RecordT>
std::string RefIdDataContainer<RecordT>::getId (int index) const std::string RefIdDataContainer<RecordT>::getId (int index) const
{ {
return mContainer.at (index).get().mId; return mContainer.at (index)->get().mId;
} }
template<typename RecordT> template<typename RecordT>
void RefIdDataContainer<RecordT>::save (int index, ESM::ESMWriter& writer) const void RefIdDataContainer<RecordT>::save (int index, ESM::ESMWriter& writer) const
{ {
Record<RecordT> record = mContainer.at(index); const Record<RecordT>& record = *mContainer.at(index);
if (record.isModified() || record.mState == RecordBase::State_Deleted) if (record.isModified() || record.mState == RecordBase::State_Deleted)
{ {
@ -260,7 +269,7 @@ namespace CSMWorld
void erase (int index, int count); void erase (int index, int count);
void insertRecord (CSMWorld::RecordBase& record, CSMWorld::UniversalId::Type type, void insertRecord (std::unique_ptr<RecordBase> record, CSMWorld::UniversalId::Type type,
const std::string& id); const std::string& id);
const RecordBase& getRecord (const LocalIndex& index) const; const RecordBase& getRecord (const LocalIndex& index) const;

View File

@ -4,44 +4,32 @@
#include <stdexcept> #include <stdexcept>
#include <algorithm> #include <algorithm>
#include <OgreResourceGroupManager.h>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
CSMWorld::Resources::Resources (const std::string& baseDirectory, UniversalId::Type type, CSMWorld::Resources::Resources (const std::string& baseDirectory, UniversalId::Type type,
const char * const *extensions) std::vector<Ogre::StringVectorPtr> resources, const char * const *extensions)
: mBaseDirectory (baseDirectory), mType (type) : mBaseDirectory (baseDirectory), mType (type)
{ {
int baseSize = mBaseDirectory.size(); int baseSize = mBaseDirectory.size();
Ogre::StringVector resourcesGroups = for (std::vector<Ogre::StringVectorPtr>::iterator iter(resources.begin()); iter != resources.end(); ++iter)
Ogre::ResourceGroupManager::getSingleton().getResourceGroups();
for (Ogre::StringVector::iterator iter (resourcesGroups.begin());
iter!=resourcesGroups.end(); ++iter)
{ {
if (*iter=="General" || *iter=="Internal" || *iter=="Autodetect") // populate mFiles and mIndex
continue; for (Ogre::StringVector::const_iterator iter2 ((*iter)->begin()); iter2 != (*iter)->end(); ++iter2)
Ogre::StringVectorPtr resources =
Ogre::ResourceGroupManager::getSingleton().listResourceNames (*iter);
for (Ogre::StringVector::const_iterator iter (resources->begin());
iter!=resources->end(); ++iter)
{ {
if (static_cast<int> (iter->size())<baseSize+1 || if (static_cast<int> (iter2->size())<baseSize+1 ||
iter->substr (0, baseSize)!=mBaseDirectory || iter2->substr (0, baseSize)!=mBaseDirectory ||
((*iter)[baseSize]!='/' && (*iter)[baseSize]!='\\')) ((*iter2)[baseSize]!='/' && (*iter2)[baseSize]!='\\'))
continue; continue;
if (extensions) if (extensions)
{ {
std::string::size_type index = iter->find_last_of ('.'); std::string::size_type index = iter2->find_last_of ('.');
if (index==std::string::npos) if (index==std::string::npos)
continue; continue;
std::string extension = iter->substr (index+1); std::string extension = iter2->substr (index+1);
int i = 0; int i = 0;
@ -53,7 +41,7 @@ CSMWorld::Resources::Resources (const std::string& baseDirectory, UniversalId::T
continue; continue;
} }
std::string file = iter->substr (baseSize+1); std::string file = iter2->substr (baseSize+1);
mFiles.push_back (file); mFiles.push_back (file);
std::replace (file.begin(), file.end(), '\\', '/'); std::replace (file.begin(), file.end(), '\\', '/');
mIndex.insert (std::make_pair ( mIndex.insert (std::make_pair (

View File

@ -5,6 +5,8 @@
#include <map> #include <map>
#include <vector> #include <vector>
#include <OgreStringVector.h>
#include "universalid.hpp" #include "universalid.hpp"
namespace CSMWorld namespace CSMWorld
@ -20,6 +22,7 @@ namespace CSMWorld
/// \param type Type of resources in this table. /// \param type Type of resources in this table.
Resources (const std::string& baseDirectory, UniversalId::Type type, Resources (const std::string& baseDirectory, UniversalId::Type type,
std::vector<Ogre::StringVectorPtr> resources,
const char * const *extensions = 0); const char * const *extensions = 0);
int getSize() const; int getSize() const;

View File

@ -2,6 +2,8 @@
#include <stdexcept> #include <stdexcept>
#include <OgreResourceGroupManager.h>
void CSMWorld::ResourcesManager::addResources (const Resources& resources) void CSMWorld::ResourcesManager::addResources (const Resources& resources)
{ {
mResources.insert (std::make_pair (resources.getType(), resources)); mResources.insert (std::make_pair (resources.getType(), resources));
@ -11,14 +13,45 @@ void CSMWorld::ResourcesManager::addResources (const Resources& resources)
void CSMWorld::ResourcesManager::listResources() void CSMWorld::ResourcesManager::listResources()
{ {
// Following code was taken out of Resources ctor, since it was being executed each time
// and slow enough to showe up in the profiler.
//
// See Editor ctor which calls Bsa::registerResources()
//
// resourceGroups include those from config files, e.g.:
//
// C:/Program Files\OpenMW\data
// C:/Program Files (x86)\Bethesda Softworks\Morrowind\Data Files
//
// and from archives:
//
// C:/Program Files (x86)\Bethesda Softworks\Morrowind\Data Files\Morrowind.bsa
// C:/Program Files (x86)\Bethesda Softworks\Morrowind\Data Files\Tribunal.bsa
// C:/Program Files (x86)\Bethesda Softworks\Morrowind\Data Files\Bloodmoon.bsa
//
std::vector<Ogre::StringVectorPtr> resources;
Ogre::StringVector resourcesGroups =
Ogre::ResourceGroupManager::getSingleton().getResourceGroups();
for (Ogre::StringVector::iterator iter (resourcesGroups.begin());
iter!=resourcesGroups.end(); ++iter)
{
if (*iter=="General" || *iter=="Internal" || *iter=="Autodetect")
continue;
resources.push_back(
Ogre::ResourceGroupManager::getSingleton().listResourceNames (*iter));
}
static const char * const sMeshTypes[] = { "nif", 0 }; static const char * const sMeshTypes[] = { "nif", 0 };
addResources (Resources ("meshes", UniversalId::Type_Mesh, sMeshTypes)); addResources (Resources ("meshes", UniversalId::Type_Mesh, resources, sMeshTypes));
addResources (Resources ("icons", UniversalId::Type_Icon)); addResources (Resources ("icons", UniversalId::Type_Icon, resources));
addResources (Resources ("music", UniversalId::Type_Music)); addResources (Resources ("music", UniversalId::Type_Music, resources));
addResources (Resources ("sound", UniversalId::Type_SoundRes)); addResources (Resources ("sound", UniversalId::Type_SoundRes, resources));
addResources (Resources ("textures", UniversalId::Type_Texture)); addResources (Resources ("textures", UniversalId::Type_Texture, resources));
addResources (Resources ("videos", UniversalId::Type_Video)); addResources (Resources ("videos", UniversalId::Type_Video, resources));
} }
const CSMWorld::Resources& CSMWorld::ResourcesManager::get (UniversalId::Type type) const const CSMWorld::Resources& CSMWorld::ResourcesManager::get (UniversalId::Type type) const

View File

@ -17,7 +17,7 @@ void CSVDoc::LoadingDocument::closeEvent (QCloseEvent *event)
} }
CSVDoc::LoadingDocument::LoadingDocument (CSMDoc::Document *document) CSVDoc::LoadingDocument::LoadingDocument (CSMDoc::Document *document)
: mDocument (document), mAborted (false), mMessages (0), mTotalRecords (0) : mDocument (document), mAborted (false), mMessages (0), mRecordsLabel (0), mTotalRecordsLabel (0)
{ {
setWindowTitle (QString::fromUtf8((std::string("Opening ") + document->getSavePath().filename().string()).c_str())); setWindowTitle (QString::fromUtf8((std::string("Opening ") + document->getSavePath().filename().string()).c_str()));
@ -25,26 +25,25 @@ CSVDoc::LoadingDocument::LoadingDocument (CSMDoc::Document *document)
mLayout = new QVBoxLayout (this); mLayout = new QVBoxLayout (this);
// file progress // total progress
mFile = new QLabel (this); mTotalRecordsLabel = new QLabel (this);
mLayout->addWidget (mFile); mLayout->addWidget (mTotalRecordsLabel);
mFileProgress = new QProgressBar (this); mTotalProgress = new QProgressBar (this);
mLayout->addWidget (mFileProgress); mLayout->addWidget (mTotalProgress);
int size = static_cast<int> (document->getContentFiles().size())+1; mTotalProgress->setMinimum (0);
if (document->isNew()) mTotalProgress->setMaximum (document->getData().getTotalRecords(document->getContentFiles()));
--size; mTotalProgress->setTextVisible (true);
mTotalProgress->setValue (0);
mTotalRecords = 0;
mFileProgress->setMinimum (0); mFilesLoaded = 0;
mFileProgress->setMaximum (size);
mFileProgress->setTextVisible (true);
mFileProgress->setValue (0);
// record progress // record progress
mLayout->addWidget (mRecords = new QLabel ("Records", this)); mLayout->addWidget (mRecordsLabel = new QLabel ("Records", this));
mRecordProgress = new QProgressBar (this); mRecordProgress = new QProgressBar (this);
@ -74,29 +73,32 @@ CSVDoc::LoadingDocument::LoadingDocument (CSMDoc::Document *document)
connect (mButtons, SIGNAL (rejected()), this, SLOT (cancel())); connect (mButtons, SIGNAL (rejected()), this, SLOT (cancel()));
} }
void CSVDoc::LoadingDocument::nextStage (const std::string& name, int totalRecords) void CSVDoc::LoadingDocument::nextStage (const std::string& name, int fileRecords)
{ {
mFile->setText (QString::fromUtf8 (("Loading: " + name).c_str())); ++mFilesLoaded;
size_t numFiles = mDocument->getContentFiles().size();
mFileProgress->setValue (mFileProgress->value()+1); mTotalRecordsLabel->setText (QString::fromUtf8 (("Loading: "+name
+" ("+std::to_string(mFilesLoaded)+" of "+std::to_string((numFiles))+")").c_str()));
mTotalRecords = mTotalProgress->value();
mRecordProgress->setValue (0); mRecordProgress->setValue (0);
mRecordProgress->setMaximum (totalRecords>0 ? totalRecords : 1); mRecordProgress->setMaximum (fileRecords>0 ? fileRecords : 1);
mTotalRecords = totalRecords; mRecords = fileRecords;
} }
void CSVDoc::LoadingDocument::nextRecord (int records) void CSVDoc::LoadingDocument::nextRecord (int records)
{ {
if (records<=mTotalRecords) if (records <= mRecords)
{ {
mRecordProgress->setValue (records); mTotalProgress->setValue (mTotalRecords+records);
std::ostringstream stream; mRecordProgress->setValue(records);
stream << "Records: " << records << " of " << mTotalRecords; mRecordsLabel->setText(QString::fromStdString(
"Records: "+std::to_string(records)+" of "+std::to_string(mRecords)));
mRecords->setText (QString::fromUtf8 (stream.str().c_str()));
} }
} }
@ -176,12 +178,12 @@ void CSVDoc::Loader::loadingStopped (CSMDoc::Document *document, bool completed,
} }
void CSVDoc::Loader::nextStage (CSMDoc::Document *document, const std::string& name, void CSVDoc::Loader::nextStage (CSMDoc::Document *document, const std::string& name,
int totalRecords) int fileRecords)
{ {
std::map<CSMDoc::Document *, LoadingDocument *>::iterator iter = mDocuments.find (document); std::map<CSMDoc::Document *, LoadingDocument *>::iterator iter = mDocuments.find (document);
if (iter!=mDocuments.end()) if (iter!=mDocuments.end())
iter->second->nextStage (name, totalRecords); iter->second->nextStage (name, fileRecords);
} }
void CSVDoc::Loader::nextRecord (CSMDoc::Document *document, int records) void CSVDoc::Loader::nextRecord (CSMDoc::Document *document, int records)

View File

@ -25,16 +25,18 @@ namespace CSVDoc
Q_OBJECT Q_OBJECT
CSMDoc::Document *mDocument; CSMDoc::Document *mDocument;
QLabel *mFile; QLabel *mTotalRecordsLabel;
QLabel *mRecords; QLabel *mRecordsLabel;
QProgressBar *mFileProgress; QProgressBar *mTotalProgress;
QProgressBar *mRecordProgress; QProgressBar *mRecordProgress;
bool mAborted; bool mAborted;
QDialogButtonBox *mButtons; QDialogButtonBox *mButtons;
QLabel *mError; QLabel *mError;
QListWidget *mMessages; QListWidget *mMessages;
QVBoxLayout *mLayout; QVBoxLayout *mLayout;
int mRecords;
int mTotalRecords; int mTotalRecords;
int mFilesLoaded;
private: private:

View File

@ -38,9 +38,9 @@ void CSVFilter::FilterBox::dropEvent (QDropEvent* event)
if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped
return; return;
std::vector<CSMWorld::UniversalId> data = mime->getData(); std::vector<CSMWorld::UniversalId> mimeData = mime->getData();
emit recordDropped(data, event->proposedAction()); emit recordDropped(mimeData, event->proposedAction());
} }
void CSVFilter::FilterBox::dragEnterEvent (QDragEnterEvent* event) void CSVFilter::FilterBox::dragEnterEvent (QDragEnterEvent* event)

View File

@ -3,6 +3,8 @@
#include <OgreSceneManager.h> #include <OgreSceneManager.h>
#include <OgreSceneNode.h> #include <OgreSceneNode.h>
#include <OgreManualObject.h> #include <OgreManualObject.h>
#include <OgreMaterialManager.h>
#include <OgreTechnique.h>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include <components/esm/loadland.hpp> #include <components/esm/loadland.hpp>

View File

@ -536,18 +536,18 @@ std::pair< int, int > CSVRender::PagedWorldspaceWidget::getCoordinatesFromId (co
} }
bool CSVRender::PagedWorldspaceWidget::handleDrop ( bool CSVRender::PagedWorldspaceWidget::handleDrop (
const std::vector< CSMWorld::UniversalId >& data, DropType type) const std::vector< CSMWorld::UniversalId >& dropData, DropType type)
{ {
if (WorldspaceWidget::handleDrop (data, type)) if (WorldspaceWidget::handleDrop (dropData, type))
return true; return true;
if (type!=Type_CellsExterior) if (type!=Type_CellsExterior)
return false; return false;
bool selectionChanged = false; bool selectionChanged = false;
for (unsigned i = 0; i < data.size(); ++i) for (unsigned i = 0; i < dropData.size(); ++i)
{ {
std::pair<int, int> coordinates(getCoordinatesFromId(data[i].getId())); std::pair<int, int> coordinates(getCoordinatesFromId(dropData[i].getId()));
if (mSelection.add(CSMWorld::CellCoordinates(coordinates.first, coordinates.second))) if (mSelection.add(CSMWorld::CellCoordinates(coordinates.first, coordinates.second)))
{ {
selectionChanged = true; selectionChanged = true;

View File

@ -74,6 +74,8 @@ namespace CSVRender
/// \todo make shortcut configurable /// \todo make shortcut configurable
QShortcut *focusToolbar = new QShortcut (Qt::Key_T, this, 0, 0, Qt::WidgetWithChildrenShortcut); QShortcut *focusToolbar = new QShortcut (Qt::Key_T, this, 0, 0, Qt::WidgetWithChildrenShortcut);
connect (focusToolbar, SIGNAL (activated()), this, SIGNAL (focusToolbarRequest())); connect (focusToolbar, SIGNAL (activated()), this, SIGNAL (focusToolbarRequest()));
updateOgreWindow();
} }
CSVWidget::SceneToolMode *SceneWidget::makeLightingSelector (CSVWidget::SceneToolbar *parent) CSVWidget::SceneToolMode *SceneWidget::makeLightingSelector (CSVWidget::SceneToolbar *parent)

View File

@ -105,7 +105,7 @@ TextOverlay::TextOverlay(const Ogre::MovableObject* obj, const Ogre::Camera* cam
"TransOverlayMaterial"); "TransOverlayMaterial");
if(mQuadMaterial.isNull()) if(mQuadMaterial.isNull())
{ {
Ogre::MaterialPtr mQuadMaterial = Ogre::MaterialManager::getSingleton().create( mQuadMaterial = Ogre::MaterialManager::getSingleton().create(
"TransOverlayMaterial", "TransOverlayMaterial",
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true ); Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true );
Ogre::Pass *pass = mQuadMaterial->getTechnique( 0 )->getPass( 0 ); Ogre::Pass *pass = mQuadMaterial->getTechnique( 0 )->getPass( 0 );

View File

@ -84,21 +84,21 @@ void CSVRender::UnpagedWorldspaceWidget::cellRowsAboutToBeRemoved (const QModelI
emit closeRequest(); emit closeRequest();
} }
bool CSVRender::UnpagedWorldspaceWidget::handleDrop (const std::vector<CSMWorld::UniversalId>& data, DropType type) bool CSVRender::UnpagedWorldspaceWidget::handleDrop (const std::vector<CSMWorld::UniversalId>& dropData, DropType type)
{ {
if (WorldspaceWidget::handleDrop (data, type)) if (WorldspaceWidget::handleDrop (dropData, type))
return true; return true;
if (type!=Type_CellsInterior) if (type!=Type_CellsInterior)
return false; return false;
mCellId = data.begin()->getId(); mCellId = dropData.begin()->getId();
Cell *cell = new Cell (getDocument(), getSceneManager(), mCellId, getDocument().getPhysics()); Cell *cell = new Cell (getDocument(), getSceneManager(), mCellId, getDocument().getPhysics());
connect (cell->getSignalHandler(), SIGNAL(flagAsModified()), this, SLOT(flagAsModSlot())); connect (cell->getSignalHandler(), SIGNAL(flagAsModified()), this, SLOT(flagAsModSlot()));
mCell.reset (cell); mCell.reset (cell);
update(); update();
emit cellChanged(*data.begin()); emit cellChanged(*dropData.begin());
return true; return true;
} }

View File

@ -233,15 +233,15 @@ CSVRender::WorldspaceWidget::dropRequirments
return ignored; return ignored;
} }
bool CSVRender::WorldspaceWidget::handleDrop (const std::vector<CSMWorld::UniversalId>& data, bool CSVRender::WorldspaceWidget::handleDrop (const std::vector<CSMWorld::UniversalId>& dropData,
DropType type) DropType type)
{ {
if (type==Type_DebugProfile) if (type==Type_DebugProfile)
{ {
if (mRun) if (mRun)
{ {
for (std::vector<CSMWorld::UniversalId>::const_iterator iter (data.begin()); for (std::vector<CSMWorld::UniversalId>::const_iterator iter (dropData.begin());
iter!=data.end(); ++iter) iter!=dropData.end(); ++iter)
mRun->addProfile (iter->getId()); mRun->addProfile (iter->getId());
} }

View File

@ -667,14 +667,14 @@ void CSVWorld::EditWidget::remake(int row)
int displayRole = tree->nestedHeaderData (i, col, int displayRole = tree->nestedHeaderData (i, col,
Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt(); Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt();
CSMWorld::ColumnBase::Display display = CSMWorld::ColumnBase::Display display2 =
static_cast<CSMWorld::ColumnBase::Display> (displayRole); static_cast<CSMWorld::ColumnBase::Display> (displayRole);
mNestedTableDispatcher->makeDelegate (display); mNestedTableDispatcher->makeDelegate (display2);
// FIXME: assumed all columns are editable // FIXME: assumed all columns are editable
QWidget* editor = QWidget* editor =
mNestedTableDispatcher->makeEditor (display, tree->index (0, col, tree->index(row, i))); mNestedTableDispatcher->makeEditor (display2, tree->index (0, col, tree->index(row, i)));
if (editor) if (editor)
{ {
mNestedTableMapper->addMapping (editor, col); mNestedTableMapper->addMapping (editor, col);
@ -699,7 +699,7 @@ void CSVWorld::EditWidget::remake(int row)
label->setEnabled(false); label->setEnabled(false);
} }
createEditorContextMenu(editor, display, row); createEditorContextMenu(editor, display2, row);
} }
} }
mNestedTableMapper->setCurrentModelIndex(tree->index(0, 0, tree->index(row, i))); mNestedTableMapper->setCurrentModelIndex(tree->index(0, 0, tree->index(row, i)));

View File

@ -67,8 +67,8 @@ void CSVWorld::DragRecordTable::dropEvent(QDropEvent *event)
CSMWorld::ColumnBase::Display display = getIndexDisplayType(index); CSMWorld::ColumnBase::Display display = getIndexDisplayType(index);
if (CSVWorld::DragDropUtils::canAcceptData(*event, display)) if (CSVWorld::DragDropUtils::canAcceptData(*event, display))
{ {
const CSMWorld::TableMimeData *data = CSVWorld::DragDropUtils::getTableMimeData(*event); const CSMWorld::TableMimeData *mimeData = CSVWorld::DragDropUtils::getTableMimeData(*event);
if (data->fromDocument(mDocument)) if (mimeData->fromDocument(mDocument))
{ {
CSMWorld::UniversalId id = CSVWorld::DragDropUtils::getAcceptedData(*event, display); CSMWorld::UniversalId id = CSVWorld::DragDropUtils::getAcceptedData(*event, display);
QVariant newIndexData = QString::fromUtf8(id.getId().c_str()); QVariant newIndexData = QString::fromUtf8(id.getId().c_str());

View File

@ -26,10 +26,10 @@ CSVWorld::ReferenceableCreator::ReferenceableCreator (CSMWorld::Data& data, QUnd
for (std::vector<CSMWorld::UniversalId::Type>::const_iterator iter (types.begin()); for (std::vector<CSMWorld::UniversalId::Type>::const_iterator iter (types.begin());
iter!=types.end(); ++iter) iter!=types.end(); ++iter)
{ {
CSMWorld::UniversalId id (*iter, ""); CSMWorld::UniversalId id2 (*iter, "");
mType->addItem (QIcon (id.getIcon().c_str()), id.getTypeName().c_str(), mType->addItem (QIcon (id2.getIcon().c_str()), id2.getTypeName().c_str(),
static_cast<int> (id.getType())); static_cast<int> (id2.getType()));
} }
insertBeforeButtons (mType, false); insertBeforeButtons (mType, false);

View File

@ -187,18 +187,18 @@ void CSVWorld::SceneSubView::cellSelectionChanged (const CSMWorld::CellSelection
emit updateTitle(); emit updateTitle();
} }
void CSVWorld::SceneSubView::handleDrop (const std::vector< CSMWorld::UniversalId >& data) void CSVWorld::SceneSubView::handleDrop (const std::vector< CSMWorld::UniversalId >& dropData)
{ {
CSVRender::PagedWorldspaceWidget* pagedNewWidget = NULL; CSVRender::PagedWorldspaceWidget* pagedNewWidget = NULL;
CSVRender::UnpagedWorldspaceWidget* unPagedNewWidget = NULL; CSVRender::UnpagedWorldspaceWidget* unPagedNewWidget = NULL;
CSVWidget::SceneToolbar* toolbar = NULL; CSVWidget::SceneToolbar* toolbar = NULL;
CSVRender::WorldspaceWidget::DropType type = CSVRender::WorldspaceWidget::getDropType (data); CSVRender::WorldspaceWidget::DropType type = CSVRender::WorldspaceWidget::getDropType (dropData);
switch (mScene->getDropRequirements (type)) switch (mScene->getDropRequirements (type))
{ {
case CSVRender::WorldspaceWidget::canHandle: case CSVRender::WorldspaceWidget::canHandle:
mScene->handleDrop (data, type); mScene->handleDrop (dropData, type);
break; break;
case CSVRender::WorldspaceWidget::needPaged: case CSVRender::WorldspaceWidget::needPaged:
@ -206,15 +206,15 @@ void CSVWorld::SceneSubView::handleDrop (const std::vector< CSMWorld::UniversalI
toolbar = makeToolbar(pagedNewWidget, widget_Paged); toolbar = makeToolbar(pagedNewWidget, widget_Paged);
makeConnections(pagedNewWidget); makeConnections(pagedNewWidget);
replaceToolbarAndWorldspace(pagedNewWidget, toolbar); replaceToolbarAndWorldspace(pagedNewWidget, toolbar);
mScene->handleDrop (data, type); mScene->handleDrop (dropData, type);
break; break;
case CSVRender::WorldspaceWidget::needUnpaged: case CSVRender::WorldspaceWidget::needUnpaged:
unPagedNewWidget = new CSVRender::UnpagedWorldspaceWidget(data.begin()->getId(), mDocument, this); unPagedNewWidget = new CSVRender::UnpagedWorldspaceWidget(dropData.begin()->getId(), mDocument, this);
toolbar = makeToolbar(unPagedNewWidget, widget_Unpaged); toolbar = makeToolbar(unPagedNewWidget, widget_Unpaged);
makeConnections(unPagedNewWidget); makeConnections(unPagedNewWidget);
replaceToolbarAndWorldspace(unPagedNewWidget, toolbar); replaceToolbarAndWorldspace(unPagedNewWidget, toolbar);
cellSelectionChanged(*(data.begin())); cellSelectionChanged(*(dropData.begin()));
break; break;
case CSVRender::WorldspaceWidget::ignored: case CSVRender::WorldspaceWidget::ignored:

View File

@ -280,12 +280,6 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
setSelectionBehavior (QAbstractItemView::SelectRows); setSelectionBehavior (QAbstractItemView::SelectRows);
setSelectionMode (QAbstractItemView::ExtendedSelection); setSelectionMode (QAbstractItemView::ExtendedSelection);
setSortingEnabled (sorting);
if (sorting)
{
sortByColumn (mModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id), Qt::AscendingOrder);
}
int columns = mModel->columnCount(); int columns = mModel->columnCount();
for (int i=0; i<columns; ++i) for (int i=0; i<columns; ++i)
{ {
@ -306,6 +300,13 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
hideColumn (i); hideColumn (i);
} }
if (sorting)
{
// FIXME: some tables (e.g. CellRef) have this column hidden, which makes it confusing
sortByColumn (mModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id), Qt::AscendingOrder);
}
setSortingEnabled (sorting);
mEditAction = new QAction (tr ("Edit Record"), this); mEditAction = new QAction (tr ("Edit Record"), this);
connect (mEditAction, SIGNAL (triggered()), this, SLOT (editRecord())); connect (mEditAction, SIGNAL (triggered()), this, SLOT (editRecord()));
addAction (mEditAction); addAction (mEditAction);
@ -429,7 +430,7 @@ std::vector<std::string> CSVWorld::Table::getSelectedIds() const
QModelIndexList selectedRows = selectionModel()->selectedRows(); QModelIndexList selectedRows = selectionModel()->selectedRows();
int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id);
for (QModelIndexList::const_iterator iter (selectedRows.begin()); for (QModelIndexList::const_iterator iter (selectedRows.begin());
iter != selectedRows.end(); iter != selectedRows.end();
++iter) ++iter)
{ {

View File

@ -203,14 +203,14 @@ bool CSVWorld::TableSubView::eventFilter (QObject* object, QEvent* event)
{ {
if (QDropEvent* drop = dynamic_cast<QDropEvent*>(event)) if (QDropEvent* drop = dynamic_cast<QDropEvent*>(event))
{ {
const CSMWorld::TableMimeData* data = dynamic_cast<const CSMWorld::TableMimeData*>(drop->mimeData()); const CSMWorld::TableMimeData* mimeData = dynamic_cast<const CSMWorld::TableMimeData*>(drop->mimeData());
if (!data) // May happen when non-records (e.g. plain text) are dragged and dropped if (!mimeData) // May happen when non-records (e.g. plain text) are dragged and dropped
return false; return false;
bool handled = data->holdsType(CSMWorld::UniversalId::Type_Filter); bool handled = mimeData->holdsType(CSMWorld::UniversalId::Type_Filter);
if (handled) if (handled)
{ {
mFilterBox->setRecordFilter(data->returnMatching(CSMWorld::UniversalId::Type_Filter).getId()); mFilterBox->setRecordFilter(mimeData->returnMatching(CSMWorld::UniversalId::Type_Filter).getId());
} }
return handled; return handled;
} }

View File

@ -49,6 +49,8 @@ using namespace Ogre;
static bool fsstrict = false; static bool fsstrict = false;
static const std::ctype<char>& facet = std::use_facet<std::ctype<char> >(std::locale::classic());
static char strict_normalize_char(char ch) static char strict_normalize_char(char ch)
{ {
return ch == '\\' ? '/' : ch; return ch == '\\' ? '/' : ch;
@ -56,7 +58,7 @@ static char strict_normalize_char(char ch)
static char nonstrict_normalize_char(char ch) static char nonstrict_normalize_char(char ch)
{ {
return ch == '\\' ? '/' : std::tolower(ch,std::locale::classic()); return ch == '\\' ? '/' : facet.tolower(ch);
} }
template<typename T1, typename T2> template<typename T1, typename T2>
@ -245,7 +247,7 @@ public:
time_t getModifiedTime(const String&) { return 0; } time_t getModifiedTime(const String&) { return 0; }
// This is never called as far as I can see. // This is never called as far as I can see. (actually called from CSMWorld::Resources ctor)
StringVectorPtr list(bool recursive = true, bool dirs = false) StringVectorPtr list(bool recursive = true, bool dirs = false)
{ {
return find ("*", recursive, dirs); return find ("*", recursive, dirs);

View File

@ -6,9 +6,9 @@
void ESM::RefNum::load (ESMReader& esm, bool wide) void ESM::RefNum::load (ESMReader& esm, bool wide)
{ {
if (wide) if (wide)
esm.getHNT (*this, "FRMR", 8); esm.getHNT (*this, SREC_FRMR, 8);
else else
esm.getHNT (mIndex, "FRMR"); esm.getHNT (mIndex, SREC_FRMR);
} }
void ESM::RefNum::save (ESMWriter &esm, bool wide, const std::string& tag) const void ESM::RefNum::save (ESMWriter &esm, bool wide, const std::string& tag) const
@ -36,14 +36,14 @@ void ESM::CellRef::loadId (ESMReader& esm, bool wideRefNum)
// the following refs are part of a "temp refs" section. A temp ref is not being tracked by the moved references system. // the following refs are part of a "temp refs" section. A temp ref is not being tracked by the moved references system.
// Its only purpose is a performance optimization for "immovable" things. We don't need this, and it's problematic anyway, // Its only purpose is a performance optimization for "immovable" things. We don't need this, and it's problematic anyway,
// because any item can theoretically be moved by a script. // because any item can theoretically be moved by a script.
if (esm.isNextSub ("NAM0")) if (esm.isNextSub (SREC_NAM0))
esm.skipHSub(); esm.skipHSub();
blank(); blank();
mRefNum.load (esm, wideRefNum); mRefNum.load (esm, wideRefNum);
mRefID = esm.getHNString ("NAME"); esm.getHNString (SREC_NAME, mRefID);
} }
void ESM::CellRef::loadData(ESMReader &esm, bool &isDeleted) void ESM::CellRef::loadData(ESMReader &esm, bool &isDeleted)
@ -180,7 +180,7 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory, bool
void ESM::CellRef::blank() void ESM::CellRef::blank()
{ {
mRefNum.unset(); mRefNum.unset();
mRefID.clear(); mRefID.clear();
mScale = 1; mScale = 1;
mOwner.clear(); mOwner.clear();
mGlobalVariable.clear(); mGlobalVariable.clear();
@ -196,7 +196,7 @@ void ESM::CellRef::blank()
mTrap.clear(); mTrap.clear();
mReferenceBlocked = -1; mReferenceBlocked = -1;
mTeleport = false; mTeleport = false;
for (int i=0; i<3; ++i) for (int i=0; i<3; ++i)
{ {
mDoorDest.pos[i] = 0; mDoorDest.pos[i] = 0;
@ -206,6 +206,67 @@ void ESM::CellRef::blank()
} }
} }
ESM::CellRef::CellRef ()
{
blank();
}
ESM::CellRef::CellRef (const CellRef& other)
: mRefNum(other.mRefNum)
, mRefID(other.mRefID)
, mScale(other.mScale)
, mOwner(other.mOwner)
, mGlobalVariable(other.mGlobalVariable)
, mSoul(other.mSoul)
, mFaction(other.mFaction)
, mFactionRank(other.mFactionRank)
, mChargeInt(other.mChargeInt)
, mEnchantmentCharge(other.mEnchantmentCharge)
, mGoldValue(other.mGoldValue)
, mTeleport(other.mTeleport)
, mDoorDest(other.mDoorDest)
, mDestCell(other.mDestCell)
, mLockLevel(other.mLockLevel)
, mKey(other.mKey)
, mTrap(other.mTrap)
, mReferenceBlocked(other.mReferenceBlocked)
, mPos(other.mPos)
{
}
ESM::CellRef::CellRef (CellRef&& other)
{
*this = std::move(other);
}
ESM::CellRef& ESM::CellRef::operator= (CellRef&& other)
{
if (this != &other)
{
mRefNum = other.mRefNum; // RefNum
mRefID = std::move(other.mRefID);
mScale = other.mScale;
mOwner = std::move(other.mOwner);
mGlobalVariable = std::move(other.mGlobalVariable);
mSoul = std::move(other.mSoul);
mFaction = std::move(other.mFaction);
mFactionRank = other.mFactionRank;
mChargeInt = other.mChargeInt;
mEnchantmentCharge = other.mEnchantmentCharge;
mGoldValue = other.mGoldValue;
mTeleport = other.mTeleport;
mDoorDest = other.mDoorDest; // Position
mDestCell = std::move(other.mDestCell);
mLockLevel = other.mLockLevel;
mKey = std::move(other.mKey);
mTrap = std::move(other.mTrap);
mReferenceBlocked = other.mReferenceBlocked;
mPos = other.mPos; // Position
}
return *this;
}
bool ESM::operator== (const RefNum& left, const RefNum& right) bool ESM::operator== (const RefNum& left, const RefNum& right)
{ {
return left.mIndex==right.mIndex && left.mContentFile==right.mContentFile; return left.mIndex==right.mIndex && left.mContentFile==right.mContentFile;

View File

@ -109,6 +109,15 @@ namespace ESM
void save (ESMWriter &esm, bool wideRefNum = false, bool inInventory = false, bool isDeleted = false) const; void save (ESMWriter &esm, bool wideRefNum = false, bool inInventory = false, bool isDeleted = false) const;
void blank(); void blank();
CellRef();
~CellRef() = default;
CellRef(const CellRef&);
CellRef& operator=(const CellRef&) = default;
CellRef (CellRef&& other);
CellRef& operator=(CellRef&& other);
}; };
bool operator== (const RefNum& left, const RefNum& right); bool operator== (const RefNum& left, const RefNum& right);

View File

@ -128,7 +128,9 @@ enum RecNameInts
enum SubRecNameInts enum SubRecNameInts
{ {
SREC_DELE = ESM::FourCC<'D','E','L','E'>::value, SREC_DELE = ESM::FourCC<'D','E','L','E'>::value,
SREC_NAME = ESM::FourCC<'N','A','M','E'>::value SREC_NAME = ESM::FourCC<'N','A','M','E'>::value,
SREC_NAM0 = ESM::FourCC<'N','A','M','0'>::value,
SREC_FRMR = ESM::FourCC<'F','R','M','R'>::value
}; };
} }

View File

@ -109,6 +109,12 @@ std::string ESMReader::getHNString(const char* name)
return getHString(); return getHString();
} }
void ESMReader::getHNString(const int name, std::string& str)
{
getSubNameIs(name);
getHString(str);
}
std::string ESMReader::getHString() std::string ESMReader::getHString()
{ {
getSubHeader(); getSubHeader();
@ -130,6 +136,28 @@ std::string ESMReader::getHString()
return getString(mCtx.leftSub); return getString(mCtx.leftSub);
} }
void ESMReader::getHString(std::string& str)
{
getSubHeader();
// Hack to make MultiMark.esp load. Zero-length strings do not
// occur in any of the official mods, but MultiMark makes use of
// them. For some reason, they break the rules, and contain a byte
// (value 0) even if the header says there is no data. If
// Morrowind accepts it, so should we.
if (mCtx.leftSub == 0)
{
// Skip the following zero byte
mCtx.leftRec--;
char c;
getExact(&c, 1);
str = "";
return;
}
getString(str, mCtx.leftSub);
}
void ESMReader::getHExact(void*p, int size) void ESMReader::getHExact(void*p, int size)
{ {
getSubHeader(); getSubHeader();
@ -159,6 +187,24 @@ void ESMReader::getSubNameIs(const char* name)
+ mCtx.subName.toString()); + mCtx.subName.toString());
} }
void ESMReader::getSubNameIs(const int name)
{
getSubName();
if (mCtx.subName != name)
{
unsigned char typeName[4];
typeName[0] = name & 0xff;
typeName[1] = (name >> 8) & 0xff;
typeName[2] = (name >> 16) & 0xff;
typeName[3] = (name >> 24) & 0xff;
std::string subName = std::string((char*)typeName, 4);
fail("Expected subrecord " + subName + " but got "
+ mCtx.subName.toString());
}
}
bool ESMReader::isNextSub(const char* name) bool ESMReader::isNextSub(const char* name)
{ {
if (!mCtx.leftRec) if (!mCtx.leftRec)
@ -174,6 +220,21 @@ bool ESMReader::isNextSub(const char* name)
return !mCtx.subCached; return !mCtx.subCached;
} }
bool ESMReader::isNextSub(const int name)
{
if (!mCtx.leftRec)
return false;
getSubName();
// If the name didn't match, then mark the it as 'cached' so it's
// available for the next call to getSubName.
mCtx.subCached = (mCtx.subName != name);
// If subCached is false, then subName == name.
return !mCtx.subCached;
}
bool ESMReader::peekNextSub(const char *name) bool ESMReader::peekNextSub(const char *name)
{ {
if (!mCtx.leftRec) if (!mCtx.leftRec)
@ -347,6 +408,26 @@ std::string ESMReader::getString(int size)
return std::string (ptr, size); return std::string (ptr, size);
} }
void ESMReader::getString(std::string& str, int size)
{
size_t s = size;
if (mBuffer.size() <= s)
// Add some extra padding to reduce the chance of having to resize again later.
mBuffer.resize(3*s);
mBuffer[s] = 0; // And make sure the string is zero terminated
char *ptr = &mBuffer[0];
getExact(ptr, size); // read ESM data
size = static_cast<int>(strnlen(ptr, size));
if (mEncoder)
str = mEncoder->getUtf8(ptr, size); // Convert to UTF8 and return
else
str = std::string (ptr, size);
}
void ESMReader::fail(const std::string &msg) void ESMReader::fail(const std::string &msg)
{ {
using namespace std; using namespace std;

View File

@ -103,6 +103,13 @@ public:
getHT(x); getHT(x);
} }
template <typename X>
void getHNT(X &x, const int name)
{
getSubNameIs(name);
getHT(x);
}
// Optional version of getHNT // Optional version of getHNT
template <typename X> template <typename X>
void getHNOT(X &x, const char* name) void getHNOT(X &x, const char* name)
@ -121,6 +128,14 @@ public:
getHT(x); getHT(x);
} }
template <typename X>
void getHNT(X &x, const int name, int size)
{
assert(sizeof(X) == size);
getSubNameIs(name);
getHT(x);
}
template <typename X> template <typename X>
void getHNOT(X &x, const char* name, int size) void getHNOT(X &x, const char* name, int size)
{ {
@ -159,9 +174,11 @@ public:
// Read a string with the given sub-record name // Read a string with the given sub-record name
std::string getHNString(const char* name); std::string getHNString(const char* name);
void getHNString(const int name, std::string& str);
// Read a string, including the sub-record header (but not the name) // Read a string, including the sub-record header (but not the name)
std::string getHString(); std::string getHString();
void getHString(std::string& str);
// Read the given number of bytes from a subrecord // Read the given number of bytes from a subrecord
void getHExact(void*p, int size); void getHExact(void*p, int size);
@ -177,6 +194,7 @@ public:
// Get the next subrecord name and check if it matches the parameter // Get the next subrecord name and check if it matches the parameter
void getSubNameIs(const char* name); void getSubNameIs(const char* name);
void getSubNameIs(const int name);
/** Checks if the next sub record name matches the parameter. If it /** Checks if the next sub record name matches the parameter. If it
does, it is read into 'subName' just as if getSubName() was does, it is read into 'subName' just as if getSubName() was
@ -184,6 +202,7 @@ public:
calls to getSubName(), isNextSub() and getSubNameIs(). calls to getSubName(), isNextSub() and getSubNameIs().
*/ */
bool isNextSub(const char* name); bool isNextSub(const char* name);
bool isNextSub(const int name);
bool peekNextSub(const char* name); bool peekNextSub(const char* name);
@ -256,6 +275,7 @@ public:
// Read the next 'size' bytes and return them as a string. Converts // Read the next 'size' bytes and return them as a string. Converts
// them from native encoding to UTF8 in the process. // them from native encoding to UTF8 in the process.
std::string getString(int size); std::string getString(int size);
void getString(std::string& str, int size);
void skip(int bytes) { mEsm->seek(mEsm->tell()+bytes); } void skip(int bytes) { mEsm->seek(mEsm->tell()+bytes); }
uint64_t getOffset() { return mEsm->tell(); } uint64_t getOffset() { return mEsm->tell(); }

View File

@ -35,6 +35,20 @@ public:
return true; return true;
} }
static bool cEqual(const std::string &x, const std::string &y) {
if (x.size() != y.size()) {
return false;
}
std::string::const_iterator xit = x.begin();
std::string::const_iterator yit = y.begin();
for (; xit != x.end(); ++xit, ++yit) {
if (*xit != *yit) {
return false;
}
}
return true;
}
static int ciCompareLen(const std::string &x, const std::string &y, size_t len) static int ciCompareLen(const std::string &x, const std::string &y, size_t len)
{ {
std::string::const_iterator xit = x.begin(); std::string::const_iterator xit = x.begin();

17
extern/murmurhash/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,17 @@
cmake_minimum_required(VERSION 2.8)
# This is NOT intended as a stand-alone build system! Instead, you should include this from the main CMakeLists of your project.
set(MURMURHASH_LIBRARY "murmurhash")
# Sources
set(SOURCE_FILES
MurmurHash2.cpp
)
add_library(${MURMURHASH_LIBRARY} STATIC ${SOURCE_FILES})
set(MURMURHASH_LIBRARIES ${MURMURHASH_LIBRARY})
link_directories(${CMAKE_CURRENT_BINARY_DIR})
set(MURMURHASH_LIBRARIES ${MURMURHASH_LIBRARIES} PARENT_SCOPE)

522
extern/murmurhash/MurmurHash2.cpp vendored Normal file
View File

@ -0,0 +1,522 @@
//-----------------------------------------------------------------------------
// MurmurHash2 was written by Austin Appleby, and is placed in the public
// domain. The author hereby disclaims copyright to this source code.
// Note - This code makes a few assumptions about how your machine behaves -
// 1. We can read a 4-byte value from any address without crashing
// 2. sizeof(int) == 4
// And it has a few limitations -
// 1. It will not work incrementally.
// 2. It will not produce the same results on little-endian and big-endian
// machines.
#include "MurmurHash2.h"
//-----------------------------------------------------------------------------
// Platform-specific functions and macros
// Microsoft Visual Studio
#if defined(_MSC_VER)
#define BIG_CONSTANT(x) (x)
// Other compilers
#else // defined(_MSC_VER)
#define BIG_CONSTANT(x) (x##LLU)
#endif // !defined(_MSC_VER)
//-----------------------------------------------------------------------------
uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed )
{
// 'm' and 'r' are mixing constants generated offline.
// They're not really 'magic', they just happen to work well.
const uint32_t m = 0x5bd1e995;
const int r = 24;
// Initialize the hash to a 'random' value
uint32_t h = seed ^ len;
// Mix 4 bytes at a time into the hash
const unsigned char * data = (const unsigned char *)key;
while(len >= 4)
{
uint32_t k = *(uint32_t*)data;
k *= m;
k ^= k >> r;
k *= m;
h *= m;
h ^= k;
data += 4;
len -= 4;
}
// Handle the last few bytes of the input array
switch(len)
{
case 3: h ^= data[2] << 16;
case 2: h ^= data[1] << 8;
case 1: h ^= data[0];
h *= m;
};
// Do a few final mixes of the hash to ensure the last few
// bytes are well-incorporated.
h ^= h >> 13;
h *= m;
h ^= h >> 15;
return h;
}
//-----------------------------------------------------------------------------
// MurmurHash2, 64-bit versions, by Austin Appleby
// The same caveats as 32-bit MurmurHash2 apply here - beware of alignment
// and endian-ness issues if used across multiple platforms.
// 64-bit hash for 64-bit platforms
uint64_t MurmurHash64A ( const void * key, int len, uint64_t seed )
{
const uint64_t m = BIG_CONSTANT(0xc6a4a7935bd1e995);
const int r = 47;
uint64_t h = seed ^ (len * m);
const uint64_t * data = (const uint64_t *)key;
const uint64_t * end = data + (len/8);
while(data != end)
{
uint64_t k = *data++;
k *= m;
k ^= k >> r;
k *= m;
h ^= k;
h *= m;
}
const unsigned char * data2 = (const unsigned char*)data;
switch(len & 7)
{
case 7: h ^= uint64_t(data2[6]) << 48;
case 6: h ^= uint64_t(data2[5]) << 40;
case 5: h ^= uint64_t(data2[4]) << 32;
case 4: h ^= uint64_t(data2[3]) << 24;
case 3: h ^= uint64_t(data2[2]) << 16;
case 2: h ^= uint64_t(data2[1]) << 8;
case 1: h ^= uint64_t(data2[0]);
h *= m;
};
h ^= h >> r;
h *= m;
h ^= h >> r;
return h;
}
// 64-bit hash for 32-bit platforms
uint64_t MurmurHash64B ( const void * key, int len, uint64_t seed )
{
const uint32_t m = 0x5bd1e995;
const int r = 24;
uint32_t h1 = uint32_t(seed) ^ len;
uint32_t h2 = uint32_t(seed >> 32);
const uint32_t * data = (const uint32_t *)key;
while(len >= 8)
{
uint32_t k1 = *data++;
k1 *= m; k1 ^= k1 >> r; k1 *= m;
h1 *= m; h1 ^= k1;
len -= 4;
uint32_t k2 = *data++;
k2 *= m; k2 ^= k2 >> r; k2 *= m;
h2 *= m; h2 ^= k2;
len -= 4;
}
if(len >= 4)
{
uint32_t k1 = *data++;
k1 *= m; k1 ^= k1 >> r; k1 *= m;
h1 *= m; h1 ^= k1;
len -= 4;
}
switch(len)
{
case 3: h2 ^= ((unsigned char*)data)[2] << 16;
case 2: h2 ^= ((unsigned char*)data)[1] << 8;
case 1: h2 ^= ((unsigned char*)data)[0];
h2 *= m;
};
h1 ^= h2 >> 18; h1 *= m;
h2 ^= h1 >> 22; h2 *= m;
h1 ^= h2 >> 17; h1 *= m;
h2 ^= h1 >> 19; h2 *= m;
uint64_t h = h1;
h = (h << 32) | h2;
return h;
}
//-----------------------------------------------------------------------------
// MurmurHash2A, by Austin Appleby
// This is a variant of MurmurHash2 modified to use the Merkle-Damgard
// construction. Bulk speed should be identical to Murmur2, small-key speed
// will be 10%-20% slower due to the added overhead at the end of the hash.
// This variant fixes a minor issue where null keys were more likely to
// collide with each other than expected, and also makes the function
// more amenable to incremental implementations.
#define mmix(h,k) { k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; }
uint32_t MurmurHash2A ( const void * key, int len, uint32_t seed )
{
const uint32_t m = 0x5bd1e995;
const int r = 24;
uint32_t l = len;
const unsigned char * data = (const unsigned char *)key;
uint32_t h = seed;
while(len >= 4)
{
uint32_t k = *(uint32_t*)data;
mmix(h,k);
data += 4;
len -= 4;
}
uint32_t t = 0;
switch(len)
{
case 3: t ^= data[2] << 16;
case 2: t ^= data[1] << 8;
case 1: t ^= data[0];
};
mmix(h,t);
mmix(h,l);
h ^= h >> 13;
h *= m;
h ^= h >> 15;
return h;
}
//-----------------------------------------------------------------------------
// CMurmurHash2A, by Austin Appleby
// This is a sample implementation of MurmurHash2A designed to work
// incrementally.
// Usage -
// CMurmurHash2A hasher
// hasher.Begin(seed);
// hasher.Add(data1,size1);
// hasher.Add(data2,size2);
// ...
// hasher.Add(dataN,sizeN);
// uint32_t hash = hasher.End()
class CMurmurHash2A
{
public:
void Begin ( uint32_t seed = 0 )
{
m_hash = seed;
m_tail = 0;
m_count = 0;
m_size = 0;
}
void Add ( const unsigned char * data, int len )
{
m_size += len;
MixTail(data,len);
while(len >= 4)
{
uint32_t k = *(uint32_t*)data;
mmix(m_hash,k);
data += 4;
len -= 4;
}
MixTail(data,len);
}
uint32_t End ( void )
{
mmix(m_hash,m_tail);
mmix(m_hash,m_size);
m_hash ^= m_hash >> 13;
m_hash *= m;
m_hash ^= m_hash >> 15;
return m_hash;
}
private:
static const uint32_t m = 0x5bd1e995;
static const int r = 24;
void MixTail ( const unsigned char * & data, int & len )
{
while( len && ((len<4) || m_count) )
{
m_tail |= (*data++) << (m_count * 8);
m_count++;
len--;
if(m_count == 4)
{
mmix(m_hash,m_tail);
m_tail = 0;
m_count = 0;
}
}
}
uint32_t m_hash;
uint32_t m_tail;
uint32_t m_count;
uint32_t m_size;
};
//-----------------------------------------------------------------------------
// MurmurHashNeutral2, by Austin Appleby
// Same as MurmurHash2, but endian- and alignment-neutral.
// Half the speed though, alas.
uint32_t MurmurHashNeutral2 ( const void * key, int len, uint32_t seed )
{
const uint32_t m = 0x5bd1e995;
const int r = 24;
uint32_t h = seed ^ len;
const unsigned char * data = (const unsigned char *)key;
while(len >= 4)
{
uint32_t k;
k = data[0];
k |= data[1] << 8;
k |= data[2] << 16;
k |= data[3] << 24;
k *= m;
k ^= k >> r;
k *= m;
h *= m;
h ^= k;
data += 4;
len -= 4;
}
switch(len)
{
case 3: h ^= data[2] << 16;
case 2: h ^= data[1] << 8;
case 1: h ^= data[0];
h *= m;
};
h ^= h >> 13;
h *= m;
h ^= h >> 15;
return h;
}
//-----------------------------------------------------------------------------
// MurmurHashAligned2, by Austin Appleby
// Same algorithm as MurmurHash2, but only does aligned reads - should be safer
// on certain platforms.
// Performance will be lower than MurmurHash2
#define MIX(h,k,m) { k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; }
uint32_t MurmurHashAligned2 ( const void * key, int len, uint32_t seed )
{
const uint32_t m = 0x5bd1e995;
const int r = 24;
const unsigned char * data = (const unsigned char *)key;
uint32_t h = seed ^ len;
int align = (uint64_t)data & 3;
if(align && (len >= 4))
{
// Pre-load the temp registers
uint32_t t = 0, d = 0;
switch(align)
{
case 1: t |= data[2] << 16;
case 2: t |= data[1] << 8;
case 3: t |= data[0];
}
t <<= (8 * align);
data += 4-align;
len -= 4-align;
int sl = 8 * (4-align);
int sr = 8 * align;
// Mix
while(len >= 4)
{
d = *(uint32_t *)data;
t = (t >> sr) | (d << sl);
uint32_t k = t;
MIX(h,k,m);
t = d;
data += 4;
len -= 4;
}
// Handle leftover data in temp registers
d = 0;
if(len >= align)
{
switch(align)
{
case 3: d |= data[2] << 16;
case 2: d |= data[1] << 8;
case 1: d |= data[0];
}
uint32_t k = (t >> sr) | (d << sl);
MIX(h,k,m);
data += align;
len -= align;
//----------
// Handle tail bytes
switch(len)
{
case 3: h ^= data[2] << 16;
case 2: h ^= data[1] << 8;
case 1: h ^= data[0];
h *= m;
};
}
else
{
switch(len)
{
case 3: d |= data[2] << 16;
case 2: d |= data[1] << 8;
case 1: d |= data[0];
case 0: h ^= (t >> sr) | (d << sl);
h *= m;
}
}
h ^= h >> 13;
h *= m;
h ^= h >> 15;
return h;
}
else
{
while(len >= 4)
{
uint32_t k = *(uint32_t *)data;
MIX(h,k,m);
data += 4;
len -= 4;
}
//----------
// Handle tail bytes
switch(len)
{
case 3: h ^= data[2] << 16;
case 2: h ^= data[1] << 8;
case 1: h ^= data[0];
h *= m;
};
h ^= h >> 13;
h *= m;
h ^= h >> 15;
return h;
}
}
//-----------------------------------------------------------------------------

38
extern/murmurhash/MurmurHash2.h vendored Normal file
View File

@ -0,0 +1,38 @@
//-----------------------------------------------------------------------------
// MurmurHash2 was written by Austin Appleby, and is placed in the public
// domain. The author hereby disclaims copyright to this source code.
#ifndef _MURMURHASH2_H_
#define _MURMURHASH2_H_
//-----------------------------------------------------------------------------
// Platform-specific functions and macros
// Microsoft Visual Studio
#if defined(_MSC_VER) && (_MSC_VER < 1600)
typedef unsigned char uint8_t;
typedef unsigned int uint32_t;
typedef unsigned __int64 uint64_t;
// Other compilers
#else // defined(_MSC_VER)
#include <stdint.h>
#endif // !defined(_MSC_VER)
//-----------------------------------------------------------------------------
uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed );
uint64_t MurmurHash64A ( const void * key, int len, uint64_t seed );
uint64_t MurmurHash64B ( const void * key, int len, uint64_t seed );
uint32_t MurmurHash2A ( const void * key, int len, uint32_t seed );
uint32_t MurmurHashNeutral2 ( const void * key, int len, uint32_t seed );
uint32_t MurmurHashAligned2 ( const void * key, int len, uint32_t seed );
//-----------------------------------------------------------------------------
#endif // _MURMURHASH2_H_

View File

@ -10,11 +10,38 @@
# pragma warning(disable: 4996) # pragma warning(disable: 4996)
# define strcasecmp stricmp # define strcasecmp stricmp
# if (_MSC_VER < 1900) # if (_MSC_VER < 1900)
# define snprintf _snprintf # include <stdio.h>
# include <stdarg.h>
# define snprintf c99_snprintf
# define vsnprintf c99_vsnprintf
/* see http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 */
inline int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap)
{
int count = -1;
if (size != 0)
count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
if (count == -1)
count = _vscprintf(format, ap);
return count;
}
inline int c99_snprintf(char *outBuf, size_t size, const char *format, ...)
{
int count;
va_list ap;
va_start(ap, format);
count = c99_vsnprintf(outBuf, size, format, ap);
va_end(ap);
return count;
}
# endif # endif
#else #else
# warning "Unable to determine your compiler, you should probably take a look here." # warning "Unable to determine your compiler, you should probably take a look here."
# include <strings.h> // Just take a guess # include <strings.h> // Just take a guess
#endif #endif
#endif /* _STRINGS_WRAPPER_H */ #endif /* _STRINGS_WRAPPER_H */