From a1931a93fb4bfad042b1235ec62270f4c74e3fbc Mon Sep 17 00:00:00 2001 From: Exter-N Date: Fri, 17 Jan 2025 01:45:37 +0100 Subject: [PATCH 1/6] 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" + ] +} From 3b8aac8eca94e8253b357a8b885a56bb7919e7d2 Mon Sep 17 00:00:00 2001 From: Exter-N Date: Fri, 17 Jan 2025 18:50:00 +0100 Subject: [PATCH 2/6] Add schema for Material Development Kit files --- schemas/shpk_devkit.json | 500 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 500 insertions(+) create mode 100644 schemas/shpk_devkit.json diff --git a/schemas/shpk_devkit.json b/schemas/shpk_devkit.json new file mode 100644 index 00000000..cd18ab81 --- /dev/null +++ b/schemas/shpk_devkit.json @@ -0,0 +1,500 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://raw.githubusercontent.com/xivdev/Penumbra/master/schemas/shpk_devkit.json", + "type": "object", + "properties": { + "ShaderKeys": { + "type": "object", + "patternProperties": { + "^\\d+$": { + "$ref": "#/$defs/ShaderKey" + } + }, + "additionalProperties": false + }, + "Comment": { + "$ref": "#/$defs/MayVary" + }, + "Samplers": { + "type": "object", + "patternProperties": { + "^\\d+$": { + "$ref": "#/$defs/MayVary" + } + }, + "additionalProperties": false + }, + "Constants": { + "type": "object", + "patternProperties": { + "^\\d+$": { + "$ref": "#/$defs/MayVary" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false, + "$defs": { + "ShaderKeyValue": { + "type": "object", + "properties": { + "Label": { + "type": "string" + }, + "Description": { + "type": "string" + } + }, + "additionalProperties": false + }, + "ShaderKey": { + "type": "object", + "properties": { + "Label": { + "type": "string" + }, + "Description": { + "type": "string" + }, + "Values": { + "type": "object", + "patternProperties": { + "^\\d+$": { + "$ref": "#/$defs/ShaderKeyValue" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "Varying": { + "type": "object", + "properties": { + "Vary": { + "type": "array", + "items": { + "$ref": "#/$defs/LaxInteger" + } + }, + "Selectors": { + "description": "Keys are Σ 31^i shaderKey(Vary[i]).", + "type": "object", + "patternProperties": { + "^\\d+$": { + "type": "integer" + } + }, + "additionalProperties": false + }, + "Items": { + "type": "array", + "$comment": "Varying is defined by constraining this array's items to T" + } + }, + "required": [ + "Vary", + "Selectors", + "Items" + ], + "additionalProperties": false + }, + "MayVary": { + "oneOf": [ + { + "type": ["string", "null"] + }, { + "allOf": [ + { + "$ref": "#/$defs/Varying" + }, { + "type": "object", + "properties": { + "Items": { + "type": "array", + "items": { + "type": ["string", "null"] + } + } + } + } + ] + } + ] + }, + "Sampler": { + "type": ["object", "null"], + "properties": { + "Label": { + "type": "string" + }, + "Description": { + "type": "string" + }, + "DefaultTexture": { + "type": "string", + "pattern": "^[^/\\\\][^\\\\]*$" + } + }, + "additionalProperties": false + }, + "MayVary": { + "oneOf": [ + { + "$ref": "#/$defs/Sampler" + }, { + "allOf": [ + { + "$ref": "#/$defs/Varying" + }, { + "type": "object", + "properties": { + "Items": { + "type": "array", + "items": { + "$ref": "#/$defs/Sampler" + } + } + } + } + ] + } + ] + }, + "ConstantBase": { + "type": "object", + "properties": { + "Offset": { + "description": "Defaults to 0. Mutually exclusive with ByteOffset.", + "type": "integer", + "minimum": 0 + }, + "Length": { + "description": "Defaults to up to the end. Mutually exclusive with ByteLength.", + "type": "integer", + "minimum": 0 + }, + "ByteOffset": { + "description": "Defaults to 0. Mutually exclusive with Offset.", + "type": "integer", + "minimum": 0 + }, + "ByteLength": { + "description": "Defaults to up to the end. Mutually exclusive with Length.", + "type": "integer", + "minimum": 0 + }, + "Group": { + "description": "Defaults to \"Further Constants\".", + "type": "string" + }, + "Label": { + "type": "string" + }, + "Description": { + "description": "Defaults to empty.", + "type": "string" + }, + "Type": { + "description": "Defaults to Float.", + "enum": ["Hidden", "Float", "Integer", "Color", "Enum", "Int32", "Int32Enum", "Int8", "Int8Enum", "Int16", "Int16Enum", "Int64", "Int64Enum", "Half", "Double", "TileIndex", "SphereMapIndex"] + } + }, + "not": { + "anyOf": [ + { + "required": ["Offset", "ByteOffset"] + }, { + "required": ["Length", "ByteLenngth"] + } + ] + } + }, + "HiddenConstant": { + "type": "object", + "properties": { + "Type": { + "const": "Hidden" + } + }, + "required": [ + "Type" + ], + "allOf": [ + { + "$ref": "#/$defs/ConstantBase" + } + ], + "unevaluatedProperties": false + }, + "FloatConstant": { + "type": "object", + "properties": { + "Type": { + "enum": ["Float", "Half", "Double"] + }, + "Minimum": { + "description": "Defaults to -∞.", + "type": "number" + }, + "Maximum": { + "description": "Defaults to ∞.", + "type": "number" + }, + "Speed": { + "description": "Defaults to 0.1.", + "type": "number", + "minimum": 0 + }, + "RelativeSpeed": { + "description": "Defaults to 0.", + "type": "number", + "minimum": 0 + }, + "Exponent": { + "description": "Defaults to 1. Uses an odd pseudo-power function, f(x) = sgn(x) |x|^n.", + "type": "number" + }, + "Factor": { + "description": "Defaults to 1.", + "type": "number" + }, + "Bias": { + "description": "Defaults to 0.", + "type": "number" + }, + "Precision": { + "description": "Defaults to 3.", + "type": "integer", + "minimum": 0, + "maximum": 9 + }, + "Slider": { + "description": "Defaults to true. Drag has priority over this.", + "type": "boolean" + }, + "Drag": { + "description": "Defaults to true. Has priority over Slider.", + "type": "boolean" + }, + "Unit": { + "description": "Defaults to no unit.", + "type": "string" + } + }, + "required": [ + "Label" + ], + "allOf": [ + { + "$ref": "#/$defs/ConstantBase" + } + ], + "unevaluatedProperties": false + }, + "IntConstant": { + "type": "object", + "properties": { + "Type": { + "enum": ["Integer", "Int32", "Int8", "Int16", "Int64"] + }, + "Minimum": { + "description": "Defaults to -2^N, N being the explicit integer width specified in the type, or 32 for Int.", + "type": "number" + }, + "Maximum": { + "description": "Defaults to 2^N - 1, N being the explicit integer width specified in the type, or 32 for Int.", + "type": "number" + }, + "Speed": { + "description": "Defaults to 0.25.", + "type": "number", + "minimum": 0 + }, + "RelativeSpeed": { + "description": "Defaults to 0.", + "type": "number", + "minimum": 0 + }, + "Factor": { + "description": "Defaults to 1.", + "type": "number" + }, + "Bias": { + "description": "Defaults to 0.", + "type": "number" + }, + "Hex": { + "description": "Defaults to false. Has priority over Slider and Drag.", + "type": "boolean" + }, + "Slider": { + "description": "Defaults to true. Hex and Drag have priority over this.", + "type": "boolean" + }, + "Drag": { + "description": "Defaults to true. Has priority over Slider, but Hex has priority over this.", + "type": "boolean" + }, + "Unit": { + "description": "Defaults to no unit.", + "type": "string" + } + }, + "required": [ + "Label", + "Type" + ], + "allOf": [ + { + "$ref": "#/$defs/ConstantBase" + } + ], + "unevaluatedProperties": false + }, + "ColorConstant": { + "type": "object", + "properties": { + "Type": { + "const": "Color" + }, + "SquaredRgb": { + "description": "Defaults to false. Uses an odd pseudo-square function, f(x) = sgn(x) |x|².", + "type": "boolean" + }, + "Clamped": { + "description": "Defaults to false.", + "type": "boolean" + } + }, + "required": [ + "Label", + "Type" + ], + "allOf": [ + { + "$ref": "#/$defs/ConstantBase" + } + ], + "unevaluatedProperties": false + }, + "EnumValue": { + "type": "object", + "properties": { + "Label": { + "type": "string" + }, + "Description": { + "type": "string" + }, + "Value": { + "type": "number" + } + }, + "required": [ + "Label", + "Value" + ], + "additionalProperties": false + }, + "EnumConstant": { + "type": "object", + "properties": { + "Type": { + "enum": ["Enum", "Int32Enum", "Int8Enum", "Int16Enum", "Int64Enum"] + }, + "Values": { + "type": "array", + "items": { + "$ref": "#/$defs/EnumValue" + } + } + }, + "required": [ + "Label", + "Type" + ], + "allOf": [ + { + "$ref": "#/$defs/ConstantBase" + } + ], + "unevaluatedProperties": false + }, + "SpecialConstant": { + "type": "object", + "properties": { + "Type": { + "enum": ["TileIndex", "SphereMapIndex"] + } + }, + "required": [ + "Label", + "Type" + ], + "allOf": [ + { + "$ref": "#/$defs/ConstantBase" + } + ], + "unevaluatedProperties": false + }, + "Constant": { + "oneOf": [ + { + "$ref": "#/$defs/HiddenConstant" + }, { + "$ref": "#/$defs/FloatConstant" + }, { + "$ref": "#/$defs/IntConstant" + }, { + "$ref": "#/$defs/ColorConstant" + }, { + "$ref": "#/$defs/EnumConstant" + }, { + "$ref": "#/$defs/SpecialConstant" + } + ] + }, + "MayVary": { + "oneOf": [ + { + "type": ["array", "null"], + "items": { + "$ref": "#/$defs/Constant" + } + }, { + "allOf": [ + { + "$ref": "#/$defs/Varying" + }, { + "type": "object", + "properties": { + "Items": { + "type": "array", + "items": { + "type": ["array", "null"], + "items": { + "$ref": "#/$defs/Constant" + } + } + } + } + } + ] + } + ] + }, + "LaxInteger": { + "oneOf": [ + { + "type": "integer" + }, { + "type": "string", + "pattern": "^\\d+$" + } + ] + } + } +} From 5f8377acaaf1bc20944af85e525b09313285272c Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Fri, 17 Jan 2025 19:54:40 +0100 Subject: [PATCH 3/6] Update mod loading structure. --- Penumbra.GameData | 2 +- Penumbra/Mods/Manager/ModDataEditor.cs | 145 +------------------------ Penumbra/Mods/ModCreator.cs | 4 +- Penumbra/Mods/ModLocalData.cs | 57 ++++++++++ Penumbra/Mods/ModMeta.cs | 83 ++++++++++++++ 5 files changed, 146 insertions(+), 145 deletions(-) diff --git a/Penumbra.GameData b/Penumbra.GameData index 78ce195c..c5250722 160000 --- a/Penumbra.GameData +++ b/Penumbra.GameData @@ -1 +1 @@ -Subproject commit 78ce195c171d7bce4ad9df105f1f95cce9bf1150 +Subproject commit c525072299d5febd2bb638ab229060b0073ba6a6 diff --git a/Penumbra/Mods/Manager/ModDataEditor.cs b/Penumbra/Mods/Manager/ModDataEditor.cs index 162f823d..7c48205a 100644 --- a/Penumbra/Mods/Manager/ModDataEditor.cs +++ b/Penumbra/Mods/Manager/ModDataEditor.cs @@ -27,6 +27,9 @@ public enum ModDataChangeType : ushort public class ModDataEditor(SaveService saveService, CommunicatorService communicatorService) : IService { + public SaveService SaveService + => saveService; + /// Create the file containing the meta information about a mod from scratch. public void CreateMeta(DirectoryInfo directory, string? name, string? author, string? description, string? version, string? website) @@ -40,148 +43,6 @@ public class ModDataEditor(SaveService saveService, CommunicatorService communic saveService.ImmediateSaveSync(new ModMeta(mod)); } - public ModDataChangeType LoadLocalData(Mod mod) - { - var dataFile = saveService.FileNames.LocalDataFile(mod); - - var importDate = 0L; - var localTags = Enumerable.Empty(); - var favorite = false; - var note = string.Empty; - - var save = true; - if (File.Exists(dataFile)) - try - { - var text = File.ReadAllText(dataFile); - var json = JObject.Parse(text); - - importDate = json[nameof(Mod.ImportDate)]?.Value() ?? importDate; - favorite = json[nameof(Mod.Favorite)]?.Value() ?? favorite; - note = json[nameof(Mod.Note)]?.Value() ?? note; - localTags = (json[nameof(Mod.LocalTags)] as JArray)?.Values().OfType() ?? localTags; - save = false; - } - catch (Exception e) - { - Penumbra.Log.Error($"Could not load local mod data:\n{e}"); - } - - if (importDate == 0) - importDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); - - ModDataChangeType changes = 0; - if (mod.ImportDate != importDate) - { - mod.ImportDate = importDate; - changes |= ModDataChangeType.ImportDate; - } - - changes |= ModLocalData.UpdateTags(mod, null, localTags); - - if (mod.Favorite != favorite) - { - mod.Favorite = favorite; - changes |= ModDataChangeType.Favorite; - } - - if (mod.Note != note) - { - mod.Note = note; - changes |= ModDataChangeType.Note; - } - - if (save) - saveService.QueueSave(new ModLocalData(mod)); - - return changes; - } - - public ModDataChangeType LoadMeta(ModCreator creator, Mod mod) - { - var metaFile = saveService.FileNames.ModMetaPath(mod); - if (!File.Exists(metaFile)) - { - Penumbra.Log.Debug($"No mod meta found for {mod.ModPath.Name}."); - return ModDataChangeType.Deletion; - } - - try - { - var text = File.ReadAllText(metaFile); - var json = JObject.Parse(text); - - var newName = json[nameof(Mod.Name)]?.Value() ?? string.Empty; - var newAuthor = json[nameof(Mod.Author)]?.Value() ?? string.Empty; - var newDescription = json[nameof(Mod.Description)]?.Value() ?? string.Empty; - var newImage = json[nameof(Mod.Image)]?.Value() ?? string.Empty; - var newVersion = json[nameof(Mod.Version)]?.Value() ?? string.Empty; - var newWebsite = json[nameof(Mod.Website)]?.Value() ?? string.Empty; - var newFileVersion = json[nameof(ModMeta.FileVersion)]?.Value() ?? 0; - var importDate = json[nameof(Mod.ImportDate)]?.Value(); - var modTags = (json[nameof(Mod.ModTags)] as JArray)?.Values().OfType(); - - ModDataChangeType changes = 0; - if (mod.Name != newName) - { - changes |= ModDataChangeType.Name; - mod.Name = newName; - } - - if (mod.Author != newAuthor) - { - changes |= ModDataChangeType.Author; - mod.Author = newAuthor; - } - - if (mod.Description != newDescription) - { - changes |= ModDataChangeType.Description; - mod.Description = newDescription; - } - - if (mod.Image != newImage) - { - changes |= ModDataChangeType.Image; - mod.Image = newImage; - } - - if (mod.Version != newVersion) - { - changes |= ModDataChangeType.Version; - mod.Version = newVersion; - } - - if (mod.Website != newWebsite) - { - changes |= ModDataChangeType.Website; - mod.Website = newWebsite; - } - - if (newFileVersion != ModMeta.FileVersion) - if (ModMigration.Migrate(creator, saveService, mod, json, ref newFileVersion)) - { - changes |= ModDataChangeType.Migration; - saveService.ImmediateSave(new ModMeta(mod)); - } - - if (importDate != null && mod.ImportDate != importDate.Value) - { - mod.ImportDate = importDate.Value; - changes |= ModDataChangeType.ImportDate; - } - - changes |= ModLocalData.UpdateTags(mod, modTags, null); - - return changes; - } - catch (Exception e) - { - Penumbra.Log.Error($"Could not load mod meta for {metaFile}:\n{e}"); - return ModDataChangeType.Deletion; - } - } - public void ChangeModName(Mod mod, string newName) { if (mod.Name.Text == newName) diff --git a/Penumbra/Mods/ModCreator.cs b/Penumbra/Mods/ModCreator.cs index 18d2bc09..0db83ef9 100644 --- a/Penumbra/Mods/ModCreator.cs +++ b/Penumbra/Mods/ModCreator.cs @@ -72,11 +72,11 @@ public partial class ModCreator( if (!Directory.Exists(mod.ModPath.FullName)) return false; - modDataChange = dataEditor.LoadMeta(this, mod); + modDataChange = ModMeta.Load(dataEditor, this, mod); if (modDataChange.HasFlag(ModDataChangeType.Deletion) || mod.Name.Length == 0) return false; - modDataChange |= dataEditor.LoadLocalData(mod); + modDataChange |= ModLocalData.Load(dataEditor, mod); LoadDefaultOption(mod); LoadAllGroups(mod); if (incorporateMetaChanges) diff --git a/Penumbra/Mods/ModLocalData.cs b/Penumbra/Mods/ModLocalData.cs index beda0dc7..d3534391 100644 --- a/Penumbra/Mods/ModLocalData.cs +++ b/Penumbra/Mods/ModLocalData.cs @@ -27,6 +27,63 @@ public readonly struct ModLocalData(Mod mod) : ISavable jObject.WriteTo(jWriter); } + public static ModDataChangeType Load(ModDataEditor editor, Mod mod) + { + var dataFile = editor.SaveService.FileNames.LocalDataFile(mod); + + var importDate = 0L; + var localTags = Enumerable.Empty(); + var favorite = false; + var note = string.Empty; + + var save = true; + if (File.Exists(dataFile)) + try + { + var text = File.ReadAllText(dataFile); + var json = JObject.Parse(text); + + importDate = json[nameof(Mod.ImportDate)]?.Value() ?? importDate; + favorite = json[nameof(Mod.Favorite)]?.Value() ?? favorite; + note = json[nameof(Mod.Note)]?.Value() ?? note; + localTags = (json[nameof(Mod.LocalTags)] as JArray)?.Values().OfType() ?? localTags; + save = false; + } + catch (Exception e) + { + Penumbra.Log.Error($"Could not load local mod data:\n{e}"); + } + + if (importDate == 0) + importDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + + ModDataChangeType changes = 0; + if (mod.ImportDate != importDate) + { + mod.ImportDate = importDate; + changes |= ModDataChangeType.ImportDate; + } + + changes |= ModLocalData.UpdateTags(mod, null, localTags); + + if (mod.Favorite != favorite) + { + mod.Favorite = favorite; + changes |= ModDataChangeType.Favorite; + } + + if (mod.Note != note) + { + mod.Note = note; + changes |= ModDataChangeType.Note; + } + + if (save) + editor.SaveService.QueueSave(new ModLocalData(mod)); + + return changes; + } + internal static ModDataChangeType UpdateTags(Mod mod, IEnumerable? newModTags, IEnumerable? newLocalTags) { if (newModTags == null && newLocalTags == null) diff --git a/Penumbra/Mods/ModMeta.cs b/Penumbra/Mods/ModMeta.cs index 39dd20e4..0cebcf81 100644 --- a/Penumbra/Mods/ModMeta.cs +++ b/Penumbra/Mods/ModMeta.cs @@ -1,5 +1,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using Penumbra.Mods.Editor; +using Penumbra.Mods.Manager; using Penumbra.Services; namespace Penumbra.Mods; @@ -28,4 +30,85 @@ public readonly struct ModMeta(Mod mod) : ISavable jWriter.Formatting = Formatting.Indented; jObject.WriteTo(jWriter); } + + public static ModDataChangeType Load(ModDataEditor editor, ModCreator creator, Mod mod) + { + var metaFile = editor.SaveService.FileNames.ModMetaPath(mod); + if (!File.Exists(metaFile)) + { + Penumbra.Log.Debug($"No mod meta found for {mod.ModPath.Name}."); + return ModDataChangeType.Deletion; + } + + try + { + var text = File.ReadAllText(metaFile); + var json = JObject.Parse(text); + + var newFileVersion = json[nameof(FileVersion)]?.Value() ?? 0; + + // Empty name gets checked after loading and is not allowed. + var newName = json[nameof(Mod.Name)]?.Value() ?? string.Empty; + + var newAuthor = json[nameof(Mod.Author)]?.Value() ?? string.Empty; + var newDescription = json[nameof(Mod.Description)]?.Value() ?? string.Empty; + var newImage = json[nameof(Mod.Image)]?.Value() ?? string.Empty; + var newVersion = json[nameof(Mod.Version)]?.Value() ?? string.Empty; + var newWebsite = json[nameof(Mod.Website)]?.Value() ?? string.Empty; + var modTags = (json[nameof(Mod.ModTags)] as JArray)?.Values().OfType(); + + ModDataChangeType changes = 0; + if (mod.Name != newName) + { + changes |= ModDataChangeType.Name; + mod.Name = newName; + } + + if (mod.Author != newAuthor) + { + changes |= ModDataChangeType.Author; + mod.Author = newAuthor; + } + + if (mod.Description != newDescription) + { + changes |= ModDataChangeType.Description; + mod.Description = newDescription; + } + + if (mod.Image != newImage) + { + changes |= ModDataChangeType.Image; + mod.Image = newImage; + } + + if (mod.Version != newVersion) + { + changes |= ModDataChangeType.Version; + mod.Version = newVersion; + } + + if (mod.Website != newWebsite) + { + changes |= ModDataChangeType.Website; + mod.Website = newWebsite; + } + + if (newFileVersion != FileVersion) + if (ModMigration.Migrate(creator, editor.SaveService, mod, json, ref newFileVersion)) + { + changes |= ModDataChangeType.Migration; + editor.SaveService.ImmediateSave(new ModMeta(mod)); + } + + changes |= ModLocalData.UpdateTags(mod, modTags, null); + + return changes; + } + catch (Exception e) + { + Penumbra.Log.Error($"Could not load mod meta for {metaFile}:\n{e}"); + return ModDataChangeType.Deletion; + } + } } From ec3ec7db4e686318c9d7c2ee7a3ded8cc6357d4f Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Fri, 17 Jan 2025 19:55:02 +0100 Subject: [PATCH 4/6] update schema organization anf change some things. --- Penumbra.sln | 32 ++ schemas/container.json | 486 --------------------- schemas/default_mod.json | 20 +- schemas/group.json | 189 +------- schemas/local_mod_data-v3.json | 32 ++ schemas/{meta-v3.json => mod_meta-v3.json} | 17 +- schemas/structs/container.json | 34 ++ schemas/structs/group_combining.json | 31 ++ schemas/structs/group_imc.json | 50 +++ schemas/structs/group_multi.json | 32 ++ schemas/structs/group_single.json | 22 + schemas/structs/manipulation.json | 95 ++++ schemas/structs/meta_atch.json | 67 +++ schemas/structs/meta_enums.json | 57 +++ schemas/structs/meta_eqdp.json | 30 ++ schemas/structs/meta_eqp.json | 20 + schemas/structs/meta_est.json | 28 ++ schemas/structs/meta_geqp.json | 40 ++ schemas/structs/meta_gmp.json | 59 +++ schemas/structs/meta_imc.json | 87 ++++ schemas/structs/meta_rsp.json | 20 + schemas/structs/option.json | 24 + 22 files changed, 796 insertions(+), 676 deletions(-) delete mode 100644 schemas/container.json create mode 100644 schemas/local_mod_data-v3.json rename schemas/{meta-v3.json => mod_meta-v3.json} (79%) create mode 100644 schemas/structs/container.json create mode 100644 schemas/structs/group_combining.json create mode 100644 schemas/structs/group_imc.json create mode 100644 schemas/structs/group_multi.json create mode 100644 schemas/structs/group_single.json create mode 100644 schemas/structs/manipulation.json create mode 100644 schemas/structs/meta_atch.json create mode 100644 schemas/structs/meta_enums.json create mode 100644 schemas/structs/meta_eqdp.json create mode 100644 schemas/structs/meta_eqp.json create mode 100644 schemas/structs/meta_est.json create mode 100644 schemas/structs/meta_geqp.json create mode 100644 schemas/structs/meta_gmp.json create mode 100644 schemas/structs/meta_imc.json create mode 100644 schemas/structs/meta_rsp.json create mode 100644 schemas/structs/option.json diff --git a/Penumbra.sln b/Penumbra.sln index 94a04ef3..c0b38118 100644 --- a/Penumbra.sln +++ b/Penumbra.sln @@ -24,6 +24,34 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Penumbra.String", "Penumbra EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Penumbra.CrashHandler", "Penumbra.CrashHandler\Penumbra.CrashHandler.csproj", "{EE834491-A98F-4395-BE0D-6861AE5AD953}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Schemas", "Schemas", "{BFEA7504-1210-4F79-A7FE-BF03B6567E33}" + ProjectSection(SolutionItems) = preProject + schemas\files\default_mod.json = schemas\files\default_mod.json + schemas\files\group.json = schemas\files\group.json + schemas\files\local_mod_data-v3.json = schemas\files\local_mod_data-v3.json + schemas\files\mod_meta-v3.json = schemas\files\mod_meta-v3.json + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "structs", "structs", "{B03F276A-0572-4F62-AF86-EF62F6B80463}" + ProjectSection(SolutionItems) = preProject + schemas\structs\container.json = schemas\structs\container.json + schemas\structs\group_combining.json = schemas\structs\group_combining.json + schemas\structs\group_imc.json = schemas\structs\group_imc.json + schemas\structs\group_multi.json = schemas\structs\group_multi.json + schemas\structs\group_single.json = schemas\structs\group_single.json + schemas\structs\manipulation.json = schemas\structs\manipulation.json + schemas\structs\meta_atch.json = schemas\structs\meta_atch.json + schemas\structs\meta_enums.json = schemas\structs\meta_enums.json + schemas\structs\meta_eqdp.json = schemas\structs\meta_eqdp.json + schemas\structs\meta_eqp.json = schemas\structs\meta_eqp.json + schemas\structs\meta_est.json = schemas\structs\meta_est.json + schemas\structs\meta_geqp.json = schemas\structs\meta_geqp.json + schemas\structs\meta_gmp.json = schemas\structs\meta_gmp.json + schemas\structs\meta_imc.json = schemas\structs\meta_imc.json + schemas\structs\meta_rsp.json = schemas\structs\meta_rsp.json + schemas\structs\option.json = schemas\structs\option.json + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -58,6 +86,10 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {BFEA7504-1210-4F79-A7FE-BF03B6567E33} = {F89C9EAE-25C8-43BE-8108-5921E5A93502} + {B03F276A-0572-4F62-AF86-EF62F6B80463} = {BFEA7504-1210-4F79-A7FE-BF03B6567E33} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {B17E85B1-5F60-4440-9F9A-3DDE877E8CDF} EndGlobalSection diff --git a/schemas/container.json b/schemas/container.json deleted file mode 100644 index 5798f46c..00000000 --- a/schemas/container.json +++ /dev/null @@ -1,486 +0,0 @@ -{ - "$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 index eecd74d0..8f50c5db 100644 --- a/schemas/default_mod.json +++ b/schemas/default_mod.json @@ -1,25 +1,19 @@ { "$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"] + "description": "Mod Container version, currently unused.", + "type": "integer", + "minimum": 0, + "maximum": 0 } } - }, { - "$ref": "https://raw.githubusercontent.com/xivdev/Penumbra/master/schemas/container.json" + }, + { + "$ref": "structs/container.json" } ] } diff --git a/schemas/group.json b/schemas/group.json index 0078e9f3..4c37b631 100644 --- a/schemas/group.json +++ b/schemas/group.json @@ -1,35 +1,35 @@ { "$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"] + "description": "Mod Container version, currently unused.", + "type": "integer" }, "Name": { "description": "Name of the group.", - "type": "string" + "type": "string", + "minLength": 1 }, "Description": { "description": "Description of the group.", - "type": ["string", "null"] + "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"] + "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"] + "type": "integer" }, "Priority": { "description": "Priority of the group. If several groups define conflicting files or manipulations, the highest priority wins.", - "type": ["integer", "null"] + "type": "integer" }, "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"] + "enum": [ "Single", "Multi", "Imc", "Combining" ] }, "DefaultSettings": { "description": "Default configuration for the group.", @@ -42,165 +42,16 @@ ], "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" - } - } - } + "$ref": "structs/group_combining.json" + }, + { + "$ref": "structs/group_imc.json" + }, + { + "$ref": "structs/group_multi.json" + }, + { + "$ref": "structs/group_single.json" } - ], - "unevaluatedProperties": false + ] } diff --git a/schemas/local_mod_data-v3.json b/schemas/local_mod_data-v3.json new file mode 100644 index 00000000..bf5d1311 --- /dev/null +++ b/schemas/local_mod_data-v3.json @@ -0,0 +1,32 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Local Penumbra Mod Data", + "description": "The locally stored data for an installed mod in Penumbra", + "type": "object", + "properties": { + "FileVersion": { + "description": "Major version of the local data schema used.", + "type": "integer", + "minimum": 3, + "maximum": 3 + }, + "ImportDate": { + "description": "The date and time of the installation of the mod as a Unix Epoch millisecond timestamp.", + "type": "integer" + }, + "LocalTags": { + "description": "User-defined local tags for the mod.", + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "uniqueItems": true + }, + "Favorite": { + "description": "Whether the mod is favourited by the user.", + "type": "boolean" + } + }, + "required": [ "FileVersion" ] +} diff --git a/schemas/meta-v3.json b/schemas/mod_meta-v3.json similarity index 79% rename from schemas/meta-v3.json rename to schemas/mod_meta-v3.json index 1a132264..a926b49e 100644 --- a/schemas/meta-v3.json +++ b/schemas/mod_meta-v3.json @@ -1,6 +1,5 @@ { "$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", @@ -13,33 +12,35 @@ }, "Name": { "description": "Name of the mod.", - "type": "string" + "type": "string", + "minLength": 1 }, "Author": { "description": "Author of the mod.", - "type": ["string", "null"] + "type": [ "string", "null" ] }, "Description": { "description": "Description of the mod. Can span multiple paragraphs.", - "type": ["string", "null"] + "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"] + "type": [ "string", "null" ] }, "Version": { "description": "Version of the mod. Can be an arbitrary string.", - "type": ["string", "null"] + "type": [ "string", "null" ] }, "Website": { "description": "URL of the web page of the mod.", - "type": ["string", "null"] + "type": [ "string", "null" ] }, "ModTags": { "description": "Author-defined tags for the mod.", "type": "array", "items": { - "type": "string" + "type": "string", + "minLength": 1 }, "uniqueItems": true } diff --git a/schemas/structs/container.json b/schemas/structs/container.json new file mode 100644 index 00000000..74db4a23 --- /dev/null +++ b/schemas/structs/container.json @@ -0,0 +1,34 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "Files": { + "description": "File redirections in this container. Keys are game paths, values are relative file paths.", + "type": [ "object", "null" ], + "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", "null" ], + "patternProperties": { + "^[^/\\\\.?:][^\\\\?:]+$": { + "type": "string", + "pattern": "^[^/\\\\.:?][^?:]+$" + } + }, + "additionalProperties": false + }, + "Manipulations": { + "type": [ "array", "null" ], + "items": { + "$ref": "manipulation.json" + } + } + } +} diff --git a/schemas/structs/group_combining.json b/schemas/structs/group_combining.json new file mode 100644 index 00000000..e42edcb8 --- /dev/null +++ b/schemas/structs/group_combining.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "Type": { + "const": "Combining" + }, + "Options": { + "type": "array", + "items": { + "$ref": "option.json" + } + }, + "Containers": { + "type": "array", + "items": { + "allOf": [ + { + "$ref": "container.json" + }, + { + "properties": { + "Name": { + "type": [ "string", "null" ] + } + } + } + ] + } + } + } +} diff --git a/schemas/structs/group_imc.json b/schemas/structs/group_imc.json new file mode 100644 index 00000000..48a04bd9 --- /dev/null +++ b/schemas/structs/group_imc.json @@ -0,0 +1,50 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "Type": { + "const": "Imc" + }, + "AllVariants": { + "type": "boolean" + }, + "OnlyAttributes": { + "type": "boolean" + }, + "Identifier": { + "$ref": "meta_imc.json#ImcIdentifier" + }, + "DefaultEntry": { + "$ref": "meta_imc.json#ImcEntry" + }, + "Options": { + "type": "array", + "items": { + "$ref": "option.json", + "oneOf": [ + { + "properties": { + "AttributeMask": { + "type": "integer", + "minimum": 0, + "maximum": 1023 + } + }, + "required": [ + "AttributeMask" + ] + }, + { + "properties": { + "IsDisableSubMod": { + "const": true + } + }, + "required": [ + "IsDisableSubMod" + ] + } + ] + } + } + } +} diff --git a/schemas/structs/group_multi.json b/schemas/structs/group_multi.json new file mode 100644 index 00000000..ca7d4dfa --- /dev/null +++ b/schemas/structs/group_multi.json @@ -0,0 +1,32 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "Type": { + "const": "Multi" + }, + "Options": { + "type": "array", + "items": { + "allOf": [ + { + "$ref": "option.json" + }, + { + "$ref": "container.json" + }, + { + "properties": { + "Priority": { + "type": "integer" + } + } + } + ] + } + } + } +} + + + diff --git a/schemas/structs/group_single.json b/schemas/structs/group_single.json new file mode 100644 index 00000000..24cda88d --- /dev/null +++ b/schemas/structs/group_single.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "Type": { + "const": "Single" + }, + "Options": { + "type": "array", + "items": { + "allOf": [ + { + "$ref": "option.json" + }, + { + "$ref": "container.json" + } + ] + } + } + } +} diff --git a/schemas/structs/manipulation.json b/schemas/structs/manipulation.json new file mode 100644 index 00000000..4a41dbe2 --- /dev/null +++ b/schemas/structs/manipulation.json @@ -0,0 +1,95 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "Type": { + "enum": [ "Unknown", "Imc", "Eqdp", "Eqp", "Est", "Gmp", "Rsp", "GlobalEqp", "Atch" ] + }, + "Manipulation": { + "type": "object" + } + }, + "required": [ "Type", "Manipulation" ], + "oneOf": [ + { + "properties": { + "Type": { + "const": "Imc" + }, + "Manipulation": { + "$ref": "meta_imc.json" + } + } + }, + { + "properties": { + "Type": { + "const": "Eqdp" + }, + "Manipulation": { + "$ref": "meta_eqdp.json" + } + } + }, + { + "properties": { + "Type": { + "const": "Eqp" + }, + "Manipulation": { + "$ref": "meta_eqp.json" + } + } + }, + { + "properties": { + "Type": { + "const": "Est" + }, + "Manipulation": { + "$ref": "meta_est.json" + } + } + }, + { + "properties": { + "Type": { + "const": "Gmp" + }, + "Manipulation": { + "$ref": "meta_gmp.json" + } + } + }, + { + "properties": { + "Type": { + "const": "Rsp" + }, + "Manipulation": { + "$ref": "meta_rsp.json" + } + } + }, + { + "properties": { + "Type": { + "const": "GlobalEqp" + }, + "Manipulation": { + "$ref": "meta_geqp.json" + } + } + }, + { + "properties": { + "Type": { + "const": "Atch" + }, + "Manipulation": { + "$ref": "meta_atch.json" + } + } + } + ] +} diff --git a/schemas/structs/meta_atch.json b/schemas/structs/meta_atch.json new file mode 100644 index 00000000..3c9cbef5 --- /dev/null +++ b/schemas/structs/meta_atch.json @@ -0,0 +1,67 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "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" + ] + }, + "Gender": { + "$ref": "meta_enums.json#Gender" + }, + "Race": { + "$ref": "meta_enums.json#ModelRace" + }, + "Type": { + "type": "string", + "minLength": 1, + "maxLength": 4 + }, + "Index": { + "$ref": "meta_enums.json#U16" + } + }, + "required": [ + "Entry", + "Gender", + "Race", + "Type", + "Index" + ] +} diff --git a/schemas/structs/meta_enums.json b/schemas/structs/meta_enums.json new file mode 100644 index 00000000..747da849 --- /dev/null +++ b/schemas/structs/meta_enums.json @@ -0,0 +1,57 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$defs": { + "EquipSlot": { + "$anchor": "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": { + "$anchor": "Gender", + "enum": [ "Unknown", "Male", "Female", "MaleNpc", "FemaleNpc" ] + }, + "ModelRace": { + "$anchor": "ModelRace", + "enum": [ "Unknown", "Midlander", "Highlander", "Elezen", "Lalafell", "Miqote", "Roegadyn", "AuRa", "Hrothgar", "Viera" ] + }, + "ObjectType": { + "$anchor": "ObjectType", + "enum": [ "Unknown", "Vfx", "DemiHuman", "Accessory", "World", "Housing", "Monster", "Icon", "LoadingScreen", "Map", "Interface", "Equipment", "Character", "Weapon", "Font" ] + }, + "BodySlot": { + "$anchor": "BodySlot", + "enum": [ "Unknown", "Hair", "Face", "Tail", "Body", "Zear" ] + }, + "SubRace": { + "$anchor": "SubRace", + "enum": [ "Unknown", "Midlander", "Highlander", "Wildwood", "Duskwight", "Plainsfolk", "Dunesfolk", "SeekerOfTheSun", "KeeperOfTheMoon", "Seawolf", "Hellsguard", "Raen", "Xaela", "Helion", "Lost", "Rava", "Veena" ] + }, + "U8": { + "$anchor": "U8", + "oneOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 255 + }, + { + "type": "string", + "pattern": "^0*(1[0-9][0-9]|[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$" + } + ] + }, + "U16": { + "$anchor": "U16", + "oneOf": [ + { + "type": "integer", + "minimum": 0, + "maximum": 65535 + }, + { + "type": "string", + "pattern": "^0*([1-5][0-9]{4}|[0-9]{0,4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$" + } + ] + } + } +} diff --git a/schemas/structs/meta_eqdp.json b/schemas/structs/meta_eqdp.json new file mode 100644 index 00000000..f27606b9 --- /dev/null +++ b/schemas/structs/meta_eqdp.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "Entry": { + "type": "integer", + "minimum": 0, + "maximum": 1023 + }, + "Gender": { + "$ref": "meta_enums.json#Gender" + }, + "Race": { + "$ref": "meta_enums.json#ModelRace" + }, + "SetId": { + "$ref": "meta_enums.json#U16" + }, + "Slot": { + "$ref": "meta_enums.json#EquipSlot" + } + }, + "required": [ + "Entry", + "Gender", + "Race", + "SetId", + "Slot" + ] +} diff --git a/schemas/structs/meta_eqp.json b/schemas/structs/meta_eqp.json new file mode 100644 index 00000000..c829d7a7 --- /dev/null +++ b/schemas/structs/meta_eqp.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "Entry": { + "type": "integer" + }, + "SetId": { + "$ref": "meta_enums.json#U16" + }, + "Slot": { + "$ref": "meta_enums.json#EquipSlot" + } + }, + "required": [ + "Entry", + "SetId", + "Slot" + ] +} diff --git a/schemas/structs/meta_est.json b/schemas/structs/meta_est.json new file mode 100644 index 00000000..22bce12b --- /dev/null +++ b/schemas/structs/meta_est.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "Entry": { + "$ref": "meta_enums.json#U16" + }, + "Gender": { + "$ref": "meta_enums.json#Gender" + }, + "Race": { + "$ref": "meta_enums.json#ModelRace" + }, + "SetId": { + "$ref": "meta_enums.json#U16" + }, + "Slot": { + "enum": [ "Hair", "Face", "Body", "Head" ] + } + }, + "required": [ + "Entry", + "Gender", + "Race", + "SetId", + "Slot" + ] +} diff --git a/schemas/structs/meta_geqp.json b/schemas/structs/meta_geqp.json new file mode 100644 index 00000000..3d4908f9 --- /dev/null +++ b/schemas/structs/meta_geqp.json @@ -0,0 +1,40 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "Condition": { + "$ref": "meta_enums.json#U16" + }, + "Type": { + "enum": [ "DoNotHideEarrings", "DoNotHideNecklace", "DoNotHideBracelets", "DoNotHideRingR", "DoNotHideRingL", "DoNotHideHrothgarHats", "DoNotHideVieraHats" ] + } + }, + "required": [ "Type" ], + "oneOf": [ + { + "properties": { + "Type": { + "const": [ "DoNotHideHrothgarHats", "DoNotHideVieraHats" ] + }, + "Condition": { + "const": 0 + } + } + }, + { + "properties": { + "Type": { + "const": [ "DoNotHideHrothgarHats", "DoNotHideVieraHats" ] + } + } + }, + { + "properties": { + "Type": { + "const": [ "DoNotHideEarrings", "DoNotHideNecklace", "DoNotHideBracelets", "DoNotHideRingR", "DoNotHideRingL" ] + }, + "Condition": {} + } + } + ] +} diff --git a/schemas/structs/meta_gmp.json b/schemas/structs/meta_gmp.json new file mode 100644 index 00000000..bf1fb1df --- /dev/null +++ b/schemas/structs/meta_gmp.json @@ -0,0 +1,59 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "Entry": { + "type": "object", + "properties": { + "Enabled": { + "type": "boolean" + }, + "Animated": { + "type": "boolean" + }, + "RotationA": { + "type": "integer", + "minimum": 0, + "maximum": 1023 + }, + "RotationB": { + "type": "integer", + "minimum": 0, + "maximum": 1023 + }, + "RotationC": { + "type": "integer", + "minimum": 0, + "maximum": 1023 + }, + "UnknownA": { + "type": "integer", + "minimum": 0, + "maximum": 15 + }, + "UnknownB": { + "type": "integer", + "minimum": 0, + "maximum": 15 + } + }, + "required": [ + "Enabled", + "Animated", + "RotationA", + "RotationB", + "RotationC", + "UnknownA", + "UnknownB" + ], + "additionalProperties": false + }, + "SetId": { + "$ref": "meta_enums.json#U16" + } + }, + "required": [ + "Entry", + "SetId" + ] +} diff --git a/schemas/structs/meta_imc.json b/schemas/structs/meta_imc.json new file mode 100644 index 00000000..aa9a4fca --- /dev/null +++ b/schemas/structs/meta_imc.json @@ -0,0 +1,87 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "Entry": { + "$ref": "#ImcEntry" + } + }, + "required": [ + "Entry" + ], + "allOf": [ + { + "$ref": "#ImcIdentifier" + } + ], + "$defs": { + "ImcIdentifier": { + "type": "object", + "properties": { + "PrimaryId": { + "$ref": "meta_enums.json#U16" + }, + "SecondaryId": { + "$ref": "meta_enums.json#U16" + }, + "Variant": { + "$ref": "meta_enums.json#U8" + }, + "ObjectType": { + "$ref": "meta_enums.json#ObjectType" + }, + "EquipSlot": { + "$ref": "meta_enums.json#EquipSlot" + }, + "BodySlot": { + "$ref": "meta_enums.json#BodySlot" + } + }, + "$anchor": "ImcIdentifier", + "required": [ + "PrimaryId", + "SecondaryId", + "Variant", + "ObjectType", + "EquipSlot", + "BodySlot" + ] + }, + "ImcEntry": { + "type": "object", + "properties": { + "MaterialId": { + "$ref": "meta_enums.json#U8" + }, + "DecalId": { + "$ref": "meta_enums.json#U8" + }, + "VfxId": { + "$ref": "meta_enums.json#U8" + }, + "MaterialAnimationId": { + "$ref": "meta_enums.json#U8" + }, + "AttributeMask": { + "type": "integer", + "minimum": 0, + "maximum": 1023 + }, + "SoundId": { + "type": "integer", + "minimum": 0, + "maximum": 63 + } + }, + "$anchor": "ImcEntry", + "required": [ + "MaterialId", + "DecalId", + "VfxId", + "MaterialAnimationId", + "AttributeMask", + "SoundId" + ] + } + } +} diff --git a/schemas/structs/meta_rsp.json b/schemas/structs/meta_rsp.json new file mode 100644 index 00000000..3354281b --- /dev/null +++ b/schemas/structs/meta_rsp.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "Entry": { + "type": "number" + }, + "SubRace": { + "$ref": "meta_enums.json#SubRace" + }, + "Attribute": { + "enum": [ "MaleMinSize", "MaleMaxSize", "MaleMinTail", "MaleMaxTail", "FemaleMinSize", "FemaleMaxSize", "FemaleMinTail", "FemaleMaxTail", "BustMinX", "BustMinY", "BustMinZ", "BustMaxX", "BustMaxY", "BustMaxZ" ] + } + }, + "required": [ + "Entry", + "SubRace", + "Attribute" + ] +} diff --git a/schemas/structs/option.json b/schemas/structs/option.json new file mode 100644 index 00000000..c45ccfdb --- /dev/null +++ b/schemas/structs/option.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "Name": { + "description": "Name of the option.", + "type": "string", + "minLength": 1 + }, + "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" + }, + "Image": { + "description": "Unused by Penumbra.", + "type": [ "string", "null" ] + } + }, + "required": [ "Name" ] +} From b62563d72131c2119370b3d25677457291c15b96 Mon Sep 17 00:00:00 2001 From: Exter-N Date: Fri, 17 Jan 2025 20:06:21 +0100 Subject: [PATCH 5/6] Remove $id from shpk_devkit schema --- schemas/shpk_devkit.json | 1 - 1 file changed, 1 deletion(-) diff --git a/schemas/shpk_devkit.json b/schemas/shpk_devkit.json index cd18ab81..f03fbb05 100644 --- a/schemas/shpk_devkit.json +++ b/schemas/shpk_devkit.json @@ -1,6 +1,5 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/xivdev/Penumbra/master/schemas/shpk_devkit.json", "type": "object", "properties": { "ShaderKeys": { From 4f0428832cadfe61c4c49dd7dcbfdeacde5332bd Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Mon, 20 Jan 2025 15:13:09 +0100 Subject: [PATCH 6/6] Fix solution file for schemas. --- Penumbra.sln | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Penumbra.sln b/Penumbra.sln index c0b38118..e864fbee 100644 --- a/Penumbra.sln +++ b/Penumbra.sln @@ -26,10 +26,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Penumbra.CrashHandler", "Pe EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Schemas", "Schemas", "{BFEA7504-1210-4F79-A7FE-BF03B6567E33}" ProjectSection(SolutionItems) = preProject - schemas\files\default_mod.json = schemas\files\default_mod.json - schemas\files\group.json = schemas\files\group.json - schemas\files\local_mod_data-v3.json = schemas\files\local_mod_data-v3.json - schemas\files\mod_meta-v3.json = schemas\files\mod_meta-v3.json + schemas\default_mod.json = schemas\default_mod.json + schemas\group.json = schemas\group.json + schemas\local_mod_data-v3.json = schemas\local_mod_data-v3.json + schemas\mod_meta-v3.json = schemas\mod_meta-v3.json EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "structs", "structs", "{B03F276A-0572-4F62-AF86-EF62F6B80463}"