Jump to content

Recommended Posts

Official tutorials for modding game data:

(Advanced) How to Edit Assets and Assetbundles (by Fhav6X): https://forums.obsidian.net/topic/105849-tutorial-how-to-edit-assets-and-assetbundles/
 
Documentation for all game data formats: https://eternity.obsidian.net/game-data-formats.
 
Basic information about modding string tables: https://forums.obsidian.net/topic/94795-obsidian-will-you-provide-xml-documentation-or-other-editing-tools-on-release/?p=1977260
 
Much of Deadfire's data is placed in easily-readable text files located in Pillars of Eternity II\PillarsOfEternityII_Data\exported.  You can modify these files directly, but a better way to make mods that you can easily share is to use the override folder.  The override folder is Pillars of Eternity II\PillarsOfEternityII_Data\override.

You should create a subfolder in the override folder for each mod. So my mod might be in the folder Pillars of Eternity II\PillarsOfEternityII_Data\override\bmac-mod.

  • *.conversationbundle files must appear at the path specified in the bundle as the "Filename" property. For example, re_si_ship_combat.conversationbundle needs to appear at \Conversations\RE_Scripted_Interactions\re_si_ship_combat.conversation.
  • *.stringtable files need to be in the same folder hierarchy as the file they're overriding. E.g. to override localized\en\text\game\gui.stringtable, your override file should be in override\[yourmod]\localized\en\text\game\gui.stringtable.
  • Other *.bundle files can be anywhere in your mod folder

If your mod causes problems or doesn't function correctly, you might find helpful error messages in the game's output log folder at "PillarsOfEternityII_Data/output_log.txt".

  • Like 11

Share this post


Link to post
Share on other sites

@BMac

I took those information bits from the link. In the final release, are there changes on the modding system comparing to the beta?

 

Can such file only contain the one changed id? -> Yes

What happens on id collision in multiple files? Alphabetical last file wins? -> Last loaded file will win.

Is the Audio system also overrideable or has it hard depends on the wwise packages? -> No override folder and needs wwise packages.

 

Can one add images for items, skills and so on?

How to add something to a vendor? Without overriding his whole backpack?

Edited by Xaratas

Share this post


Link to post
Share on other sites

There weren't any major changes for modding between the last backer beta release and the 1.0 release, just some changes in the structure of a few gamedata types to make them more amendable to overriding.

 

Adding new icons is still difficult; it's possible a patch could change this.

 

You do have to fully override one of the store's loot lists to add an item to it.  I could make a change in a patch to make it possible to add additional loot lists to stores.  I'll make a note of it.

  • Like 4

Share this post


Link to post
Share on other sites

Much of Deadfire's data is placed in easily-readable text files located in Pillars of Eternity II\PillarsOfEternityII_Data\exported.  You can modify these files directly, but a better way to make mods that you can easily share is to use the override folder.  The override folder is Pillars of Eternity II\PillarsOfEternityII_Data\override.

 

Any file in the override folder will take priority over the files in the exported data folder - so you can add new data objects to the game in these files, or replace existing ones.

 

You should create a subfolder in the override folder for each mod. So my mod might be in the folder Pillars of Eternity II\PillarsOfEternityII_Data\override\bmac-mod.

 

Hi, there!

 

So I've created a mod that replaces numerous audio files. I've verified this works by overwriting the original audio files, but upon seeing this post decided to do it this way as it seems a lot safer.

 

However, the override folder didn't exist. I created it, created a subfolder for my mod, and placed the files within... yet it does not work. As far as I can tell, it is not overriding the files.

 

Are the to-be-overridden files supposed to be loose in the subfolder? If so, that didn't work. I also tried keeping them in their original folders, as well as re-creating the original path to the files within the subfolder.

 

Thanks in advance!

 

Edit: I just read the answer to this question above "Is the Audio system also overrideable or has it hard depends on the wwise packages? -> No override folder and needs wwise packages." Apologies for my haste!

Edited by Finchyy

Share this post


Link to post
Share on other sites

From what I've seen, it's not possible to partially change objects in the *.*bundle. If it is, then please inform me.

 

