From a1931a93fb4bfad042b1235ec62270f4c74e3fbc Mon Sep 17 00:00:00 2001 From: Exter-N Date: Fri, 17 Jan 2025 01:45:37 +0100 Subject: [PATCH] Add drafts of JSON schemas --- schemas/container.json | 486 +++++++++++++++++++++++++++++++++++++++ schemas/default_mod.json | 25 ++ schemas/group.json | 206 +++++++++++++++++ schemas/meta-v3.json | 51 ++++ 4 files changed, 768 insertions(+) create mode 100644 schemas/container.json create mode 100644 schemas/default_mod.json create mode 100644 schemas/group.json create mode 100644 schemas/meta-v3.json diff --git a/schemas/container.json b/schemas/container.json new file mode 100644 index 00000000..5798f46c --- /dev/null +++ b/schemas/container.json @@ -0,0 +1,486 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://raw.githubusercontent.com/xivdev/Penumbra/master/schemas/container.json", + "type": "object", + "properties": { + "Name": { + "description": "Name of the container/option/sub-mod.", + "type": ["string", "null"] + }, + "Files": { + "description": "File redirections in this container. Keys are game paths, values are relative file paths.", + "type": "object", + "patternProperties": { + "^[^/\\\\][^\\\\]*$": { + "type": "string", + "pattern": "^[^/\\\\][^/]*$" + } + }, + "additionalProperties": false + }, + "FileSwaps": { + "description": "File swaps in this container. Keys are original game paths, values are actual game paths.", + "type": "object", + "patternProperties": { + "^[^/\\\\][^\\\\]*$": { + "type": "string", + "pattern": "^[^/\\\\][^/]*$" + } + }, + "additionalProperties": false + }, + "Manipulations": { + "type": "array", + "items": { + "$ref": "#/$defs/Manipulation" + } + } + }, + "$defs": { + "Manipulation": { + "type": "object", + "properties": { + "Type": { + "enum": ["Unknown", "Imc", "Eqdp", "Eqp", "Est", "Gmp", "Rsp", "GlobalEqp", "Atch"] + }, + "Manipulation": { + "type": ["object", "null"] + } + }, + "required": ["Type", "Manipulation"], + "oneOf": [ + { + "properties": { + "Type": { + "const": "Unknown" + }, + "Manipulation": { + "type": "null" + } + } + }, { + "properties": { + "Type": { + "const": "Imc" + }, + "Manipulation": { + "$ref": "#/$defs/ImcManipulation" + } + } + }, { + "properties": { + "Type": { + "const": "Eqdp" + }, + "Manipulation": { + "$ref": "#/$defs/EqdpManipulation" + } + } + }, { + "properties": { + "Type": { + "const": "Eqp" + }, + "Manipulation": { + "$ref": "#/$defs/EqpManipulation" + } + } + }, { + "properties": { + "Type": { + "const": "Est" + }, + "Manipulation": { + "$ref": "#/$defs/EstManipulation" + } + } + }, { + "properties": { + "Type": { + "const": "Gmp" + }, + "Manipulation": { + "$ref": "#/$defs/GmpManipulation" + } + } + }, { + "properties": { + "Type": { + "const": "Rsp" + }, + "Manipulation": { + "$ref": "#/$defs/RspManipulation" + } + } + }, { + "properties": { + "Type": { + "const": "GlobalEqp" + }, + "Manipulation": { + "$ref": "#/$defs/GlobalEqpManipulation" + } + } + }, { + "properties": { + "Type": { + "const": "Atch" + }, + "Manipulation": { + "$ref": "#/$defs/AtchManipulation" + } + } + } + ], + "additionalProperties": false + }, + "ImcManipulation": { + "type": "object", + "properties": { + "Entry": { + "$ref": "#/$defs/ImcEntry" + }, + "Valid": { + "type": "boolean" + } + }, + "required": [ + "Entry" + ], + "allOf": [ + { + "$ref": "#/$defs/ImcIdentifier" + } + ], + "unevaluatedProperties": false + }, + "ImcIdentifier": { + "type": "object", + "properties": { + "PrimaryId": { + "type": "integer" + }, + "SecondaryId": { + "type": "integer" + }, + "Variant": { + "type": "integer" + }, + "ObjectType": { + "$ref": "#/$defs/ObjectType" + }, + "EquipSlot": { + "$ref": "#/$defs/EquipSlot" + }, + "BodySlot": { + "$ref": "#/$defs/BodySlot" + } + }, + "required": [ + "PrimaryId", + "SecondaryId", + "Variant", + "ObjectType", + "EquipSlot", + "BodySlot" + ] + }, + "ImcEntry": { + "type": "object", + "properties": { + "AttributeAndSound": { + "type": "integer" + }, + "MaterialId": { + "type": "integer" + }, + "DecalId": { + "type": "integer" + }, + "VfxId": { + "type": "integer" + }, + "MaterialAnimationId": { + "type": "integer" + }, + "AttributeMask": { + "type": "integer" + }, + "SoundId": { + "type": "integer" + } + }, + "required": [ + "MaterialId", + "DecalId", + "VfxId", + "MaterialAnimationId", + "AttributeMask", + "SoundId" + ], + "additionalProperties": false + }, + "EqdpManipulation": { + "type": "object", + "properties": { + "Entry": { + "type": "integer" + }, + "Gender": { + "$ref": "#/$defs/Gender" + }, + "Race": { + "$ref": "#/$defs/ModelRace" + }, + "SetId": { + "$ref": "#/$defs/LaxInteger" + }, + "Slot": { + "$ref": "#/$defs/EquipSlot" + }, + "ShiftedEntry": { + "type": "integer" + } + }, + "required": [ + "Entry", + "Gender", + "Race", + "SetId", + "Slot" + ], + "additionalProperties": false + }, + "EqpManipulation": { + "type": "object", + "properties": { + "Entry": { + "type": "integer" + }, + "SetId": { + "$ref": "#/$defs/LaxInteger" + }, + "Slot": { + "$ref": "#/$defs/EquipSlot" + } + }, + "required": [ + "Entry", + "SetId", + "Slot" + ], + "additionalProperties": false + }, + "EstManipulation": { + "type": "object", + "properties": { + "Entry": { + "type": "integer" + }, + "Gender": { + "$ref": "#/$defs/Gender" + }, + "Race": { + "$ref": "#/$defs/ModelRace" + }, + "SetId": { + "$ref": "#/$defs/LaxInteger" + }, + "Slot": { + "enum": ["Hair", "Face", "Body", "Head"] + } + }, + "required": [ + "Entry", + "Gender", + "Race", + "SetId", + "Slot" + ], + "additionalProperties": false + }, + "GmpManipulation": { + "type": "object", + "properties": { + "Entry": { + "type": "object", + "properties": { + "Enabled": { + "type": "boolean" + }, + "Animated": { + "type": "boolean" + }, + "RotationA": { + "type": "number" + }, + "RotationB": { + "type": "number" + }, + "RotationC": { + "type": "number" + }, + "UnknownA": { + "type": "number" + }, + "UnknownB": { + "type": "number" + }, + "UnknownTotal": { + "type": "number" + }, + "Value": { + "type": "number" + } + }, + "required": [ + "Enabled", + "Animated", + "RotationA", + "RotationB", + "RotationC", + "UnknownA", + "UnknownB", + "UnknownTotal", + "Value" + ], + "additionalProperties": false + }, + "SetId": { + "$ref": "#/$defs/LaxInteger" + } + }, + "required": [ + "Entry", + "SetId" + ], + "additionalProperties": false + }, + "RspManipulation": { + "type": "object", + "properties": { + "Entry": { + "type": "number" + }, + "SubRace": { + "$ref": "#/$defs/SubRace" + }, + "Attribute": { + "$ref": "#/$defs/RspAttribute" + } + }, + "additionalProperties": false + }, + "GlobalEqpManipulation": { + "type": "object", + "properties": { + "Condition": { + "type": "integer" + }, + "Type": { + "enum": ["DoNotHideEarrings", "DoNotHideNecklace", "DoNotHideBracelets", "DoNotHideRingR", "DoNotHideRingL", "DoNotHideHrothgarHats", "DoNotHideVieraHats"] + } + }, + "additionalProperties": false + }, + "AtchManipulation": { + "type": "object", + "properties": { + "Entry": { + "type": "object", + "properties": { + "Bone": { + "type": "string", + "maxLength": 34 + }, + "Scale": { + "type": "number" + }, + "OffsetX": { + "type": "number" + }, + "OffsetY": { + "type": "number" + }, + "OffsetZ": { + "type": "number" + }, + "RotationX": { + "type": "number" + }, + "RotationY": { + "type": "number" + }, + "RotationZ": { + "type": "number" + } + }, + "required": [ + "Bone", + "Scale", + "OffsetX", + "OffsetY", + "OffsetZ", + "RotationX", + "RotationY", + "RotationZ" + ], + "additionalProperties": false + }, + "Gender": { + "$ref": "#/$defs/Gender" + }, + "Race": { + "$ref": "#/$defs/ModelRace" + }, + "Type": { + "type": "string", + "minLength": 3, + "maxLength": 3 + }, + "Index": { + "type": "integer" + } + }, + "required": [ + "Entry", + "Gender", + "Race", + "Type", + "Index" + ], + "additionalProperties": false + }, + "LaxInteger": { + "oneOf": [ + { + "type": "integer" + }, { + "type": "string", + "pattern": "^\\d+$" + } + ] + }, + "EquipSlot": { + "enum": ["Unknown", "MainHand", "OffHand", "Head", "Body", "Hands", "Belt", "Legs", "Feet", "Ears", "Neck", "Wrists", "RFinger", "BothHand", "LFinger", "HeadBody", "BodyHandsLegsFeet", "SoulCrystal", "LegsFeet", "FullBody", "BodyHands", "BodyLegsFeet", "ChestHands", "Nothing", "All"] + }, + "Gender": { + "enum": ["Unknown", "Male", "Female", "MaleNpc", "FemaleNpc"] + }, + "ModelRace": { + "enum": ["Unknown", "Midlander", "Highlander", "Elezen", "Lalafell", "Miqote", "Roegadyn", "AuRa", "Hrothgar", "Viera"] + }, + "ObjectType": { + "enum": ["Unknown", "Vfx", "DemiHuman", "Accessory", "World", "Housing", "Monster", "Icon", "LoadingScreen", "Map", "Interface", "Equipment", "Character", "Weapon", "Font"] + }, + "BodySlot": { + "enum": ["Unknown", "Hair", "Face", "Tail", "Body", "Zear"] + }, + "SubRace": { + "enum": ["Unknown", "Midlander", "Highlander", "Wildwood", "Duskwight", "Plainsfolk", "Dunesfolk", "SeekerOfTheSun", "KeeperOfTheMoon", "Seawolf", "Hellsguard", "Raen", "Xaela", "Helion", "Lost", "Rava", "Veena"] + }, + "RspAttribute": { + "enum": ["MaleMinSize", "MaleMaxSize", "MaleMinTail", "MaleMaxTail", "FemaleMinSize", "FemaleMaxSize", "FemaleMinTail", "FemaleMaxTail", "BustMinX", "BustMinY", "BustMinZ", "BustMaxX", "BustMaxY", "BustMaxZ"] + } + } +} diff --git a/schemas/default_mod.json b/schemas/default_mod.json new file mode 100644 index 00000000..eecd74d0 --- /dev/null +++ b/schemas/default_mod.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://raw.githubusercontent.com/xivdev/Penumbra/master/schemas/container.json", + "allOf": [ + { + "type": "object", + "properties": { + "Version": { + "description": "???", + "type": ["integer", "null"] + }, + "Description": { + "description": "Description of the sub-mod.", + "type": ["string", "null"] + }, + "Image": { + "description": "Unused by Penumbra.", + "type": ["string", "null"] + } + } + }, { + "$ref": "https://raw.githubusercontent.com/xivdev/Penumbra/master/schemas/container.json" + } + ] +} diff --git a/schemas/group.json b/schemas/group.json new file mode 100644 index 00000000..0078e9f3 --- /dev/null +++ b/schemas/group.json @@ -0,0 +1,206 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://raw.githubusercontent.com/xivdev/Penumbra/master/schemas/group.json", + "type": "object", + "properties": { + "Version": { + "description": "???", + "type": ["integer", "null"] + }, + "Name": { + "description": "Name of the group.", + "type": "string" + }, + "Description": { + "description": "Description of the group.", + "type": ["string", "null"] + }, + "Image": { + "description": "Relative path to a preview image for the group. Unused by Penumbra, present for round-trip import/export of TexTools-generated mods.", + "type": ["string", "null"] + }, + "Page": { + "description": "TexTools page of the group. Unused by Penumbra, present for round-trip import/export of TexTools-generated mods.", + "type": ["integer", "null"] + }, + "Priority": { + "description": "Priority of the group. If several groups define conflicting files or manipulations, the highest priority wins.", + "type": ["integer", "null"] + }, + "Type": { + "description": "Group type. Single groups have one and only one of their options active at any point. Multi groups can have zero, one or many of their options active. Combining groups have n options, 2^n containers, and will have one and only one container active depending on the selected options.", + "enum": ["Single", "Multi", "Imc", "Combining"] + }, + "DefaultSettings": { + "description": "Default configuration for the group.", + "type": "integer" + } + }, + "required": [ + "Name", + "Type" + ], + "oneOf": [ + { + "properties": { + "Type": { + "const": "Single" + }, + "Options": { + "type": "array", + "items": { + "allOf": [ + { + "type": "object", + "properties": { + "Description": { + "description": "Description of the option.", + "type": ["string", "null"] + }, + "Image": { + "description": "Unused by Penumbra.", + "type": ["string", "null"] + } + } + }, { + "$ref": "https://raw.githubusercontent.com/xivdev/Penumbra/master/schemas/container.json" + } + ] + } + } + } + }, { + "properties": { + "Type": { + "const": "Multi" + }, + "Options": { + "type": "array", + "items": { + "allOf": [ + { + "type": "object", + "properties": { + "Description": { + "description": "Description of the option.", + "type": ["string", "null"] + }, + "Priority": { + "description": "Priority of the option. If several enabled options within the group define conflicting files or manipulations, the highest priority wins.", + "type": ["integer", "null"] + }, + "Image": { + "description": "Unused by Penumbra.", + "type": ["string", "null"] + } + } + }, { + "$ref": "https://raw.githubusercontent.com/xivdev/Penumbra/master/schemas/container.json" + } + ] + } + } + } + }, { + "properties": { + "Type": { + "const": "Imc" + }, + "AllVariants": { + "type": "boolean" + }, + "OnlyAttributes": { + "type": "boolean" + }, + "Identifier": { + "$ref": "https://raw.githubusercontent.com/xivdev/Penumbra/master/schemas/container.json#/$defs/ImcIdentifier" + }, + "DefaultEntry": { + "$ref": "https://raw.githubusercontent.com/xivdev/Penumbra/master/schemas/container.json#/$defs/ImcEntry" + }, + "Options": { + "type": "array", + "items": { + "type": "object", + "properties": { + "Name": { + "description": "Name of the option.", + "type": "string" + }, + "Description": { + "description": "Description of the option.", + "type": ["string", "null"] + }, + "Image": { + "description": "Unused by Penumbra.", + "type": ["string", "null"] + } + }, + "required": [ + "Name" + ], + "oneOf": [ + { + "properties": { + "AttributeMask": { + "type": "integer" + } + }, + "required": [ + "AttributeMask" + ] + }, { + "properties": { + "IsDisableSubMod": { + "const": true + } + }, + "required": [ + "IsDisableSubMod" + ] + } + ], + "unevaluatedProperties": false + } + } + } + }, { + "properties": { + "Type": { + "const": "Combining" + }, + "Options": { + "type": "array", + "items": { + "type": "object", + "properties": { + "Name": { + "description": "Name of the option.", + "type": "string" + }, + "Description": { + "description": "Description of the option.", + "type": ["string", "null"] + }, + "Image": { + "description": "Unused by Penumbra.", + "type": ["string", "null"] + } + }, + "required": [ + "Name" + ], + "additionalProperties": false + } + }, + "Containers": { + "type": "array", + "items": { + "$ref": "https://raw.githubusercontent.com/xivdev/Penumbra/master/schemas/container.json" + } + } + } + } + ], + "unevaluatedProperties": false +} diff --git a/schemas/meta-v3.json b/schemas/meta-v3.json new file mode 100644 index 00000000..1a132264 --- /dev/null +++ b/schemas/meta-v3.json @@ -0,0 +1,51 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://raw.githubusercontent.com/xivdev/Penumbra/master/schemas/meta-v3.json", + "title": "Penumbra Mod Metadata", + "description": "Metadata of a Penumbra mod.", + "type": "object", + "properties": { + "FileVersion": { + "description": "Major version of the metadata schema used.", + "type": "integer", + "minimum": 3, + "maximum": 3 + }, + "Name": { + "description": "Name of the mod.", + "type": "string" + }, + "Author": { + "description": "Author of the mod.", + "type": ["string", "null"] + }, + "Description": { + "description": "Description of the mod. Can span multiple paragraphs.", + "type": ["string", "null"] + }, + "Image": { + "description": "Relative path to a preview image for the mod. Unused by Penumbra, present for round-trip import/export of TexTools-generated mods.", + "type": ["string", "null"] + }, + "Version": { + "description": "Version of the mod. Can be an arbitrary string.", + "type": ["string", "null"] + }, + "Website": { + "description": "URL of the web page of the mod.", + "type": ["string", "null"] + }, + "ModTags": { + "description": "Author-defined tags for the mod.", + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + } + }, + "required": [ + "FileVersion", + "Name" + ] +}