I think it'd be cool if it was possible to only change single variables in objects. I made a mod to change the level cap, it only needs to change a single variable in global.gamedatabundle but I had to copy the whole GlobalGameSettings object which is 1200 lines just to change that one variable and that makes this change incompatible with any other mod that would change the GlobalGameSettings right?

Share this post


Link to post
Share on other sites

Seconding the above question, someone asked me for a Mac version of my mod, saying the file formats are different. Are they? Can I convert somehow without having a Mac myself?

Share this post


Link to post
Share on other sites

The process should be relatively similar on Mac.  The 'override' folder should go inside the app at "PillarsOfEternityII.app\Contents\override".  Nothing should need to be different about the files themselves.  If you run into any issues with that, let me know and I'll dig deeper.

 

 

From what I've seen, it's not possible to partially change objects in the *.*bundle. If it is, then please inform me.

 

I think it'd be cool if it was possible to only change single variables in objects. I made a mod to change the level cap, it only needs to change a single variable in global.gamedatabundle but I had to copy the whole GlobalGameSettings object which is 1200 lines just to change that one variable and that makes this change incompatible with any other mod that would change the GlobalGameSettings right?

That's right.  I'll make a note of that and see if we can do something about it in a future version.

  • Like 1

Share this post


Link to post
Share on other sites

From what I've seen, it's not possible to partially change objects in the *.*bundle. If it is, then please inform me.

 

I think it'd be cool if it was possible to only change single variables in objects. I made a mod to change the level cap, it only needs to change a single variable in global.gamedatabundle but I had to copy the whole GlobalGameSettings object which is 1200 lines just to change that one variable and that makes this change incompatible with any other mod that would change the GlobalGameSettings right?

 

I only took the blocks of code(or whatever its called) that I edited within, I didn't need to copy the whole file to make it work. (I took the code related to recovery and reload that I wanted to edit; in the end mod file ended up 3411 lines(for 29 edits in it) but the original attacks.gamedatabundle file has 240252 lines). What didn't work was combining code from another original file with it, I had to make 2 separate mod files from 2 separate origins for 2 separate purposes :p

 

Anyway, so what's changed with 1.1 related to modding? People's been saying that it would become easier somehow...

Share this post


Link to post
Share on other sites

 

From what I've seen, it's not possible to partially change objects in the *.*bundle. If it is, then please inform me.

 

I think it'd be cool if it was possible to only change single variables in objects. I made a mod to change the level cap, it only needs to change a single variable in global.gamedatabundle but I had to copy the whole GlobalGameSettings object which is 1200 lines just to change that one variable and that makes this change incompatible with any other mod that would change the GlobalGameSettings right?

 

I only took the blocks of code(or whatever its called) that I edited within, I didn't need to copy the whole file to make it work. (I took the code related to recovery and reload that I wanted to edit; in the end mod file ended up 3411 lines(for 29 edits in it) but the original attacks.gamedatabundle file has 240252 lines). What didn't work was combining code from another original file with it, I had to make 2 separate mod files from 2 separate origins for 2 separate purposes :p

 

That's right, you can override particular objects from a gamedatabundle without overriding the whole bundle, you just can't override particular values on an object without overriding the whole object.

 

You should be able to put any number of gamedata objects in one .gamedatabundle, regardless of the source.  You can't mix objects from different types of bundles, of course (for example, a gamedata object and a global script).

 

Anyway, so what's changed with 1.1 related to modding? People's been saying that it would become easier somehow...

 

I don't think there have been any large systematic changes. Some specific data is now in gamedata that wasn't, particularly the tables that define how many spellcasts spellcasting classes get.  Player voice sets also exist on their own individual gamedata objects instead of as a list on a single object, which made it difficult to have multiple voice set mods.

  • Like 1

Share this post


Link to post
Share on other sites

I've edited crews starting job traits using the Ship GameDataBundle file. If I want to keep that file I've edited through each patch, do I just create an override folder and add the edited Ship file to it?

 

I also don't want to miss out on new stuff that may get added to the Ship file via future patches, so how do I keep my edited Ship file yet still allow the new things to be added via future game patches?

Share this post


Link to post
Share on other sites

I don't think there have been any large systematic changes. Some specific data is now in gamedata that wasn't, particularly the tables that define how many spellcasts spellcasting classes get.  Player voice sets also exist on their own individual gamedata objects instead of as a list on a single object, which made it difficult to have multiple voice set mods.

Any chance for attack/ability animation speeds to be in gamedata in the future?

 

Or speed slider's data if it ain't just time manipulation :p

Share this post


Link to post
Share on other sites

I've edited crews starting job traits using the Ship GameDataBundle file. If I want to keep that file I've edited through each patch, do I just create an override folder and add the edited Ship file to it?

 

I also don't want to miss out on new stuff that may get added to the Ship file via future patches, so how do I keep my edited Ship file yet still allow the new things to be added via future game patches?

Putting a modded file in the override folder will make it so it doesn't get overwritten by a patch, yes.  HOWEVER, as you note, it's generally a good idea to always check a modded file against an updated basefile to see what sort of additions have been made to the basefile that the game is expecting to see.  If you don't do this, there's the potential for weirdness or even crashes.  And that's not including game balance patches like changing the values of ship hull health and whatnot.

 

Theoretically it should be possible to put a modded crewmember in its own gamedatabundle file, as long as it has its entire statblock properly formatted.  However, I was running into issues with that last night when I was testing the possibility (like you, I have modded a couple of crew members).  So perhaps not.

 

As it is, I would just grit my teeth and re-mod the ships.gamedatabundle file with whatever changes you've made to the crew members (and whatever else) and then plop that into the override folder.  Then I would start messing around with a crew member stat block as a solitary gamedatabundle file and see whether or not the game will recognize it on its own.

 

NOTE:::: In this case, the patch that dropped today changed A LOT in ships.gamedatabundle, so it is almost assuredly easier just to re-mod the crew in the basefile than add all of the changes to your original modded file.  Though I will note that it doesn't appear to have changed any of the hirable crewmembers, so you should be able to just copypasta those changes over without too much problem.

Edited by Zap Gun For Hire

Share this post


Link to post
Share on other sites

Examining the *.gamadatabundle files the all have EF BB BF at the start followed by a load of JSON

 

Any significance to this for MODs?

 

Just looked this up and it signifies BOM encoding

Edited by peardox

OK Fair warning has been applied

 

I'm gonna move the domain to https://perspak.com early Feb but will keep all content

 

There are reasons behind this move which basically boil down to unifying my release schedule

 

My friends are welcome to play (I'll set you up your own areas if you desire them)

 

Please note that this process is messy so may take a few weeks 

Share this post


Link to post
Share on other sites

While BMac is completely correct I've looked at a load of MODs and it appears most of us on NexusMods are replicating the original structure.

 

Of course, both will work but for making things easy for our fellow MOD developers to understand how a MOD is put together I feel it beneficial if we stick to the original structure.

 

This approach, whilst COMPLETELY un-required makes it easier for the community to understand how someone else's MOD works.

 

I got into MOD development as a side project to another POE2 I have going on (mapping the DB) and today released my first 'No Storms'

 

My reasoning for this rationale is that while we pick things up quickly others may get confused given conflicting info.

 

If everything is strictly placed there is no chance of mis-understanding for newbs (like me)

 

I could have placed my MOD in a dir and forgot about it, I decoded to follow structure

 

It's OK saying a *bundle can go anywhere but stringtables etc have to replicate structure but that will confuse the novice MODder

 

Simply lie and say EVERYTHING has to be where it's meant to be!

 

This will produce better newbs and understandable structure for the community

 

I see zero problems with my concept

Edited by peardox
  • Like 1

OK Fair warning has been applied

 

I'm gonna move the domain to https://perspak.com early Feb but will keep all content

 

There are reasons behind this move which basically boil down to unifying my release schedule

 

My friends are welcome to play (I'll set you up your own areas if you desire them)

 

Please note that this process is messy so may take a few weeks 

Share this post


Link to post
Share on other sites

There is way to change the name of a creature summoned using a .prefab file?

 

i did that , its a mod for a panther figurine , it's my first mod, so maybe i wrong something

 

btw my issue is about the name of the panther in game

it call a prefab file , under the calling there is a  SummonDisplayStrings value that i've changed but it isn't recognized .

The game take the string assigned to the CRE_Panther_Elder in character.gamedatabundle, (entry 606 in characters.stringtable)

so is there a way to make another version of it just using the same model? or some string value that i can add to the mod to show another name of my liking?

maybe clone the prefab ? I've already opened also the unity file and found it, but i don't have any clue about the rebuild of those kind of files

{
    "GameDataObjects": [
	{
			"$type": "Game.GameData.AttackSummonGameData, Assembly-CSharp",
			"DebugName": "Figurine_Jade_Panther_Ability_AttackSummon",
			"ID": "efe48ffd-75e4-4d7f-a4c9-dd86c70a88b6",
			"Components": [{
					"$type": "Game.GameData.AttackBaseComponent, Assembly-CSharp",
					"KeywordsIDs": [],
					"AttackDistance": 12,
					"MinAttackDistance": 0,
					"AttackVariationID": "dd5934cf-0e6f-4f4a-8f92-3d3102090e8f",
					"UseParentEquippableHand": "false",
					"CastSpeedID": "eacb53e3-6eb5-422a-92ca-99cc883ae4a9",
					"RecoveryTimeID": "566840d9-1561-4243-8ca7-889df9869847",
					"ImpactDelay": 0,
					"ForcedTarget": "None",
					"AffectedTargetType": "All",
					"AffectedTargetConditional": {
						"Conditional": {
							"Operator": 0,
							"Components": []
						}
					},
					"AffectedTargetDeathState": "Alive",
					"HostilityOverride": "Default",
					"PushDistance": 0,
					"FaceTarget": "true",
					"AccuracyBonus": 0,
					"PenetrationRating": 7,
					"DamageData": {
						"DamageType": "None",
						"AlternateDamageType": "None",
						"Minimum": 0,
						"Maximum": 0,
						"DamageProcs": []
					},
					"Require****Object": "false",
					"StatusEffectKeywordsIDs": [],
					"StatusEffectsIDs": [],
					"RandomizeStatusEffect": "false",
					"CanGraze": "false",
					"CanCrit": "true",
					"DefendedBy": "None",
					"AfflictionsDefendedBy": "None",
					"AfflictionApplicationModifier": "None",
					"SubstituteHitVisualEffect": "",
					"VisualEffects": [],
					"AttackOnImpactID": "00000000-0000-0000-0000-000000000000",
					"ExtraAttackID": "00000000-0000-0000-0000-000000000000",
					"LaunchBone": "RightWeapon",
					"HitBone": "Chest",
					"OnHitShakeDuration": "None",
					"OnHitShakeStrength": "None",
					"NoiseLevelID": "15743f94-1026-40b0-8e13-a667b3f66f63",
					"AllReactNoise": "false",
					"InterruptsOn": "None",
					"InterruptType": "Normal",
					"TargetAngle": 0,
					"ApplyOnceOnly": "false",
					"PathsToTarget": "true",
					"HideFromCombatLog": "false",
					"AdditionalAttackOnTooltip": "false",
					"DoesNotApplyDamage": "false",
					"TreatAsWeapon": "false",
					"BounceData": {
						"Bounces": 0,
						"Multiplier": 0.5,
						"Range": 10,
						"InRangeOrder": "false",
						"NoRepeatTargets": "false",
						"AlwaysBounceAtEnemies": "false",
						"Delay": 0,
						"NeverBounce": "false"
					}
				}, {
					"$type": "Game.GameData.AttackSummonComponent, Assembly-CSharp",
					"SummonType": "Summoned",
					"SummonFileList": [{
							"Filename": "prefabs/characters/animal companions/CRE_Panther_Elder.prefab"
						}
					],
					"SummonDisplayStrings": [{
							"String": 15000
						}
					],
					"OnSummonVisualEffect": "prefabs/effects/abilities/summon/fx_summon_yellow.prefab",
					"OnDesummonVisualEffect": "prefabs/effects/abilities/summon/fx_summon_yellow.prefab",
					"TeamType": "JoinTeam",
					"SummonCopyOfSelf": "false",
					"Duration": 30,
					"HasLoot": "false"
				}
			]
		},
	{
			"$type": "Game.GameData.GenericAbilityGameData, Assembly-CSharp",
			"DebugName": "Figurine_Jade_Panther_Ability",
			"ID": "45d3d709-e939-4793-8005-103f734119fb",
			"Components": [{
					"$type": "Game.GameData.GenericAbilityComponent, Assembly-CSharp",
					"KeywordsIDs": ["ddf90f19-c9a2-4087-a1a7-a00ee46bc3dd"],
					"DisplayName": 10000,
					"Description": -1,
					"UpgradeDescriptions": [],
					"UpgradedFromID": "00000000-0000-0000-0000-000000000000",
					"Vocalization": "NoVocalization",
					"Icon": "",
					"UsageType": "None",
					"UsageValue": 0,
					"AbilityClass": "None",
					"AbilityLevel": 1,
					"IsPassive": "false",
					"StackingRuleOverride": "Default",
					"TriggerOnHit": "false",
					"IsModal": "false",
					"ModalGroupID": "00000000-0000-0000-0000-000000000000",
					"IsCombatOnly": "true",
					"IsNonCombatOnly": "false",
					"HideFromUI": "false",
					"HideFromCombatLog": "false",
					"UniqueSet": "None",
					"NoiseLevelID": "15743f94-1026-40b0-8e13-a667b3f66f63",
					"DurationOverride": 0,
					"OverrideEmpower": "Default",
					"ClearsOnMovement": "false",
					"CannotActivateWhileInStealth": "false",
					"CannotActivateWhileInvisible": "false",
					"ActivationPrerequisites": {
						"Conditional": {
							"Operator": 0,
							"Components": []
						}
					},
					"ApplicationPrerequisites": {
						"Conditional": {
							"Operator": 0,
							"Components": []
						}
					},
					"DeactivationPrerequisites": {
						"Conditional": {
							"Operator": 0,
							"Components": []
						}
					},
					"PowerLevelScaling": {
						"BaseLevel": 0,
						"LevelIncrement": 1,
						"MaxLevel": 0,
						"DamageAdjustment": 1,
						"DurationAdjustment": 1,
						"BounceCountAdjustment": 0,
						"ProjectileCountAdjustment": 0,
						"AccuracyAdjustment": 0,
						"PenetrationAdjustment": 0
					},
					"StatusEffectKeywordsIDs": [],
					"StatusEffectsIDs": [],
					"VisualEffects": [],
					"SelfMaterialReplacementID": "00000000-0000-0000-0000-000000000000",
					"AttackID": "efe48ffd-75e4-4d7f-a4c9-dd86c70a88b6",
					"AITargetingConditional": {
						"Conditional": {
							"Operator": 0,
							"Components": []
						},
						"Scripts": []
					},
					"AudioEventListID": "00000000-0000-0000-0000-000000000000"
				}, {
					"$type": "Game.GameData.ProgressionUnlockableComponent, Assembly-CSharp"
				}
			]
		},
      {
			"$type": "Game.GameData.ConsumableGameData, Assembly-CSharp",
			"DebugName": "Figurine_Jade_Panther",
			"ID": "839ebb7d-da8f-4e42-9c2a-7d550cb306ce",
			"Components": [{
					"$type": "Game.GameData.ItemComponent, Assembly-CSharp",
					"DisplayName": 10000,
					"DescriptionText": 10001,
					"FilterType": "Consumables",
					"InventoryAudioEventListID": "705deb97-3f84-48c8-a84b-e3c34e2d0e3a",
					"IsQuestItem": "false",
					"IsIngredient": "false",
					"IsCurrency": "false",
					"IsAdventuringItem": "false",
					"CanSellForFullValue": "false",
					"MaxStackSize": 1,
					"NeverDropAsLoot": "false",
					"CanBePickpocketed": "true",
					"IsUnique": "true",
					"Value": 1500,
					"IconTextureSmall": "gui/icons/items/misc/figurine_jade_tiger_s.png",
					"IconTextureLarge": "gui/icons/items/misc/figurine_jade_tiger_l.png",
					"PencilSketchTexture": "",
					"InspectOnUseButton": [],
					"IsPlaceholder": "false"
				}, {
					"$type": "Game.GameData.ConsumableComponent, Assembly-CSharp",
					"Type": "Figurine",
					"UsageCount": 1,
					"UsageType": "PerRest",
					"AnimationVariation": 100,
					"AbilityID": "45d3d709-e939-4793-8005-103f734119fb",
					"PickpocketAbilityID": "00000000-0000-0000-0000-000000000000",
					"Timer": 0,
					"SkillRequirement": {
						"SkillID": "00000000-0000-0000-0000-000000000000",
						"Value": 0
					},
					"ShipMoraleBonus": 1
				}
			]
		},
{
			"$type": "Game.GameData.PromotionalItemCollectionGameData, Assembly-CSharp",
			"DebugName": "Drizzt_Figurine_Panther",
			"ID": "a02eeafa-1f73-41db-b12b-46e57b2f019c",
			"Components": [{
					"$type": "Game.GameData.PromotionalItemCollectionComponent, Assembly-CSharp",
					"PromotionalItemCollections": {
						"PromotionalItemCollection": [{
								"ItemReferenceID": "839ebb7d-da8f-4e42-9c2a-7d550cb306ce",
								"Quantity": 1
							}
						]
					}
				}
			]
		}		
    ]
}
Edited by kilay

Share this post


Link to post
Share on other sites

And also what's about entry count in stringtable?

a lot of mod add new entries string, isn't that value related to that?

Edited by kilay

Share this post


Link to post
Share on other sites

And also what's about entry count in stringtable?

a lot of mod add new entries string, isn't that value related to that?

 

The entry count in the stringtable files isn't used. You can ignore it in your mod.

  • Like 1

Share this post


Link to post
Share on other sites

 

 

*.*bundle files can be anywhere in your mod folder, but *.stringtable files need to be in the same folder hierarchy as the file they're overriding. E.g. to override localized\en\text\game\gui.stringtable, your override file should be in override\[yourmod]\localized\en\text\game\gui.stringtable.

 

*note* That also means that you can not use subfolders in your mod to split texts for different purposes.

For example override\[yourmod]\blueThings\localized\en\text\game\gui.stringtable will not work.

 

I suggest using such folder names, as I will do for the enhanced ui mod:

override\[yourmod]_blueThings\localized\en\text\game\gui.stringtable

override\[yourmod]_redThings\localized\en\text\game\gui.stringtable

Share this post


Link to post
Share on other sites

Wrong thread, apologies.

Edited by kilay

Share this post


Link to post
Share on other sites

 

*.*bundle files can be anywhere in your mod folder, but *.stringtable files need to be in the same folder hierarchy as the file they're overriding. E.g. to override localized\en\text\game\gui.stringtable, your override file should be in override\[yourmod]\localized\en\text\game\gui.stringtable.

 

*note* That also means that you can not use subfolders in your mod to split texts for different purposes.

For example override\[yourmod]\blueThings\localized\en\text\game\gui.stringtable will not work.

 

I suggest using such folder names, as I will do for the enhanced ui mod:

override\[yourmod]_blueThings\localized\en\text\game\gui.stringtable

override\[yourmod]_redThings\localized\en\text\game\gui.stringtable

 

 

So how do you provided updated translations for all expansions? You have to have a mod for each expansion because you can not have the folder "exported "and "laxa_exported "in there.

 

Or do you need to put all ids from items.stringtable into the items.stringtable from exported folder?

Share this post


Link to post
Share on other sites

What's about game.globalvariablesbundle?

 

Could I override it ?

If I can, where I have to put the file ? override/mymod/design/gamedata/  ?

Edited by kilay

Share this post


Link to post
Share on other sites

So how do you provided updated translations for all expansions? You have to have a mod for each expansion because you can not have the folder "exported "and "laxa_exported "in there.

 

Or do you need to put all ids from items.stringtable into the items.stringtable from exported folder?

You don't need to use any different folders to mod expansion content.

 

You can think of the expansion "lax*_exported" folders as being themselves much like mods of the base game

 

What's about game.globalvariablesbundle?

 

Could I override it ?

If I can, where I have to put the file ? override/mymod/design/gamedata/  ?

You can't override the globalvariablesbundle yet, unfortunately.

  • Like 1

Share this post


Link to post
Share on other sites

You can't override the globalvariablesbundle yet, unfortunately.

 

Thnks for reply, i hope for a future addition, with the help of @peardox i was able to trigger a script with a new variable, but ofc we have replaced the original game.globalvariablesbundle in exported/globalvariables/ with a custom one.

Edited by kilay

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

×
×
  • Create New...