Giter Site home page Giter Site logo

niftools / nifxml Goto Github PK

View Code? Open in Web Editor NEW
33.0 33.0 42.0 2.75 MB

A repository for the nif.xml file, which contains the nif file format description.

Home Page: http://www.niftools.org

License: GNU General Public License v3.0

Python 100.00%
documentation format nif

nifxml's Introduction

niftools.github.io

Installing Locally

Prerequisites

You need to install Ruby. After you install Ruby you can simply install Bundler: gem install bundler

Then to get the necessary gems for the site you run bundle install from the project root.

You will most likely get an SSL error while trying to build so you need to grab http://curl.haxx.se/ca/cacert.pem then install it anywhere (e.g. Ruby folder) and add it to your PATH.

OPTIONAL If you want to edit or create posts more easily (instead of by hand), you can uncomment - jekyll-admin in _config.yml to use the admin interface at localhost:4000/admin. This change does not need to be committed as jekyll-admin is local only. I would recommend leaving it otherwise commented as it appears to disable file watching while editing the site design.

OPTIONAL If you are ever designing new pages and need to change \bower_components in order to add package dependencies you will need to install Node.js/npm. You must then install Bower with npm install -g bower. From there you can add Bower packages to the project root's bower.json and then run bower install or you can run bower install <package>.

Building and Running

To build the site locally you run jekyll build. To serve the site locally you can run jekyll serve. This will serve from localhost:4000 and will watch for any changed files so that changes will show up as you make them.

To view what the site would look like with the posts in _drafts published you can run jekyll serve --drafts.

Front-Matter

Jekyll will parse any file with front matter at the very beginning of the file. Empty front matter looks like so:

   ---
   ---

You can also include the following variables:

Variable Description Format
layout: Which template to choose in _layouts. default, page, post
title: Text which shows in the webpage title and also the jumbotron <string>
date: Used to sort a post in chronological order. Also used in feed.xml. YYYY-MM-DD HH:MM:SS +/-TTTT
category: What category your post belongs in. <string>
categories: What categories your post belongs in. [<string>, ...]
tags: A list of tags relevant to the post. [<string>, ...]
excerpt: Text which shows in the page meta description. If used in a post it will act as the synopsis on the front page. <string>
subtitle: Text which shows under the title in the jumbotron. <string>
menu: Which navbar item is to have the class selected <string>
toc: Whether this page should have an auto-generated table of contents. true | false
geopattern: A custom string input for the jumbotron SVG background. Otherwise it uses title: <string>
navbar: Used to override the navbar style for this page. navbar-inverse navbar-inversed
css: Custom CSS to include on this page. Must install to /assets/css/ but only list the filename. [<string>, ...]
js: Custom JS to include on this page. Must install to /assets/js/ but only list the filename. [<string>, ...]

A common example of front matter for a post would be

---
layout: post
title:  "Example Post"
date:   2017-05-10 13:25:35 +0200
categories: nifskope releases
---

Publishing

To avoid publishing manually, you may use jekyll-admin if you have uncommented it in _config.yml and restarted jekyll. The admin interface will be available at localhost:4000/admin. You can include custom front matter variables in the field below the post text.

Posts

Placing any Markdown or HTML file in _posts with valid front matter will publish the file to the site. Where it goes will depend on the front matter such as the layout and what categories are listed. A post layout with categories: nifskope releases will get published to /nifskope/releases/.

Relevant Documentation

Categories

Most posts should likely consist of two categories: the project or main product name and a subcategory such as releases e.g. categories: nifskope releases or categories: blender releases. If posting about general developmental news or about multiple projects, use something like category: updates.

Pages

A page layout titled mypage.md will get published to /mypage if it is in the project root.

Relevant Documentation

nifxml's People

Contributors

abies avatar aerisarn avatar alphax avatar amorilia avatar aperture-it avatar arves100 avatar bobtedbob avatar candoran2 avatar davidboese avatar figment avatar ghostwalker71 avatar hexabits avatar luxaritas avatar madace avatar neomonkeus avatar nickdickinsonwilde avatar ousnius avatar skyfox69 avatar throttlekitty avatar ttl269 avatar usernameak avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nifxml's Issues

Add support for Aura Kingdom nifs

Problem

Aura Kingdom nifs appear to have some properties that are not yet denoted in the xml.

Solution

Hexabits apparently made a number of changes to the xml (linked and somewhat discussed on niftools/nifskope#187 ), but it still needs to be seen what exactly those changes are and whether they can be integrated into the main fork.

If it is possible, then obviously do that. Here is an example of an Aura Kingdom nif for analysis and testing whether the changes to the xml are sufficient:
test.zip

If it is not possible, this issue can at least serve as a place to put down exactly why that can't happen and what the exact differences are.

[Spec] Version tag changes

There are currently several issues with <version> that need to be addressed for various lib and scripting features.

Version Granularity

The primary issue is that there are more "versions" than <version> tags, when you include User Version and User Version 2 (which is actually more appropriately BS Version or BS Stream). Version should mean not just the 4 MAJ/MIN/REV/PATCH components, but a distinct NIF header for which all blocks are identical in layout.

This means spreading many existing versions across multiple elements, and adding several attributes to differentiate them.

Unique Identifier: id = <string>

Now that a <version> is a combination of multiple values, each version should have an id attribute. This ID must be unique and ideally can be used in code generation of enumerations. Thusly, a format was chosen similar to existing enumerations for NIF versions in the various projects.

ID must start with V and should only separate Version components with underscores. Currently, two underscores separate the Version and User Version when applicable. When a version is highly associated with only one game and vice versa, instead of using User Version or (User Version + BS Version), the ID can be suffixed with an abbreviation for the game, e.g. V20_2_0_7_FO4.

Note: Right now, the User Version is generally appended to the ID, but the BS Version is not. This is because multiple BS Versions are likely to be listed/grouped for one <version> and it's possible to lose/gain BS Versions as the version conditions are modified or simplified.

User Versions: user = <List[int]>

This is the User Version in the header. The default is assumed to be 0. However, User Version did not technically exist until after version 10.0.1.8. Despite this, there need not be any distinction between 0 and null for User Version. All versions shall report a User Version of 0 unless it is overridden in the attributes.

<List[int]> will be explained in Grouping of User and Bethesda versions

Bethesda Versions: bsver = <List[int]>

Alternative name: bsstream, named after the BSStreamHeader struct Bethesda added to the header serialization/deserialization which contains the stream version and export strings.

The default is assumed to be 0. This is vital as it is used directly in version conditions to exclude non-Bethesda files or vice versa.

The Bethesda Version is almost entirely the only thing checked against in Bethesda's own blocks during de/serialization, and so it is actually more important than User Version, which is very rarely checked against for any NIF version.

Grouping of User and Bethesda versions

There are sets/ranges of User and Bethesda versions that are functionally equivalent to each other. They still need to be enumerated however, so this requires that user and bsver accept space-separated lists. Space-separated is the appropriate way to write lists in XML and should be maintained. (The comma-separated lists currently in nif.xml are technically not incorrect as it is actually a string used directly in code generation.)

Important: Hex-formatted integers must also be supported. The custom versions for Divinity 2 are easier to reference in hex (0x10000, 0x20000, 0x30000).

Example:

<version id="V20_2_0_7__11_7" num="20.2.0.7" user="11" bsver="27 28">Fallout 3, Fallout NV</version>
<version id="V20_2_0_7__11_8" num="20.2.0.7" user="11" bsver="30 31 32 33">Fallout 3, Fallout NV</version>
<version id="V20_2_0_7_FO3" num="20.2.0.7" user="11" bsver="34">Fallout 3, Fallout NV</version>

Custom Versions: custom = <boolean>

Some games have ignored User Version and instead created their own 4-component Version and left User Version at 0. The custom attribute set to true denotes that it is not an official Gamebryo version and has customization of one or more blocks. For versions with a user or bsver, custom="true" is implied and redundant to specify.

Even though this is listed under Version Granularity, two otherwise identical <version> but one with custom="true" and one with custom="false" should not be allowed at this time. There is generally no way to actually tell the difference by reading the header, etc. when a Custom Version uses the same 4-component Version number as an official Gamebryo Version. This does in fact happen in practice, and those custom versions are not currently supported by the XML.

Other Considerations

Aside from version granularity issues, there are other lacking version-related descriptors that the XML would benefit from having.

Supported status: supported = <boolean>

Right now this is designed to be true/false, but there is the possibility of it becoming more (tri-state, enum). The default is assumed to be true. If set to false this means that in some way or another, this version is not fully decoded and either some or all files of that version will not read/write correctly regardless of the software.

This attribute has many potential uses. Most generally, software/libs can decide to ignore unsupported versions. This is important for both code generation and runtime interpreters of the XML. There are currently 10 versions marked as unsupported and when ignored they all fairly drastically reduce the complexity of the blocks that those versions influence.

Custom file extensions ext = <List[string]>

There is no formalized association of NIF files with custom extensions and what versions use them. This will associate the custom extensions with their version(s) and this info can be used to automate the ability to acknowledge other file extensions than NIF/KF for software/libs.

Example:

<version id="V20_2_0_7_FO3" num="20.2.0.7" user="11" bsver="34" ext="rdt">Fallout 3, Fallout NV</version>
<version id="V20_2_0_7_SKY" num="20.2.0.7" user="12" bsver="83" ext="bto btr">Skyrim</version>
<version id="V20_3_0_9" num="20.3.0.9" user="0 0x10000" ext="nft item">Warhammer, Lazeska, Howling Sword, Bully SE, Divinity 2 (0x10000)</version>

Removal of hypothetical or unused versions

The listed versions should be concrete, real-world examples of NIFs found in the wild. There is at least one version that seems purely hypothetical (10.0.1.3) and doesn't even have any games listed for it.

The "demo/example" versions listed in the XML might also be better if they were dropped. For example: 20.3.0.1, 20.3.0.2, 20.3.0.3, 20.3.0.6 do not seem to actually appear in any real games. Their presence or absence do not really change anything at the moment, but upcoming features depend on keeping the number of versions low for complexity/performance/storage reasons.

Important: Versions used in attributes like vercond do not need to be in the list of versions. These versions are often purely hypothetical and are an artifact of the iterative development of Gamebryo.

Listed order must be maintained

The id attribute has use in generating enumerations and so the ordering of <version> has implicit value. The first version == 0, the second == 1 and so on.

Number of versions must stay under 65

The version count should stay under 65 at all costs, and under 54 if possible. A new feature will be encoding the applicable versions for every <add> into a 64-bit integer, i.e. a bitflag. To allow this to be used by as many languages as possible, it should stay inside of 64 bits. Ideally, it should try to stay within 53 bits, as some programming languages' underlying number type is double-precision floating point. In these languages only the integers up to 253 can be exactly represented.

Game List formatting

The element content for each <version> requires special formatting.

  1. Inner element text must be comma-separated list of games
  2. {{}} around a game name means this <version> is most important to that game, i.e. it should be considered the "primary" version for that game.
  3. Each game must have one instance at most of {{}} around its name.
  4. A <version> without any {{}} games can be considered "secondary". This is useful for determining the level of support for said version. For example, a <version> with only secondary usage might be ignored in UI for exporters or programs, or ignored for codegen.

Note: In the case of Oblivion, there are two "primary" versions.. 20.0.0.4 (10) is used for KF and 20.0.0.5 is used for NIF. Thus I changed the "game name" to {{Oblivion KF}} for 20.0.0.4 (10).

Example

Gist for Current WIP spec

For Discussion

Yes, FO3/FNV really have that many BS Versions. No, there's nothing to do about combining them into fewer versions, because the XML has been analyzed and the version splits are all made ideally. Any changes would result in a few files no longer loading correctly.

Epic Mickey 15/17 split is unconfirmed. The Divinity 2 0x10000/0x20000/0x30000 splits are unconfirmed.

There's not much for actual discussion other than my formatting choices for the id attribute. As I stated, the groupings of bsver could potentially change, either split up or lumped together. So putting a volatile set of integers into the ID seems like a poor choice. For User Versions, usually the ID just gets an identifier appended that references the game, like for Divinity 2.

The double underscore before the User Version when included is so that it doesn't look like part of the Version components.

License for nif.xml?

I am considering using the contents of the nif.xml for one of my projects to figure out how I should write up an NIF reader. The problem I have is that I'm not sure what the licensing for this file is and how compatible it is with my project's MIT license.

The repo's license file specifies the GPL 3 but the commit summary says that this is for the parser and docs gen and there is nothing in the XML file itself that states the license. Please can this be clarified? Thanks.

Name uniqueness issues

Recent nif.xml changes have required a type to vary while the name remains the same. This is typically because the type changes between versions yet the function is identical. Examples include:

HavokColFilter Layer
HavokMaterial
BSVertexData/BSVertexDataSSE

There are more but these are the most obvious examples. Here is a visual example:

The "Layer" name needs to remain the same name for several reasons:

  1. The function does not change.
  2. The version/game is irrelevant to the name.
  3. The name remaining consistent is better for users, i.e. you do not need to specify which "Layer" specifically as it is always just "Layer".
  4. Functionality in programs such as NifSkope rely on the name and changing the name will break the functionality.

The issue is that C++ is strongly typed and so niflib has a problem with its codegen. Refer to #62. Relevant discussion: #62 (review)

So far I have suggested that gen_niflib.py will need to change in some way to accommodate this requirement. Either it would fix it internally by creating unique names automatically, or we could change nif.xml to accommodate niflib's codegen. So far I have suggested something like so:

Proposal

An attribute which, when present, gen_niflib.py will use instead of name.

        <add name="Layer" uniquename="Layer_OB" type="OblivionLayer" ... />
        <add name="Layer" uniquename="Layer_FO" type="Fallout3Layer" ... />
        <add name="Layer" uniquename="Layer_SK" type="SkyrimLayer" ... />

The exact string used for the attribute is up for discussion, though I think it is likely the best choice. uniqueid would imply UUID, and niflibname is accurate but project-specific. A generic uniquename thus seems like the best option.

Updated Proposal

        <add name="Layer" suffix="OB" type="OblivionLayer" ... />
        <add name="Layer" suffix="FO" type="Fallout3Layer" ... />
        <add name="Layer" suffix="SK" type="SkyrimLayer" ... />

In niflib this would become:

OblivionLayer layer_ob;
Fallout3Layer layer_fo;
SkyrimLayer layer_sk;

Support for NiDavi...Streamable blocks.

Certain meshes from Knight Rider 2 appear to contain at least 4 unknown blocks:

  • NiDaviWaypointsStreamable
  • NiDaviPhysicsStreamable
  • NiDaviChunkListStreamable
  • NiDaviShadowSkinStreamable

I haven't analyzed this format, but someone on the NifMania server came with the following nifs:
TestTracks.zip
So I thought I should at least create an issue.

Non compatible change support

@ttl269 @throttlekitty @jonwd7 @Ghostwalker71 @deedes @skyfox69
Just getting the ball rolling.

As per the discussion in #24 EE2 AoS breaks general nif format by not incrementing the user version, such that the version differences can't be detected through normal condition checking.

I mod EE2 models ok, but i cant do with the expansion (Art of Supremacy) models, when i open AoS models appears this error: ""array Properties much too large. 16777216 bytes requested""
"failed to load block number 0 (NiNode) previous block was NiHeader"
""infinite recursive link construct detected 0 -> 0""
"failed to load nif from 'C:/Documents and Settings/GONGALO/Configuración local/Temp/Rar$DI97.584/civ10_cuirassier_French.nifcache'"

I'm really confused about the structure of this project

I'm in the process of writing a native .NET parser that consumes nif.xml. I have most of the read structures ported, but at this point I'm completely confused as to what the authoritative source is for the nif.xml format. nifdocsys seems to use one (really old) format, the file in this repo uses field for struct members, and yet pyffi seems pinned to a older version of this doc that doesn't use field and instead uses add.

I had leveraged gen_niflib.py to generate my C# code, but it seems like that code is deprecated in favor of PyFFI? At any rate, could someone tell me how these projects are structured, is the latest commit in this repo the authoritative source for nif.xml? And if so what's the authoritative source for code generation?

Thanks!

Support for QQSpeed nifs

Bug Nif version not support (v0.1.0 and v0.0.14)

image

I can't import NIF file from QQ飞车 game
I'm using Blender ver2.29 (2021) /// Niftools_addon v0.0.14 and also use Niftools_addon v0.1.0 with Blender newest ver (2023)

File:
Nif File.zip

[Spec] Additions, Removals, and Deprecations

This is a catch-all for attribute changes and their discussion that are not covered by other tickets.

Replace/Remove/Rename

General

  • For any attributes intended as boolean, true/false should be used over 1/0. They are valid in XML as well, and more clear as to their intent for vague attribute names.
  • Replace TEMPLATE and ARG with #T# and #ARG#, to match with #70

'Flags' type removal

As discussed in #3, a replacement for Flags is desperately needed. Instead of rewriting the issue of that ticket I will put everything here with the rest of the removals and changes.

While the comments of issue #3 were very informative for the concept and possible solutions, we were all trying to change <bitflags> to suit our needs. A bitflag is an enumeration, and in languages like C++ is able to be represented with an enum, except instead of 0,1,2,3, the values go 0x1,0x2,0x4,0x8, just like we are doing with our enum and bitflags.

What we were really discussing was bitfields, which are separate from enumerations. They are simply a struct, but with a bit spanning syntax. In my 010 editor templates (C-like), a bitfield looks like this:

typedef union {
    ushort value;
    struct {
        ushort doBlending      : 1;
        BlendFunction srcBlend : 4;
        BlendFunction dstBlend : 4;
        ushort doTesting       : 1;
        TestFunction testFunc  : 3;
        ushort noSorter        : 1;
    } bits;
} AlphaFlags;

A bitflag can also be represented in this way, but only with : 1 at the end of each statement. (I will note that in 010 editor I also used bitfields for bitflags, but simply due to the way the UI treats enums.)

The current proposed syntax is:

<bitfield name="TimeControllerFlags" storage="ushort">
    Flags for NiTimeController
    <member width="1" pos="0" mask="0x0001" name="Anim Type" type="AnimType" />
    <member width="2" pos="1" mask="0x0006" name="Cycle Type" type="CycleType" default="CYCLE_CLAMP" />
    <member width="1" pos="3" mask="0x0008" name="Active" type="bool" default="1" />
    <member width="1" pos="4" mask="0x0010" name="Play Backwards" type="bool" />
    <member width="1" pos="5" mask="0x0020" name="Manager Controlled" type="bool" />
    <member width="1" pos="6" mask="0x0040" name="Compute Scaled Time" type="bool" default="1" />
    <member width="1" pos="7" mask="0x0080" name="Forced Update" type="bool" />
</bitfield>

For C/C++ and other languages with actual bitfields, only the name, storage="ushort", and width are needed. The type shown here for each member is NOT used for (de)serialization, it is more or less the return type for getters and the input type for setters for methods to set the members.

For anything else that cannot use just the width for each member, the position and mask have been included. These allow you to retrieve the value for the member from the ushort directly.

Getter

(flags & MASK) >> POS

Setter

flags = (flags & ~MASK) | (value << POS)

Where flags is the entire ushort, and value is the value of the particular field.

<add>

Remove: userver and userver2

These two attributes had minimal remaining use. I've already switched their use to the format vercond="User Version == ...". Also, userver2 was never technically formalized afaict. It was not supported in every project such as niflib, where nifxml.py/gen_niflib.py seem to have no support for it.

Remove: arr3

There is a single legacy undecoded object using arr3 for some reason, and it is otherwise undocumented and unsupported by all programs. It only seems to exist due to lack of arithmetic in the arr expr, which exists now. So it is being removed in lieu of that.

Rename: ver1 and ver2

For block versioning, I have adopted since and until and this is succinct and human readable. The nice would be the same for <add>. However there is also alternatives like minver and maxver. But ver "1" and ver "2" inherently mean nothing.

Replace/Remove: calculated

I've talked about this attribute a lot and still am completely in the dark about its intent. If it does what I think it's intended for, it should be on way more fields than the 3 that it is present on.

For most things, there is a simple two-way boolean relationship for example:

<add name="Has Triangles" type="bool" calculated="1" />
<add name="Triangles" type="Triangle" arr1="Num Triangles" cond="Has Triangles" />

I don't really understand the need for calculated here. At write time, if the Triangles array has a length, you know that you must set Has Triangles to true. If the Triangles array has no length, it doesn't matter whether or not Has Triangles is set to true, unless for some reason the program has erroneously set a Num Triangles but doesn't have any Triangles in the array.

Where it's actually needed is in examples like this:

<add name="Vertex Desc" type="BSVertexDesc" />
<add name="Num Triangles" type="ushort" />
<add name="Num Vertices" type="ushort" />
<add name="Data Size" type="uint" calculate="(Num Vertices * (Vertex Desc &amp; 0xF) * 4) + (Num Triangles * 6)" />
<!-- -->
<add name="Particle Data Size" type="uint" calculate="(Num Vertices * 6) + (Num Triangles * 3)" />

You can calculate the Data Size values from the content after them, but in this case, the data before Data Size has to be in agreement as well.

Except... either size can also be 0 even if Vertex Desc, Num Triangles, and Num Vertices are all non-zero. So, really the calculate field would only be a guideline on how to get the correct value, not a rule that is always followed.

Replace abstract

NiDataStream has two members which do not contribute to serialization. This is done because NiDataStream RTTI string has two arguments packed at the end so they are effectively the first two members of the NiObject, just packed with the name instead of the block.

The name abstract is confusing for many reasons, but it's simply factually incorrect in OOP, which the XML is emulating.

As for alternatives, hkxcmd uses flags='SERIALIZE_IGNORED' for example, but it also has a lot of flags for its class members. I also think using an attribute would be kind of hard to see and a separate tag name would also enforce treating them differently so that there is no mistaking that the data need not be serialized.

So, the obvious choices would be to have tags named <property>/<prop> or <metadata>/<meta>.

For existing parsers or codegen, this has the benefit of not confounding members that serialize vs members that don't, as they simply will not see the new tag. Right now, anything that doesn't pay attention to abstract on members will erroneously try to read/write the values from disk.

Replace cond="T" and cond="!T"

Several instances of cond involve checking against the type of NiObject the member is being included in such as

<niobject name="NiExtraData" inherit="NiObject">
    <add name="Name" type="string" cond="!BSExtraData">
    <!-- -->
</niobject>

or for NiGeometry

<add name="Bound" type="NiBound" vercond="(User Version 2 &gt;= 100)" cond="NiParticleSystem" />
<add name="Skin" type="Ref" template="NiObject" vercond="(User Version 2 &gt;= 100)" cond="NiParticleSystem" />
<!-- -->
<add name="Material Data" type="MaterialData" vercond="(User Version 2 &lt; 100)" />
<add name="Material Data" type="MaterialData" vercond="(User Version 2 &gt;= 100)" cond="!NiParticleSystem" />

Mostly in order to fix inheritance issues caused by historical changes to class hierarchies, or in Bethesda's case, entire class replacement (NiGeometry->BSGeometry). Since we have no mechanism for multiple niobject definition with inheritance changes, we settle for customizing the content of the parent niobject based on the child type.

This mechanism is not an expression though, and as such does not belong inside of cond. The names of NiObject types should not appear in cond as they are not local information.

The replacement would be:

<niobject name="NiExtraData" inherit="NiObject">
    <add name="Name" type="string" excludeT="BSExtraData">
    <!-- -->
</niobject>

or for NiGeometry

<!-- BSGeometry: Used by Bethesda Stream 100+ NiParticleSystem -->
<add name="Bound" type="NiBound" vercond="(User Version 2 &gt;= 100)" onlyT="NiParticleSystem" />
<add name="Skin" type="Ref" template="NiObject" vercond="(User Version 2 &gt;= 100)" onlyT="NiParticleSystem" />
<!-- -->
<add name="Material Data" type="MaterialData" vercond="(User Version 2 &lt; 100)" />
<add name="Material Data" type="MaterialData" vercond="(User Version 2 &gt;= 100)" excludeT="NiParticleSystem" />

Two separate attributes will set it apart from normal cond, which will no longer have to be parsed to see whether or not any part of the expression is an NiObject and whether there is a unary not (!).

For existing applications, if rewriting logic is undesirable, excludeT and onlyT can simply be aliases for cond="!T" and cond="T" respectively.

<basic>

Rename count

The function of this initially eluded us until I saw the connection between the types that have it, and then eventually noticed that the language in the docs generator supported this. Basically, it means "integral", but should mean "countable". It doesn't technically mean countable because the int basic type is set as countable as well, even though signed ints should not be used as counts.

The phrase "count" is also too close in meaning to "size" or "length", which may also be introduced as an attribute for basic/compound types, and the use of 0/` instead of false/true also further confuses the intent.

Additions

<basic> and <compound>

int64 and uint64 types

A few types in the XML are really bitfields with a 64-bit storage type, and at least one field is potentially a 64-bit hash. So the addition of 64-bit types is necessary.

size

The basic and basic-ish compound types have no information on their size for read/write. Objects that are always a fixed size should declare their size in the XML.

casts_to / convertible

The other types that it is acceptable to be cast to (where information is not lost), e.g. short->int or ushort->uint. This is necessary for deducing if version-exclusive duplicate names of differing types are true duplicates or not. Or if the members are in an incorrect order, e.g. for ushort->uint, the uint has to be listed first.

It also is useful for defining what compound types must have a conversion function available, such as HalfVector3 -> Vector3.

For example:

<add name="Num Triangles" type="uint" vercond="User Version 2 == 130" />
<add name="Num Triangles" type="ushort" vercond="User Version 2 &lt; 130" />

These do not need to be treated as different values for codegen or anything else because a ushort can be held inside of a uint. However if the members were in reverse order, the uint could not be fit into the ushort without potential information loss.

For Python, this doesn't really matter much, as there is no real size limit to integers, but it does affect the logic during (de)serialize. It will also aid in linting.

Example of usage:

<basic name="byte" countable="true" casts_to="ushort uint">
<basic name="char" countable="false" casts_to="short int">

The opposite direction of this could also be used instead

<basic name="uint" countable="true" casts_from="ushort byte">
<basic name="int" countable="false" casts_from="short char">

This would basically be a list of all the types that can fit inside it.

If the basic types also had their size attribute, this might not even be necessary.

Split countable into integral, boolean, and countable

Currently the signed integer values are marked as countable, but really shouldn't be. You can't have a negative value for an array length. The type is still integral. Furthermore, it's not boolean. The only types that should be considered boolean are byte and bool. Boolean types should be the only types that can toggle the presence of a sibling member. Adding integral and boolean will help sort out typing issues during linting for array and cond references.

In Summary

  • Integral is not necessary countable
  • Signed integers should not be countable (used for array sizes)
  • Integral == False implies countable and boolean are false as well.
  • Boolean == True should be the only way a member can toggle the presence of another member.

<niobject>

Defaults of inherited values

For each niobject, there can be only one default for a member, in the niobject that it is declared on. However, several Gamebryo classes modify inherited defaults for each class type. Some examples:

  • NiPSysModifier\Order: Specific subclasses of NiPSysModifier always have the same Order
  • NiAVObject\Flags: Subclasses of NiAVObject tend to have different Flags defaults, such as NiNode vs BSFadeNode vs BSBlastNode/BSDamageStage.

So to provide proper defaults for subclasses, a syntax is necessary for overriding default values of inherited members.

Using a Pythonic dictionary syntax:

<niobject name="NiPSysModifier" abstract="true" inherit="NiObject">
    Abstract base class for all particle system modifiers.
    <!-- -->
    <add name="Order" type="uint">Modifier ID in the particle modifier chain</add>
    <!-- -->
</niobject>

<niobject name="NiPSysEmitter" abstract="true" inherit="NiPSysModifier" defaults="{'Order': 1000}">
    <!-- -->
</niobject>

<niobject name="NiPSysDragModifier" inherit="NiPSysModifier" defaults="{'Order': 4000}">
    <!-- -->
</niobject>

Downside: Other than Python using literal eval, this syntax would have to be specially parsed. A native XML syntax could be used, but it would be more verbose and introduce more tag names.

Example:

<niobject name="NiPSysModifier" abstract="true" inherit="NiObject">
    Abstract base class for all particle system modifiers.
    <!-- -->
    <add name="Order" type="uint">Modifier ID in the particle modifier chain</add>
    <!-- -->
</niobject>

<niobject name="NiPSysEmitter" abstract="true" inherit="NiPSysModifier">
    <default name="Order" value="1000" />
    <!-- -->
</niobject>

<niobject name="NiPSysDragModifier" inherit="NiPSysModifier">
    <default name="Order" value="4000" />
    <!-- -->
</niobject>

Downside: NifSkope currently actually errors if anything other than add exists inside of niobject etc. so simply adding a new child tag would not be ignored. This is a minor downside, and can quickly be fixed, but just pointing out that new tags are not necessarily ignored by default.

Organizationally, a heterogeneous mix of child tags is a bit odd for nif.xml but not XML in general. The default metadata could also be supplied alongside each subclass, or in the parent itself.

In the parent:

<niobject name="NiPSysModifier" abstract="true" inherit="NiObject">
    Abstract base class for all particle system modifiers.
    <!-- -->
    <add name="Order" type="uint">
        Modifier ID in the particle modifier chain
        <default type="NiPSysEmitter" value="1000" />
        <default type="NiPSysDragModifier" value="4000" />
        <!-- -->
    </add>
    <!-- -->
</niobject>

Upside: This centralizes the info and it's not strewn across the subclasses.
Downside: This info is not apparent when looking at a subclass. 🤕

Note: For this particular member, there may be more than a dozen actual defaults listed.. not just the two shown in the examples.

Lastly, I mentioned that it's technically metadata i.e. not directly vital to (de)serialization which is true, but it is still vital to data correctness. That is why the last option of putting the info alongside the niobjects instead of inside is the least appealing to me. It would essentially be a type-name-value dictionary in XML so I won't bother with an example.

<add>

range (numeric limits)

As of writing this, I use calc in a few locations to limit the final size of a Num value. This is fine, but in the general case, some values need to be enforced to a very limited range of their full storage size. In some cases (such as Num values), the only limitation is that the value must be at least 1. Sometimes it is an upper limit. I feel that having an attribute for both the lower and upper limit is excessive and instead the Python range syntax can be borrowed.

Right now, I've begun marking some of these fields as default="1" because there should always be at least one, but this isn't actually an enforcement.

<!-- Min+Max -->
<add name="Num A" type="uint" range="1:8" default="1" />
<!-- No Max -->
<add name="Num B" type="uint" range="1:" default="1" />
<!-- No Min -->
<add name="Num C" type="uint" range=":255" />

For the first two, default="1" is technically redundant. Since the default for a uint is 0, then any min of non-zero automatically becomes the default. However, existing systems already rely on the default attribute for member initialization, and the number of fields that will have default="1" range="1:" should not be too high. This opinion may change though.

To not write both, any non-zero min of range="X:Y" would have to imply default="X" and any parser would have to derive default="X" on its own.

const (immutable values)

Some class members should be set at creation time and be immutable, i.e. not changeable by a user or any other means. For example type info that is tightly coupled with class type.

A default value is obviously required when const="true".

<enum> and <bitflags>

Bit Patterns in bitflags

Several bitflags have default attributes set with plain integral values, and this doesn't allow anyone to easily see which bitflags options these are. For example:

<add name="Shader Flags 1" ... default="2185233153" />
<add name="Shader Flags 2" ... default="32801" />
<add name="Shader Flags 1" ... default="2151678465" />
<add name="Shader Flags 2" ... default="1" />

Default fields don't support expressions, and so a token with the various options BITORed together would not help. It could instead be a feature of the option:

<option value="0|8|9|22|25|31" name="SLSF1_DEFAULT" />

<add name="Shader Flags 1" ... default="SLSF1_DEFAULT" />

Or so. Each value would get split on | and then shifted and BITORed. This way, using actual enumeration values as defaults could be enforced for bitflags as well.

Clarifications

Some things remain unclear about nif.xml and need to be stated explicitly

<add>

default usage for arrays

Nowhere is default used alongside arr1. However there are some arrays where a default should be provided. The default would apply to any members added to the array of that type.

Example:

<add name="Vertex Colors" type="Color4" default="#VEC4_ONE#" arr1="Num Vertices" />

The default color for the Vertex Colors array should be 1.0, 1.0, 1.0, 1.0.

For Discussion

Like other tickets, this isn't completely finished and I may be adding more as time goes on. I will be making commits for this ticket fairly immediately however.

Checklist

  • Replace ver with equivalent ver1/ver2 statement
  • Remove userver/userver2
  • Remove arr3
  • Replace cond="T" and cond="!T" with onlyT and excludeT
  • Add size to appropriate basic and compound
  • Replace TEMPLATE and ARG
  • Add int64 and uint64
  • Split countable into integral/boolean/countable.
  • Add convertible for types that can be cast to a larger type without information loss.
  • 'Flags' basic type replacement with bitfield tag.
  • Replace calculated
  • Replace abstract for <add> e.g. NiDataStream "Usage" and "Access".
  • Any boolean attributes using 1/0 as values should be changed to use true/false for clarity.
  • Bit patterns in bitflags
  • niobject inherited value defaults
  • range attribute
  • const attribute
  • default for array values where applicable.

NiPhysXScene not supported by nifxml

@niftools/blender-niftools-addon-reviewer -

Before creating a new issue, ensure that

  • Read the user documentation.
  • Check that the issue hasn't already been reported.
  • Read through the list of Helpful resources links on the right-hand side.
  • Talk with the developers to ensure its a valid issue

The issue will get closed off immediately as invalid if

  • Looking for support where the forums, chat or documentation is more appropriate.
  • Goes off topic, loses focus or turns into a discussion.
  • Removing parts of the template the issue.

Fill out the template below to the best of your ability, including .blend files, nifs, logs; help us to help you.

Issue Overview

I'm trying to import a .nif file, but with every .nif file it returns that message :

Traceback (most recent call last):
  File "C:\Users\User\AppData\Roaming\Blender Foundation\Blender\2.81\scripts\addons\io_scene_nif\dependencies\pyffi\formats\nif\__init__.py", line 1375, in read
    block = getattr(NifFormat, block_type)()
AttributeError: type object 'NifFormat' has no attribute 'NiPhysXScene'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\User\AppData\Roaming\Blender Foundation\Blender\2.81\scripts\addons\io_scene_niftools\operators\nif_import_op.py", line 125, in execute
    return nif_import.NifImport(self, context).execute()
  File "C:\Users\User\AppData\Roaming\Blender Foundation\Blender\2.81\scripts\addons\io_scene_niftools\nif_import.py", line 73, in execute
    self.load_files()  # needs to be first to provide version info.
  File "C:\Users\User\AppData\Roaming\Blender Foundation\Blender\2.81\scripts\addons\io_scene_niftools\nif_import.py", line 140, in load_files
    NifData.init(NifFile.load_nif(NifOp.props.filepath))
  File "C:\Users\User\AppData\Roaming\Blender Foundation\Blender\2.81\scripts\addons\io_scene_niftools\file_io\nif.py", line 65, in load_nif
    data.read(nif_stream)
  File "C:\Users\User\AppData\Roaming\Blender Foundation\Blender\2.81\scripts\addons\io_scene_nif\dependencies\pyffi\formats\nif\__init__.py", line 1378, in read
    "Unknown block type '%s'." % block_type)
ValueError: Unknown block type 'NiPhysXScene'.

location: <unknown location>:-1

Version Information

Blender Niftools Addon Version Info

2.6.0.dev4

Blender Version Info

2.8.1

Platform information

Windows 10

Steps to Reproduce

1- Import a .nif file
2- Get an error message.

Expected Result

I wanted to Blender to open the file.

Actual Result

I'm getting this message :

bpy.context.space_data.system_bookmarks_active = 1
Dev: Sys variable not set
Executing - Niftools : Blender Niftools Addon v0.0.2(running on Blender 2.81 (sub 16), PyFFI 2.2.4.dev4)
Importing C:\Users\User\Desktop\todo\natere_tree3001_01.nif
NIF file version: 14020008
Reading file
Traceback (most recent call last):
  File "C:\Users\User\AppData\Roaming\Blender Foundation\Blender\2.81\scripts\addons\io_scene_nif\dependencies\pyffi\formats\nif\__init__.py", line 1375, in read
    block = getattr(NifFormat, block_type)()
AttributeError: type object 'NifFormat' has no attribute 'NiPhysXScene'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\User\AppData\Roaming\Blender Foundation\Blender\2.81\scripts\addons\io_scene_niftools\operators\nif_import_op.py", line 125, in execute
    return nif_import.NifImport(self, context).execute()
  File "C:\Users\User\AppData\Roaming\Blender Foundation\Blender\2.81\scripts\addons\io_scene_niftools\nif_import.py", line 73, in execute
    self.load_files()  # needs to be first to provide version info.
  File "C:\Users\User\AppData\Roaming\Blender Foundation\Blender\2.81\scripts\addons\io_scene_niftools\nif_import.py", line 140, in load_files
    NifData.init(NifFile.load_nif(NifOp.props.filepath))
  File "C:\Users\User\AppData\Roaming\Blender Foundation\Blender\2.81\scripts\addons\io_scene_niftools\file_io\nif.py", line 65, in load_nif
    data.read(nif_stream)
  File "C:\Users\User\AppData\Roaming\Blender Foundation\Blender\2.81\scripts\addons\io_scene_nif\dependencies\pyffi\formats\nif\__init__.py", line 1378, in read
    "Unknown block type '%s'." % block_type)
ValueError: Unknown block type 'NiPhysXScene'.

location: <unknown location>:-1

Screenshot

Screenshot

Nif File

Nif file

Additional Information

I'm using :

  • Python 3.9

BSEffectShaderProperty - Texture Names

(Edit: Forgot this part was included in #41)
It seems that nearly all values for this are 65283 (0xFF03). I'd like to find examples that are not though.

I'm not sure if the FF part is really part of the texture clamp mode, since the 03 part matches up with TexClampMode type in nif.xml. WRAP_S_WRAP_T (3) is the most common value.

However the storage type of TexClampMode is uint so it is meant to take up that many bytes and therefore it can't be used with BSESP.

There is a small chance the extra data has to do with the second texture. I have just figured out that the "Greyscale Texture" is actually set to mirrored repeat. It has to be with the way the texture is used as a LUT, or when the sampler gets values near 0.0 or 1.0 there are artifacts.


Speaking of, I feel the names for the textures are wrong. "Greyscale Texture" is actually what the "Source Texture" should be called. The color in the "Source Texture" doesn't actually matter as only the green channel is taken into account. I've tested and confirmed this. So, generally, the "Source Texture" is meant to be greyscale.

The "Greyscale Texture" is best called a Palette Texture, or Lookup Texture. The reason is because it is used as a LUT (look up table) given the "Source" texture as input. This is only if using SLSF1_Greyscale_To_PaletteColor/SLSF1_Greyscale_To_PaletteAlpha. Note the name... Greyscale to Palette.

Refraction Strength

Reported by - Renegade1979

"After looking at the _byoh\clutter\resources\glass.nif mesh from Skyrim's Hearthfire DLC I've realized that Unknown Float 2 in BSLightingShaderProperty is used for setting the strength of the refraction applied to a NiTriShape via SLSF1_Refraction.
We can add SLSF1_Fire_Refraction and SLSF1_Temp_Refraction to it, so with this unknown value value set to 0 refraction doesn't work.

Tons of modders gotta know about that :D Also it's possible to duplicate a transparent branch then add refraction shader to it."

@ttl269 or @skyfox69 can you look into this, I can provide files mentioned.

Not too sure about the "duplicate transparent branch" comment, possibly just a workflow thing.

Add support for Epic Mickey-specific blocks

The following blocks are not yet present in nif xml, and are found in Epic Mickey nifs:

  • NonUniformParticleSystem
  • NonUniformSimulatorGeneralStep
  • NonUniformQuadGenerator

It also appears that JPSJigsawNode can have more data than a regular NiNode has.

Non-header nif

i stumbled a new versionless nif files from game Twin Saga / Astral Realm. in this case it doesn't seems to be the same as niftools/nifskope#187

since the aura kingdom versionless 20.3.1.2 header were

Gamebryo File Format\x0A\x02\x01\x03\x14\x01

while this one is

Gamebryo File Format\x0A\x01\x02\x03\x14\x01

Gamebryo File Format\x0A\x02\x02\x03\x14\x01

NIF.zip

FO3/OB Regressions

Originally discussed here: #51 (comment)

The latest XML gives the following errors when scanning all FO3 meshes:

https://gist.github.com/jonwd7/390c5d88b3f64766e8f0dc301fc1f7a9

There appear to be 3 separate regressions.

Issue 1 (Commit bbd0d5e)

meshes/architecture/megaton/megatonrampturn45sml.nif
meshes/architecture/megaton/megatonrampturn90sml.nif
meshes/armor/raiderarmor01/m/glovel.nif
meshes/characters/head/headfemalefacegen.nif
meshes/dungeons/office/rubblepiles/offrubblechunka03.nif
meshes/dungeons/office/rubblepiles/offrubblechunka01.nif
meshes/dungeons/office/rubblepiles/offrubblechunka06.nif
meshes/dungeons/office/rubblepiles/offrubblechunka02.nif
meshes/dungeons/rivetcity/exterior/rcexbridge.nif
meshes/dungeons/rivetcity/exterior/rcextaircarrierback.nif
meshes/dungeons/rivetcity/exterior/rcexaircarrierfront.nif
meshes/dungeons/rivetcity/exterior/rcaircarriertower.nif
meshes/dungeons/rivetcity/exterior/rcextaircarrier01.nif

Error Text Sample:

"device position incorrect after block number 4 (NiTriStripsData) at 0x76f ended at 0x2490 (expected 0x9bf)"
...
"failed to load file footer"

Issue 2 (Commit bbd0d5e)

meshes/dungeons/office/rubblepiles/offrubblechunka04.nif
meshes/dungeons/office/rubblepiles/offrubblechunka05.nif

Error Text Sample:

"device position incorrect after block number 4 (NiTriStripsData) at 0x80f ended at 0xcaf (expected 0xba7)"

Issue 3 (Commit b14b409)

meshes/triggers/trigplayerbox01.nif 
meshes/triggers/trigplayerbox03.nif 
meshes/triggers/trigplayerbox02.nif 
meshes/triggers/trigplayerbox04.nif 
meshes/triggers/trigplayerwall01.nif
meshes/triggers/trigplayerwall02.nif
meshes/triggers/trigplayerwall04.nif
meshes/triggers/trigplayerwall03.nif

Error Text Sample:

"device position incorrect after block number 3 (bhkBoxShape) at 0x3b4 ended at 0x3d0 (expected 0x3d4)"
"device position incorrect after block number 4 (bhkTransformShape) at 0x3d4 ended at 0x424 (expected 0x428)"
"device position incorrect after block number 5 (bhkSimpleShapePhantom) at 0x428 ended at 0x48b (expected 0x48c)"

Issue 4

D:/Oblivion/Data/meshes/meshes/marker_arrow.nif (4.0.0.2)
"next block does not start with a NiString"
D:/Oblivion/Data/meshes/meshes/marker_divine.nif (4.0.0.2)
"next block does not start with a NiString"
D:/Oblivion/Data/meshes/meshes/marker_radius.nif (3.3.0.13)
"next block does not start with a NiString"
D:/Oblivion/Data/meshes/meshes/marker_temple.nif (4.0.0.2)
"next block does not start with a NiString"
D:/Oblivion/Data/meshes/meshes/marker_travel.nif (4.0.0.2)
"next block does not start with a NiString"

[Spec] Token definitions

Tokens could be defined directly in nif.xml which would help with simplification of writing and parsing expressions or composing strings in various attributes, while improving readability and adding context.

Right now the logical and arithmetic operators are used in nif.xml without any description. Also, various expressions used in vercond can be very complex to write, and harder to decipher. They can also be repeatedly used, in the case of custom versions (Bethesda and others). Tokens defined by the XML itself would give these expressions context and readability. Tokenizing the expressions with repeated usage would increase maintainability as the expression string would be in only one location.

Tokenizing operators would also alleviate issues with parsing, reading, and writing (typing) XML entities such as &amp; &gt; and &lt;. It would also remove ambiguity between &/&& and |/|| which is currently an issue that parsers have to deal with specially.

Example:

<operator token="#ADD#" string="+" />
<operator token="#SUB#" string="-" />
<operator token="#MUL#" string="*" />
<operator token="#DIV#" string="/" />
<operator token="#AND#" string="&amp;&amp;" />
<operator token="#OR#" string="||" />
<operator token="#LT#" string="&lt;" />
<operator token="#GT#" string="&gt;" />
<operator token="#LTE#" string="&lt;=" />
<operator token="#GTE#" string="&gt;=" />
<operator token="#EQ#" string="==" />
<operator token="#NEQ#" string="!=" />
<operator token="#BITAND#" string="&amp;" />
<operator token="#BITOR#" string="|" />

<!-- Note: The strings for expression tokens may need to use the operator tokens. -->
<expression token="#BS202#" string="((Version == 20.2.0.7) &amp;&amp; (User Version 2 &gt; 0))" />
<expression token="#BSSTREAM#" string="(User Version 2 &gt; 0)" />
<expression token="#NISTREAM#" string="(User Version 2 == 0)" />
<expression token="#DIVINITY2#" string="((User Version == 0x20000) || (User Version == 0x30000))" />
<expression token="#SSE#" string="(User Version 2 == 100)" />
<expression token="#FO4#" string="(User Version 2 == 130)" />

Parsers would have two main ways of dealing with tokens, as first-class entities (ignoring the string attr and dealing with the tokens directly), or as second-class entities (using string contents to replace the token and dealing with the strings like before).

Operators are best dealt with as first-class, otherwise you eliminate the benefits of tokens for both regex and non-regex parsing. Other token types might be best dealt with as basic string replacement.

Other potential usages:

Commonly used default values

<default token="#FLT_MAX#" string="3.402823466e+38" />
<default token="#FLT_MIN#" string="-3.402823466e+38" />
<default token="#INV_FLT#" string="-3.402823466e+38" />
<default token="#INV_VEC3#" string="-3.402823466e+38, -3.402823466e+38, -3.402823466e+38" />
<default token="#INV_VEC4#" string="-3.402823466e+38, -3.402823466e+38, -3.402823466e+38, -3.402823466e+38" />

<!-- BEFORE -->
<niobject name="NiConstColorEvaluator" inherit="NiEvaluator">
    <add name="Value" type="Color4" default="-3.402823466e+38, -3.402823466e+38, -3.402823466e+38, -3.402823466e+38" />
</niobject>

<niobject name="NiConstFloatEvaluator" inherit="NiEvaluator">
    <add name="Value" type="float" default="-3.402823466e+38" />
</niobject>

<niobject name="NiConstPoint3Evaluator" inherit="NiEvaluator">
    <add name="Value" type="Vector3" default="-3.402823466e+38, -3.402823466e+38, -3.402823466e+38" />
</niobject>

<niobject name="NiConstQuaternionEvaluator" inherit="NiEvaluator">
    <add name="Value" type="Quaternion" default="-3.402823466e+38, -3.402823466e+38, -3.402823466e+38, -3.402823466e+38" />
</niobject>

<niobject name="NiBSplineEvaluator" inherit="NiEvaluator">
    <add name="Start Time" type="float" default="3.402823466e+38" />
    <add name="End Time" type="float" default="-3.402823466e+38" />
    <add name="Data" type="Ref" template="NiBSplineData" />
    <add name="Basis Data" type="Ref" template="NiBSplineBasisData" />
</niobject>

<!-- AFTER -->
<niobject name="NiConstColorEvaluator" inherit="NiEvaluator">
    <add name="Value" type="Color4" default="#INV_VEC4#" />
</niobject>

<niobject name="NiConstFloatEvaluator" inherit="NiEvaluator">
    <add name="Value" type="float" default="#INV_FLT#" />
</niobject>

<niobject name="NiConstPoint3Evaluator" inherit="NiEvaluator">
    <add name="Value" type="Vector3" default="#INV_VEC3#" />
</niobject>

<niobject name="NiConstQuaternionEvaluator" inherit="NiEvaluator">
    <add name="Value" type="Quaternion" default="#INV_VEC4#" />
</niobject>

<niobject name="NiBSplineEvaluator" inherit="NiEvaluator">
    <add name="Start Time" type="float" default="#FLT_MAX#" />
    <add name="End Time" type="float" default="#FLT_MIN#" />
    <add name="Data" type="Ref" template="NiBSplineData" />
    <add name="Basis Data" type="Ref" template="NiBSplineBasisData" />
</niobject>

Note: #FLT_MIN# and #INV_FLT# have the same string value but provide different context for the usage of the value. One is the negation of #FLT_MAX# regarding start/end time in a sequence, and the other is denoting an invalid uninitialized value.

Real world example: These defaults caused a compiler error in niflib when I accidentally included a trailing comma in one of them. This would not have happened if I had been using tokens.

Forthcoming block versioning (will get separate ticket)

<!-- Note: Space-separated is the correct way to make lists in XML -->
<!-- Note: VXX_X_X_X is a unique ID for <version> being added to the spec, will receive ticket. -->
<versionset token="#BETHESDA#" string="V10_0_1_2 V10_1_0_101 V10_1_0_106 V10_2_0_0__10 V20_0_0_4__10 V20_0_0_4__11 V20_0_0_5_OBL V20_2_0_7__11_1 V20_2_0_7__11_2 V20_2_0_7__11_3 V20_2_0_7__11_4 V20_2_0_7__11_5 V20_2_0_7__11_6 V20_2_0_7__11_7 V20_2_0_7__11_8 V20_2_0_7_FO3 V20_2_0_7_SKY V20_2_0_7_SSE V20_2_0_7_FO4" />
<versionset token="#FO3#" string="V20_0_0_4__11 V20_2_0_7__11_1 V20_2_0_7__11_2 V20_2_0_7__11_3 V20_2_0_7__11_4 V20_2_0_7__11_5 V20_2_0_7__11_6 V20_2_0_7__11_7 V20_2_0_7__11_8 V20_2_0_7_FO3" />
<versionset token="#SSE#" string="V20_2_0_7_SSE" />
<versionset token="#FO4#" string="V20_2_0_7_FO4" />

<niobject name="bhkRefObject" versions="#BETHESDA#">
<niobject name="bhkSerializable" versions="#BETHESDA#">
<niobject name="bhkWorldObject" versions="#BETHESDA#">
<niobject name="bhkEntity" versions="#BETHESDA#">

<niobject name="bhkPoseArray" versions="#FO3#" />
<niobject name="bhkPhysicsSystem" versions="#FO4#" />

In brief, since it will be discussed in its own ticket, block versioning being reintroduced has run into issues with the granularity of since/until in that for custom blocks it is not good enough and would require vercond expressions, which should be avoided at all costs. Instead each version can be listed (<version> now being every distinct format including user and Bethesda versions) but this gets extremely repetitious and needs a system of abbreviation.

For Discussion

Delimiter

Is # the best delimiter? There is also @. Neither have any uses in logic/arithmetic or occur commonly in the strings we use in nif.xml. Especially not an issue since the delimiter goes at each end, which I think is required to eliminate any token containing another e.g. #LT and #LTE collide.

Organization

Do we put all token elements nested under a <tokens>? Or do we leave them flat alongside <version>, <basic>, etc. I feel grouping them is important as it makes sure an XML parser can grab every token at once without needing to know tag names.

Element tag names

Do we use multiple tag names to specify/limit the token usage, i.e. contexts?

Contexts

Separate tag name and contexts would require separate maps/dictionaries on the parser side. There are already 4 example contexts (operator, expression, default, versionset). Currently the example specification does not include any built-in way of specifying what tags and attributes a token's context is limited to, so the association with tag name -> attributes to parse using it would have to be manual.

Reusing token identifiers in differing contexts

Note that I use #FO4# and #SSE# in both <versionset> and <expression>. Given that these tags are used in completely different domains, is this OK? Reuse or not, contexts means that technically a parser can no longer do a naive search/replace on the entire file in memory before reading the XML in. Because regardless of identifier collisions, a search/replace violates the limitation on association of a token tag to a specific element attribute.

Token string attributes

Expression token strings will likely need to use the operator tokens, because first-class token parsers would no longer know about &amp;&amp;, et al.

Also if a parser is treating tokens as second-class, this means that order matters as all <operator> tags need to be read in first. Then as each <expression> tag is read in, the parser will replace the tokens with the operator strings.

[Spec] Module organization for blocks

Adding category information to types can help with organization of code gen, docs gen, or filtering/categorizing types inside of a UI. Without this info in nif.xml it has to be associated manually by the library or program.

The vanilla Gamebryo engine is split up into libs/modules such as:

NiAnimation
NiCollision
NiMain
NiMesh (20.5+)
NiParticle
NiPhysX (20.2+)
NiPortal

While I do not believe we need to perfectly replicate their organization, I think it is a good basis. NiMain could probably be split up into a few smaller logical sections.

Historically, there are actually two different NiParticle modules. In 20.5 everything NiParticleSystem/NiPSys related was deprecated and replaced by NiPSParticleSystem/NiPS. So NiParticle needs to organizationally become two modules e.g. NiParticle and NiParticle 20.5+. Making all new particle classes was actually due to the replacement of NiGeometry with NiMesh, because NiParticles' parent is NiGeometry and NiParticles is the top level of the particle system.

Given this history and since there is an NiMesh module, there can probably be an NiGeometry module too. Other potential sub-categories of NiMain are NiProperty and NiExtraData.

There is also the question of custom blocks. Either each game should get a category or it should be Bethesda and "Other". In addition, Bethesda's custom blocks are extensive enough that it could probably be split up into its own categories. The most egregious example of their custom blocks being bhk.

Proposed Categories

# Vanilla
NiAnimation
NiMain
    NiGeometry
    NiExtraData
    NiProperty
NiMesh
NiParticle 
NiParticle (20.5+)
NiPhysX
# Custom (Bethesda)
BSMain
BSHavok
BSShader
BSAnimation
# Custom (Other)
NiCustom
# NetImmerse (pre-10.1)
NiLegacy

Example

<module name="NiMain" priority="0" />
<!-- ... -->
<module name="BSMain" custom="true" priority="5000" />
<module name="NiCustom" custom="true" priority="9000" />
<module name="NiLegacy" deprecated="true" priority="10000" />

<niobject name="NiAVObject" abstract="1" inherit="NiObjectNET" module="NiMain">
<niobject name="NiRawImageData" inherit="NiObject" module="NiLegacy">
<niobject name="NiBezierMesh" inherit="NiAVObject" module="NiLegacy">
<niobject name="NiClod" inherit="NiTriBasedGeom" module="NiCustom">
<niobject name="NiFurSpringController" inherit="NiTimeController" module="NiCustom">
<niobject name="BSFadeNode" inherit="NiNode" module="BSMain">

With the module attribute you can immediately tell if something is old or custom, etc. Non-Bethesda custom blocks have a tendency to use the Ni terminology and so they blend in well with vanilla types.

Also for NiAnimation and NiParticle, there are major organizational benefits as there are so many blocks and yet none are innately defined as being animation or particle related. In NifSkope, I resorted to complicated rules on the names and predefined maps of top-most parents.

Module Tag

Deprecated status: deprecated = <boolean>

There are a lot of NetImmerse blocks that are hardly functioning at the moment. This mostly applies to versions before 4.0.0.0, and at least Morrowind (4.0.0.2) is exempt, but everything else before 10.1 is a crapshoot.. Anything that is old and still not decoded should go into NiLegacy and the module should be marked as deprecated.

This allows a lib/program to discard these old and likely non-functioning blocks without resorting to discarding everything before a certain version.

Custom flag: custom = <boolean>

This is a no-brainer; it allows entire groups of blocks to be marked as non-standard without having to know each and every one by heart or having to keep a list manually. Like deprecated, it can also be leveraged during code or docs generation to various effect.

Implicit or Explicit ordering: priority | order = <integer>

At first I figured that the order in which it's listed in the XML should be the implicit priority of the modules. The ordering would be useful for the main page of the docs, or for actually reading and re-writing the XML for various reasons and keeping the organization the same (for version control reasons).

However, if say the XML were to be deserialized in order to be updated/sanitized/formatted/etc then at least the modules should have their order explicitly defined. The only real way to order them automatically would be alphabetically, which would put Bethesda stuff at the top of the file.

Opinion: Go ahead and explicitly order the modules so that their declaration order is not important.

For Discussion

Ultimately, the end goal is to have a tool which will reorganize the XML, initially having one horrendous reorganization commit, and then after that the tool will keep the XML consistently formatted for us. No blocks strewn about the file that belong together.

However the module organization will benefit code and docs generation as well. The main page of the XML docs is currently alphabetical in both hierarchy and list, which puts important blocks much further down, and the list view is nigh unusable with no categorization.

For interpreters of the XML, like NifSkope and PyFFI, the categorization still has uses for filtering and UI.

Support Atlantica

Migrated from sf.net

Atlantica Online seems to have updated SOME of their files. Some .NIF files are now 20.6.0.0 and .KFM are 20.6.0.0b.
On opening a .KFM, NifSkope gives the following error:
""version" "20.6.0.b" "not supported yet""
""failed to load kfm file (537264139)""

NifSkope was, however, (or at least appeared to) load the .NIF files, although Blender gave the error: "ValueError: Corrupted nif file: invalid Ndoors version."
When I examined the file I found the listed version was actually 20.6.0.0b (with a zero before the \'b\', if that makes any difference). When I manually added 20.6.0.0b to the kfm.xml file, and tried to load the .KFM file with NifSkope, I got the message:
"array "Transitions" much too large"
""failed to load kfm file (537264139)""
Blender was completely unable to parse the .KFM file and gave the message: "ValueError: Not a nif file."

Attached is a 7zip of the files causing the error. These are located (along with one or two other files) in C:\nDoors\Atlantica\NChar3D\Char\GUNNER1 for anyone with the Atlantica client already installed.
I may likely try to create my own patch adding support for the new files, but I will need to study up on the .NIF/.KFM format layout first, as I have rather little experience with these files at the binary level.

Reference nif - https://www.dropbox.com/sh/7bbf50pn8ghqu1y/AAANeF05v6apAlZq0Vr0hUkKa?dl=0

nifdoc.py don't generating htmls correctly

Hi
The nifdoc.py script is generating the html files but it doesn't show the specs as before. I don't remember the previous version that worked better.
Example is NiNode that showed the fields depending on version and type.
I tested it with python 3.7 and python 2.7.
Thanks

[Spec] Block Versioning

Various features being worked on necessitate that an entire block scope be limited to a range or set of versions to minimize how many distinct NIF versions "see" that block. Range or set are the two important ways that block versions need to be referenced.

Note: The syntax here will be relying on Issues #69 and #70 , so it is important to be familiarized with those tickets. Most importantly, a "version" has become more than simply the 4-component Version number, and these proposed attributes will not be using expressions. Therefore they will be using the new ID attribute to reference these distinct versions.

Range

Some blocks can simply be defined as implemented in a specific version or removed in a specific version, or both. They are available to all versions within that range.

Example

<niobject name="NiPhysXDynamicSrc" inherit="NiPhysXRigidBodySrc" since="V20_2_0_8">
<niobject name="NiMesh" inherit="NiRenderObject" since="V20_5_0_0">
<niobject name="NiPSysModifier" abstract="1" inherit="NiObject" since="V10_1_0_0" until="V20_5_0_0">

Note: The until for NiPSysModifier is actually the version it was deprecated. It's possible that some game still uses NiPSys* instead of the replacement system. This is addressed below in For Discussion.

Set

Some blocks need to have each version they belong to listed in a non-continuous fashion. Bethesda's custom blocks are a perfect example, as are nearly all custom blocks.

When a block is for only one single version, using since/until is a bit clunky and verbose, so sets of versions can also have a size of 1.

Note: Space-separated is the correct way to write lists in XML.

Example

<niobject name="Ni3dsAlphaAnimator" inherit="NiObject" versions="V2_3">
<niobject name="AvoidNode" inherit="NiNode" versions="V4_0_0_2">
<niobject name="FxWidget" inherit="NiNode" versions="V10_0_1_0">
<niobject name="BSTriShape" inherit="NiAVObject" versions="V20_2_0_7_SSE V20_2_0_7_FO4">
<niobject name="BSDynamicTriShape" inherit="BSTriShape" versions="V20_2_0_7_SSE">
<!-- Reference Issue #70 for these. In short they are tokens representing a list of many versions. -->
<niobject name="bhkRefObject" versions="#BETHESDA#">
<niobject name="bhkPoseArray" versions="#FO3#" />

For Discussion

Deprecated vs Removed

The attribute until implies the finality of "removed", yet most info available to us only denotes the version in which the block was deprecated. So alternatively,

since, until => introduced, deprecated, removed

...Instead? The word "introduced" was chosen because "implemented" clashes a bit too much with the proposed block contracts specification syntax (#55). In fact since could still be kept, but split until into deprecated/removed, whichever is applicable and available.

Deprecation as part of the block attributes would have a number of uses, namely warning that a NIF being loaded is using deprecated systems, or if creating a NIF yourself, that you might want to use the newer alternatives.

One important difference between until and removed is that until is inclusive and removed is not. This should be taken into consideration when deciding which set of attributes makes the most sense.

Opinion: I believe that since/until is most natural, and deprecated can be a third optional attribute associated with these attributes. And that way you are also making the deprecation knowledge part of the spec.

Inheritance

Technically, the versions a block belongs to must confine any children of that block to only those versions. The children can override the set of versions, but only from the versions on the parent. For example, a child block cannot apply to 30.0.0.2 if the parent was removed in 20.5.0.0. Verifying this need not fall on parsers or interpreters however. It would be part of the XML's eventual test suite.

The question is, do we repeat the version info on every block even if it's inherited? This is repetitious, but also explicit. It also simplifies parsing as you do not need to follow each parent up the hierarchy to get a set of versions for a child block.

Opinion: Marching through ancestors to get the set of applicable versions is unwieldy for parsers. Putting the versions on each block is verbose but also explicit, and easy to validate there are no violations (children listing versions outside their parents' sets).

Mutual exclusivity by version

Can a block have more than one definition if they are mutually exclusive due to versioning?

Some blocks like NiGeometry/NiGeometryData and NiParticlesData are extremely complex due to changes by Bethesda to the actual inheritance of the types. For some NIF versions, NiGeometry was removed and replaced with BSGeometry. The current nif.xml goes through a lot of effort to fake this inheritance change.

However, there is likely a much simpler way and that's with aliasing, e.g. If Version == V20_2_0_7_SSE, NiGeometryData -> BSGeometryData, NiGeometry -> BSGeometry, and so on. This essentially reroutes the RTTI type to a new type for specific versions. Although this would likely harm interoperability of classes and damage the API for stuff like niflib and prohibit code reuse. So discussion of this feature would need its own ticket.

Opinion: Type redirection to simplify some of the blocks messed up by custom versions sounds very appealing, but needs more thought, and its own ticket. Using the block versions to have exclusive types seems suboptimal compared to conditional type aliasing.

Scope

Should this apply to only niobject and compound or do enum and bitflag benefit as well? The reasoning for applying it to those types as well is mostly for cleanliness when it comes to code generation which targets only certain versions or ignores unsupported versions. If you do not generate the code that uses the enums, you do not need the enums either.

Opinion: Should be applicable for all the datatypes, except maybe <basic>.

Referencing types which are unavailable or "hidden"

Any parser/intepreter/generator will need to be able to deal with the notion of unavailable types for a version. For example:

<niobject name="NiMesh" inherit="NiRenderObject" since="V20_5_0_0">
    <!-- ... -->
    <!-- Epic Mickey -->
    <add name="Has Extra EM Data" type="bool" ver1="20.6.5.0" ver2="20.6.5.0" vercond="(User Version &gt; 9)" />
    <add name="Extra EM Data" type="ExtraMeshDataEpicMickey" cond="Has Extra EM Data" ver1="20.6.5.0" ver2="20.6.5.0" vercond="(User Version &gt; 9)" />
</niobject>

Note that since the "Has" row is explicitly versioned, everything but the cond in the next row is actually unnecessary. The "Has" can never be true if it doesn't exist in the current version. So this is also currently valid in the XML:

<add name="Has Extra EM Data" type="bool" ver1="20.6.5.0" ver2="20.6.5.0" vercond="(User Version &gt; 9)" />
<add name="Extra EM Data" type="ExtraMeshDataEpicMickey" cond="Has Extra EM Data" />

However, the ExtraMeshDataEpicMickey type--which only applies to 20.6.5.0--may be entirely ignored, or in the case of generation completely absent if the code was generated without support for that custom version.

So, any parser will need to be able to resolve the type="<T>" and if <T> is not found due to versioning, actually completely remove the <add> from its generation, AST, or whatever data structures being used during parsing.

Export info String Lenght Constraint

Report by @deedes
"The limits for the Export Info 1 is 133 characters and 68 characters for the Export Info 2. Everything above will simply not display the mesh in the preview window and lock up the file until the GECK is closed."

  • Check to see if this is just a Geck issue or an engine constraint too.
  • Investigate if this is specifically for this version of the nif or if it is applicable to other versions.
  • See what implementing a new attribute "lenght" will have for the various parsers.

NiTimeController Flags and default values

There are a 5th and 6th undocumented bit in Skyrim NiTimeController blocks. I do not know how early the extra bits were introduced.

The default value of the Flags is generally:

1000101 (69) - If the effects are not marked Active and there are no sequences.
1001000 (72) - If the effects are marked Active and there are no sequences.
1101100 (108) - If the effects are controlled by a sequence.

Bit 5 might have something to do with the fact that in NIFs with sequences, the controllers on the actual meshes/properties use a blend Interpolator (e.g. NiBlendFloatInterpolator) with no keyframe data. Changing sequences basically reroutes what interpolators the mesh/property is using. But turning it off doesn't seem to change sequence behavior.

Bit 6, I have no idea but all Skyrim NIFs I've seen have it. Toggling it off didn't seem to have any effect though.

So, in the nif.xml I would recommend adding something like:

    Bit 5 : Generally only set when sequences are present
    Bit 6 : Always seems to be set on Skyrim NIFs, unknown function

Or maybe we can figure out exactly what the flags are supposed to be and provide better documentation.

Also, the documentation for bits 1-2 should say:

Bit 1-2 : Cycle type  00=Loop 01=Reverse 10=Clamp

(it says "Loop" twice currently)

... and for Frequency, the default may as well be added:

<add name="Frequency" type="float" default="1.0">Frequency</add>

Support for Futurama (Original Xbox: 4.2.1.0 / PS2: 4.2.1.1)

I've a lot of this prototyped in https://github.com/JayFoxRox/futurama-tools-nifxml/ . The Xbox files are viewable in NifSkope (ideally, add NiUDSNode as accepted node type for rendering, or you won't know where certain script triggers are). The PS2 files are parseable, but have reference type errors and NifSkope refuses to load them.

You'll need something like https://github.com/JayFoxRox/futurama-tools to extract the img archive files first.

There's still a lot of stuff to do:

  • Ensure compatibility with non-Futurama files (regression testing)
  • Improve NiBsp support (format is largely unknown, and one of the array sizes must be manually entered, as I couldn't figure out how it works)
  • Improve NiWall support (entirely unknown)
  • Improve NiFltAnimationNode support (entirely unknown)
  • Consider moving NiUDSNode away from NiAVObject as it abuses field names (velocity is used to store extends / dimensions of trigger zones for mission scripts)
  • Remove platform name from version information (appears to be non-standard, although it's extremely helpful)
  • Fix all comments and field names
  • Re-enable code that had been disabled for pyffi nifxml support
  • Clean up git history

I currently don't have any plans to continue work on this myself, but I'd like to see this work upstreamed as soon as possible, so that I can remove my repository.

NiShadeProperty

Add a new BitFlag to represent the options of this property,

Moving these options will allow nif.xml users to programmatically know the values and alter them, rather that it being documented as a comment in the nif.xml.

Ref: #3

[Spec] Block contracts

A contract is a formal set of obligations which a data structure must follow. Generally it is a precondition, a postcondition, or an invariant (an assertion which must stay true). You can read up on it here.

For our uses a contract would be something that must be true for a block, evaluated at parse time. Contracts can provide additional details about a block that otherwise wouldn't be defined (i.e. it's a form of documentation) and can be used for validation as well as performance optimizations.

Reasoning

For Fallout 4, a vertex is now a compound with 15 conditional rows. NifSkope evaluates the conditions per row for each vertex which is unnecessary because the conditions are passed down from the BSTriShape, thus every row will have the same conditions on each vertex. So, to parse this compound at any reasonable speed I inserted an attribute on my shipped nif.xml which tells NifSkope the cond are all external (i.e. ARG) and to optimize this compound.

With a contract on the BSVertexData compound I was able to cache the conditions for subsequent vertices and speed up reading by over 12x.

Suggested Contracts

External Conditions externalcond

  • The <add> in this block can only have cond attributes which use a passed in ARG.
  • Error Checking: The cond attribute cannot contain the names of any <add> before it. (This is naive and would fail if a row name were "ARG" but easier than actual expression parsing.)

Potential Contracts

No Conditions nocond

  • The <add> in this block cannot have cond attributes.
  • Error Checking: The presence of a cond attribute indicates immediate failure.

No Version Conditions novercond

  • The <add> in this block cannot have attributes related to version checking.
  • Error Checking: The presence of a vercond, ver1, ver2, etc. attribute indicates immediate failure.

Fixed Size fixed

  • The block is guaranteed to always be the same size in a file. This means it contains no variable arrays, internal or external conditions, or child compounds with these things.
  • Error Checking: The presence of a cond attribute indicates immediate failure. Additionally, referenced compounds must also be fixed, and the attributes arr1 and arr2 cannot contain a variable.

Basic basic

  • The block is fixed + nocond + novercond and additionally only contains basic types (i.e. no compounds). Examples: Color3, Vector3.
  • Error Checking: The type attribute cannot point to anything but a basic block. Additionally, run the logic for fixed, nocond, and novercond.

Implicit Contracts

Some contracts would be implicit for a block type and not need to be defined.

  • <niobject> is inherently internalcond as you cannot pass ARG into it. (Ideally the parser should make certain that the cond on an <niobject> are not using ARG as an operand.)
  • <compound> is inherently mixedcond as it can use both internal and external conditions.

There is not much use for explicitly defining internalcond or mixedcond for anything.

Proposed Syntax

Single contract

<compound name="BSVertexData" implements="externalcond">

Multiple contracts

<!-- Correct way to make lists in XML attributes is space-separated -->
<compound name="BSVertexData" implements="externalcond novercond">

Parsing

Implemented as a new attribute in this way, the XML would still be backwards compatible. In fact different parsers would never have to support this at all if they did not want to. But when it comes to authoring the XML, one would still have to write valid contracts and check on parsers who do use them.

As for error checking, each contract need only be concerned with its own rules, and interactions between contracts shall not be a concern. Take for example externalcond + nocond.

Up for discussion

  • Do we this for only compounds or do we also do it for niobjects? The answer is now unquestionably yes.
  • How we document the contracts at the top of the file.. Do they get their own XML tag like <version>? Or just a comment block?
  • Which of the "potential" contracts do we really need? Are there any contracts you could think of that would be useful for documenting or validating the data behavior?
  • Is the syntax sufficient, or would anyone care to propose alternatives?

Master X Master nif/kf file not supported

Previously a request was submitted on NifSkope
niftools/nifskope#186

nif files cannot be viewed with the nifskope and blender plugins, but can be read by a 3D tool called Noesis. Currently, kf cannot be imported by any software.

A sample file exists at the request URL above, but you can submit more files as needed.

Thanks!

FONV LOD BSSegment

Migrated from sf.net - @ttl269, @deedes

This seems to be correct decode for BSSegmentedTriangle, at least as far as Fallout New Vegas LOD data is conserned.
It relates to the number of triangles in the triangle data per 'segment'
The segments seem to be limited to a max of 16, or 4x4 cells(?)
Confirmed by generating a new worldspace, placing 1 object in the worldspace and generating object lod for that worldspace
then repeating with more objects and noting the triangle count of the objects vs the resulting object lod files and BSSegmentedTriangle section.

<compound name="BSSegmentedTriangle">
Bethesda-specific node.
<add name="Unknown Byte 1" type="byte" >Unknown</add>
<add name="triangle*3 offset" type="short" >triangle*3 offset</add>
<add name="Unknown Byte 2" type="byte" >Unknown</add>
<add name="Unknown Byte 3" type="byte" >Unknown</add>
<add name="triangle count" type="short" >triangle count</add>
<add name="Unknown Byte 4" type="byte" >Unknown</add>
<add name="Unknown Byte 5" type="byte" >Unknown</add>
</compound>

Sorry for the late reply. From what I can infer from the history, our current nif.xml calls it BSSegment:

<enum name="BSSegmentFlags" storage="uint">
An unsigned 32-bit integer, describing what's inside the segment.
<option value="9" name="BSSEG_WATER">Contains water.</option>
</enum>
<compound name="BSSegment">
Bethesda-specific node.
<add name="Internal index" type="int" >Index multiplied by 1536 (0x0600)</add>
<add name="Flags" type="BSSegmentFlags" default="0x00000000">Geometry present in the segment</add>
<add name="Unknown Byte 1" type="byte" >Unknown</add>
</compound>
<niobject name="BSSegmentedTriShape" inherit="NiTriShape">
Bethesda-specific node.
<add name="Num Segments" type="int" >Number of segments in the square grid</add>
<add name="Segment" type="BSSegment" arr1="Num Segments">Configuration of each segment</add>
</niobject>

What you're suggesting appears to be something vastly different. Do you think you can produce a patch against our current xml? Note that it is in process of a severe cleanup operation; you'll find the latest one here:
https://github.com/amorilia/nifxml/tree/feature/history-cleanup

Sorry but I have absolutely no clue how to edit XML.
I provide this information on an AS IS basis and don't mod FONV anymore.
No idea how you 'rename' things and still manage for the nifskope to link to the correct nif chunk.
I do however know thats what BSSegmentFlags does in FONV has nothing to do with flags or water or multiplying by 0600. It works out to the exact tris count of LOD objects per 'cell' and offset in triangles.

errr not BSSegmentFlags but BSSegmentedTriangle
Used to be between

<niobject name="BSMultiBoundSphere" inherit="BSMultiBoundData">```
and 
```xml
<niobject name="BSSegmentedTriShape" inherit="NiTriShape">

If that means anything to you.
And yes, I understand its vastly diffrent but I generated a brand new worldspace and played with LOD objects for a few hours to come up with this decode. No idea exactly what games it applys to, but Im sure it applys to FONV.

Ok, I'll revisit this when the cleanup is done. It should be pretty easy to integrate your updates. I'm sure our current xml is vastly wrong, as the current decoding of that block just seems weird.

Yea, Maybe the current decoding makes sense in some other game, but it does not make any sense in the FONV lod files that I found this block in.

Organizing the format descriptions, templates and format informations on a NifTools repository.

Disclaimer

There's no real place to discuss this on GitHub as there's no team repository. The most relevant repositories to discuss this are this one and kfmxml, so it seems like a good place to open this issue.

Scope

The NifTools ecosystem is built around both tools to use and manipulate .nif, .kf and other formats, but also around ways to describe, analyze and manipulate these formats.

The main way to describe file formats have been .xml files with descriptions of the file's content throughout versions. Therefore, there have been two repositories made specifically for hosting these files, namely nifxml and kfmxml.

However, there hasn't been any way to store other files used in describing the contents of nif/kf files. I'm thinking about .bt templates used by 010, but not only.

Issue overview

There's no definitive and easy place to store and share templates and data used while studying the different formats handled by the NifTools tools.

  • Steps to reproduce
    Take a .bt file. Try to put it on a NifTools repository.
  • Expected result
    You can find a suitable repository to create a Pull Request on.
  • Current result
    You can't find a suitable repository.

Possible fix

The easiest way would be to create a new repository to put the templates and data on.

However, creating a new repository for this raises several organization questions. What are the limits of the file we would put in this repository ? Do we limit it to specific files ? Shouldn't already existing .xml files be in this repository too, as they are also in their own way "analysis tools" ?

Problems to solve.

Relevancy of NifXML/KfmXML specific repositories.

The first problem to solve is to determine whether or not the nifxml and kfmxml are still relevant as is. They are widely used as-is, but this shouldn't be the only reason to keep them as such.

Limits of a new repository

There's a need to determine what is the intended purpose of a new repository for analysis tools, if there's a will to create it.

The main point to consider is whether to limlit the file type in this repository to a single format, therefore duplicating repositories for each format. An example of an end result would be :

  • niftools/010-templates : contains 010 specific templates in the .btformat

Feel free to give your opinion about these problems below.

@niftools/core @niftools/nifxml-reviewer

Enum usability / definition issues

There have been numerous issues and inter-project fights over the values of the name attribute in <option>. There is also a usability issue in that it is impossible to edit enum or bitflags options by hand and know that you haven't created a non-unique name. niflib needs unique option names for the codegen from nif.xml (See: https://github.com/niftools/nifdocsys/blob/master/gen_niflib.py#L412)

There is an additional problem in that the nice neat names we give to option tags for the UI become much uglier when adding the unique prefix for the enum (See #62).

I propose:

Add a prefix attribute to <enum> and <bitflags>

The prefix attribute would be prepended to each name, preventing collisions. For applications where the uniqueness doesn't matter and where the name are used for UI, this keeps the name clean but allows codegen to enforce uniqueness via the prefix.

Example

    <enum name="EffectShaderControlledVariable" storage="uint" prefix="ESCV">
        An unsigned 32-bit integer, describing which float variable in BSEffectShaderProperty to animate.
        <option value="0" name="EmissiveMultiple">EmissiveMultiple.</option>
        <option value="1" name="Falloff Start Angle">Falloff Start Angle (degrees).</option>
        <option value="2" name="Falloff Stop Angle">Falloff Stop Angle (degrees).</option>
        <option value="3" name="Falloff Start Opacity">Falloff Start Opacity.</option>
        <option value="4" name="Falloff Stop Opacity">Falloff Stop Opacity.</option>
        <option value="5" name="Alpha Transparency">Alpha Transparency (Emissive alpha?).</option>
        <option value="6" name="U Offset">U Offset.</option>
        <option value="7" name="U Scale">U Scale.</option>
        <option value="8" name="V Offset">V Offset.</option>
        <option value="9" name="V Scale">V Scale.</option>
    </enum>

    <enum name="LightingShaderControlledVariable" storage="uint" prefix="LSCV">
        An unsigned 32-bit integer, describing which float variable in BSLightingShaderProperty to animate.
        <option value="0" name="Refraction Strength">The amount of distortion.</option>
        <option value="8" name="Environment Map Scale">Environment Map Scale.</option>
        <option value="9" name="Glossiness">Glossiness.</option>
        <option value="10" name="Specular Strength">Specular Strength.</option>
        <option value="11" name="Emissive Multiple">Emissive Multiple.</option>
        <option value="12" name="Alpha">Alpha.</option>
        <option value="20" name="U Offset">U Offset.</option>
        <option value="21" name="U Scale">U Scale.</option>
        <option value="22" name="V Offset">V Offset.</option>
        <option value="23" name="V Scale">V Scale.</option>
    </enum>

It's also the easiest way to ensure uniqueness when editing by hand as you no longer need to check the entire file for the the same name string, only assure that it is unique inside that enum/bitflag.

Adoption

For this to work, gen_niflib.py would have to be updated to account for the new prefix attribute. It would have to prepend the prefix to each name attribute on write.

DTD / Validation

nifxml should have a full Document Type Definition (DTD) for XML validation. It will allow anyone modifying nif.xml to verify that their changes maintain the well formedness of the document. Any errors in syntax or grammar will be caught when running validation on the XML.

See: http://edutechwiki.unige.ch/en/DTD_tutorial

An example of a DTD for the XML to be valid in its current state:

<!DOCTYPE niftoolsxml [
    <!ELEMENT niftoolsxml (#PCDATA|version|basic|enum|bitflags|compound|niobject)* >
    <!ATTLIST niftoolsxml version CDATA #REQUIRED>

    <!ELEMENT version (#PCDATA) >
    <!ATTLIST version num CDATA #REQUIRED>

    <!ELEMENT basic (#PCDATA) >
    <!ATTLIST basic name CDATA #REQUIRED>
    <!ATTLIST basic count CDATA #REQUIRED>
    <!ATTLIST basic niflibtype CDATA #REQUIRED>
    <!ATTLIST basic nifskopetype CDATA #REQUIRED>
    <!ATTLIST basic istemplate CDATA "0">

    <!ELEMENT enum (#PCDATA|option)* >
    <!ATTLIST enum name CDATA #REQUIRED>
    <!ATTLIST enum storage CDATA #REQUIRED>
    <!ATTLIST enum ver1 CDATA #IMPLIED>

    <!ELEMENT bitflags (#PCDATA|option)* >
    <!ATTLIST bitflags name CDATA #REQUIRED>
    <!ATTLIST bitflags storage CDATA #REQUIRED>

    <!ELEMENT option (#PCDATA) >
    <!ATTLIST option value CDATA #REQUIRED>
    <!ATTLIST option name CDATA #REQUIRED>

    <!ELEMENT add (#PCDATA) >
    <!ATTLIST add name CDATA #REQUIRED>
    <!ATTLIST add type CDATA #REQUIRED>
    <!ATTLIST add default CDATA #IMPLIED>
    <!ATTLIST add template CDATA #IMPLIED>
    <!ATTLIST add ver1 CDATA #IMPLIED>
    <!ATTLIST add ver2 CDATA #IMPLIED>
    <!ATTLIST add arr1 CDATA #IMPLIED>
    <!ATTLIST add arr2 CDATA #IMPLIED>
    <!ATTLIST add cond CDATA #IMPLIED>
    <!ATTLIST add vercond CDATA #IMPLIED>
    <!ATTLIST add calculated CDATA #IMPLIED>
    <!ATTLIST add arg CDATA #IMPLIED>
    <!ATTLIST add userver CDATA #IMPLIED>
    <!ATTLIST add arr3 CDATA #IMPLIED>
    <!ATTLIST add abstract CDATA #IMPLIED>
    <!ATTLIST add nifskopetype CDATA #IMPLIED>

    <!ELEMENT compound (#PCDATA|add)* >
    <!ATTLIST compound name CDATA #REQUIRED>
    <!ATTLIST compound count CDATA #IMPLIED>
    <!ATTLIST compound niflibtype CDATA #IMPLIED>
    <!ATTLIST compound nifskopetype CDATA #IMPLIED>
    <!ATTLIST compound istemplate CDATA "0">
    <!ATTLIST compound ver1 CDATA #IMPLIED>

    <!ELEMENT niobject (#PCDATA|add)* >
    <!ATTLIST niobject name CDATA #REQUIRED>
    <!ATTLIST niobject abstract CDATA "0">
    <!ATTLIST niobject inherit CDATA #IMPLIED>

]>

... Which I think maybe illustrates some issues with the format currently. Like:

  • What is arr3? It only occurs once.
  • What is abstract and nifskopetype doing on an add element?
  • Is ver1 on an enum intended? It only occurs once (PSLoopBehavior).
  • Is ver1 on a compound necessary? It seems to be repeated in the add...type= line.
  • What is userver and why is it not used very often? Remembered it's probably about User Version 1/2 in header.

Flags - Alpha threshold

Threshold values for NiAplhaProperty seem to be bit-flags, should find common values and see what the bits are.

@ttl269 - any input on this as you encounter this issues alot.

SkyrimLayer wireframe colors

Commit: 959cb9c

... Has gotten rid of wireframe coloring for Skyrim collisions. For example, OL_STATIC is red, and SKYL_STATIC is green (the default presumably).


OK, the regression is actually because of the disabling of Oblivion Layer when User Version >= 12. NifSkope looks at that only.

Furthermore, it doesn't actually reference those colors in the enum! NifSkope creates an index of colors and uses the index in the enum to refer to that color.

With all that said, maybe there's an opportunity to actually do something about this and do it right. :)

We could theoretically keep the core idea and change from:

<option value="1" name="OL_STATIC">Static (red)</option>

to

<option value="1" name="OL_STATIC" color="red">Static</option>

Or, more optimally:

<option value="1" name="OL_STATIC" color="#FF0000">Static</option>

While perfectly legible to me, maybe not to everybody. :)


OK, the simplest change is not naming them "Oblivion Layer" and "Skyrim Layer", etc. But keeping the Type name that way. NifSkope relies on the value being called "Layer" and I think the name should stay game-agnostic.

[Spec] Expression grammar formalization

The repo needs files which include grammars for the attributes that require evaluation of their strings.

These attributes are:

  • For <add>:
    • arr1
    • arr2
    • cond
    • vercond
    • arg (?)

For each of these, the grammar would answer:

  1. What is a valid identifier?
  2. What is a valid number?
  3. What is a valid operation?
  4. What is a valid order of operations?

There are additional things to formalize outside of the grammar itself, such as having multiple grammars specialized for the various attributes. For example, logical operations are not valid in arr1 and arr2, or are they? Since we haven't formalized this, there is no actual answer. Right now the only operations that makes arr1 and arr2 expressions are arithmetic though extremely limited (only 1-2 uses iirc).

I will elaborate on this in the comments, since discussing it at the top of the ticket will be likely overwhelming. :)

Identifier

The rules for what a valid identifier is are fairly simple. It's whatever makes a valid name for any of the XML tags, plus a few reserved keywords like the ARG and TEMPLATE tokens. However the latter does not apply to all of the attributes equally.

Identifier Regex

The current regex matches any sequence of alphanumeric (plus :) words separated by a single space.

  • Like C and other languages, the identifier cannot start with 0-9. Unlike other languages, underscore is NOT valid for a starting character.
  • Colon has to be an exception because several FO4 RTTI include colons, e.g. BSConnectPoint::Parents. Colon is also used in some name attributes already.
  • Dashes/hyphens which were present in name attributes had to be removed, as - is an arithmetic operator.
  • Question marks which were present in name attributes also had to be removed. There is no reason to make a name of a field a question.

Rigorous

identifierregexrigorous

\b[A-Za-z]+(?:[\:]{0,2}?\s{0,1}?\w)+

Edit: I have added \b to the start as it is more explicit about requiring alpha at the start. Without it it would incorrectly match 0x10000.

Disallows:

  • Any character except A-Z, a-z, 0-9, _ and :.
  • Identifiers not starting with an alphabetical character ([A-Za-z])
  • More than one space between words (2+ spaces is considered separate identifiers)
  • More than two colons in a row
  • Trailing or leading whitespace

Simplified

identifierregexsimplified

\b[A-Za-z](?:[\w\:]+\s*)+

Almost exactly half the steps in this case, but the output would have to be trimmed.

Allows:

  • Trailing colons (BAD)
  • Trailing space (BAD)-ish
  • 3+ sequential colons (BAD)-ish

Number

Version Regex

Gamebryo versions are considered a number because they are merely a way of representing each component bit shifted into an analogous number, best seen in hex, e.g. 20.2.0.7 => 0x14020007.

It gets complicated because to support versions 2.3, 3.0, 3.03, and 3.1 would require a regex that can also match floats. However, none of these versions are currently used in vercond. They are only referred to in ver1 and ver2 which are not expressions.

Simplest 4-Component or 2+ Component

versionregexsimple2

[0-9]{1,2}\.[0-9]{1,2}\.[0-9]{1,2}\.[0-9]{1,3}

[0-9]{1,2}\.[0-9]{1,2}[\.]?[0-9]{0,2}[\.]?[0-9]{0,3}

Allows:

  • Trailing periods (BAD)
  • 3-component versions which do not exist e.g. 3.0.1 (BAD)
  • Versions that start with a 0
  • Matches well outside the range of currently valid for each field, e.g. 99.99.99.999
  • Matches substrings of much larger groups of numbers and periods, e.g. 99.99.99.999.99.99.99.999

Somewhat Rigorous

versionregexkindarigorous

[0-9]{1,2}\.[0-9]{1,2}(?:[\.][0-9]{0,2}[\.][0-9]{0,3})?

Disallows:

  • Trailing periods
  • 3-component versions e.g. 3.0.1
  • Versions that start with a 0

Allows:

  • Matches well outside the range of currently valid for each field, e.g. 99.99.99.999
  • Matches substrings of much larger groups of numbers and periods, e.g. 99.99.99.999.99.99.99.999

Rigorous

versionregexrigorous

(?<![\w\.])[1-9]{1}[0-9]?\.[0-9]{1,2}(?:\.[0-9]{0,2}\.[0-9]{0,3})?(?![\w\.])

The number of steps (in Python at least) blows up with both a negative lookbehind and lookahead. In PCRE (php) it is only ~500 steps. Uncached, both take about 300ms, but even cached, Python takes ~5ms which is quite slow.

Disallows:

  • Matching of an entire version if preceded or succeeded immediately by a word character or a dot.

Pedantic

I'm not patient enough to write this one, but it would include minimizing the value ranges of each component to what exists in reality, kind of like limiting IPs to 255.255.255.255 among other limitations.

Numeric Regexes

Decimal

\d+(?!\.)

Any series of 1 or more digits that are not directly succeeded by a dot (in order to not match floats).

Hexadecimal

0[xX][0-9a-fA-F]+

Binary

0b[01]+

Float

Simple

[-+]?[0-9]+\.[0-9]+

floatregexsimple

This version of the regex could erroneously match 4-component Version identifiers.

Rigorous

(?<![\w\.])[-+]?[0-9]*[\.][0-9]+(?:[eE][-+]?[0-9]+)?(?:[fF])?(?![\w\.])

floatregexrigorous2

This still doesn't apply to the rarer formats 0.-0. 0.f -0.f 1e10 1e-5, but no one really writes them that way anyway.

Other Identifiers

TEMPLATE and ARG

These are currently special key words and should formally be considered a token. Their use outside of the intended areas (which also aren't currently defined) should error. These two tokens are also a good example of why vercond at least needs its own separate grammar (aside from arithmetic making no sense there).

  1. TEMPLATE is not actually used in any attributes with expressions, only type and template, but it technically could be. Should it?

    • Example: cond="TEMPLATE == NiProperty" i.e. template specialization.
  2. TEMPLATE and ARG should probably take on the token delimiter syntax proposed in #70 . It would make things consistent and all strings could be split on tokens via one single regex

Operators

For ease of reading I will make tables of what is in use and what operators do and do not need to be implemented that aren't already being used.

Arithmetic

Addition, Multiplication, and Division are all used. Subtraction used to be but was actually unnecessary for that field.

In Use ✔️
+ * / - % ++ -- =

Logical and Bitwise

In Use ✔️
&& || !
& | << >> ^ ~

Member Access

With the introduction of BSVertexDesc, a compound that is attempting to mimic a bitfield that spans a 64-bit integer, a child of BSVertexDesc needed to be passed into another compound via the shared parent.

<add name="Vertex Desc" type="BSVertexDesc" />
<add name="Vertex Data" type="BSVertexData" arg="Vertex Desc\Vertex Attributes" />

Most programming languages use . for this, but with our identifiers having spaces, it just looks awkward. (Also, in this particular case, the real answer is to actually introduce a real bitfield type, which is relevant to #3 and the Flags type.)

However, a member access operator is still potentially useful and should be formalized. It could potentially be used nearly everywhere because User Version 2 is actually incorrect. The header should look something like this:

<compound name="BSStreamHeader" versions="#BETHESDA#">
    <add name="Version" type="ulittle32" />
    <add name="Export Info" type="ExportInfo" />
</compound>

<compound name="Header">
    <!-- -->
    <add name="Num Blocks" type="ulittle32" ver1="3.1.0.1" />
    <add name="BSStream" type="BSStreamHeader" cond="#BSVERSIONS#" />
    <!-- -->
</compound>

As this is exactly what happens in their engine (minus the conditions on the header version of course, since it's assumed).

Then anywhere there is User Version 2 it would be replaced with BSStream\Version.

Global Variables

This brings up the next issue, which is globally available variables. Right now, all vercond assume access to the following identifiers in some way or another:

  • Version
  • User Version
  • User Version 2 (i.e. BSStream\Version)

Since they have special meaning, they should probably be tokenized (see #70), and any parser should see the token and replace it with the correct value. This is of course only for vercond--furthering the need for two grammars.

These variables are referenced in cond but only in the Header compound since they are actually local variables in that case. Which brings up the next thing in need of specification: Do we make the version values explicitly unavailable to cond? I could contrive uses for having them in cond (as I mentioned I will illustrate in a comment below), but these mostly involve using ternary expressions, something that we also haven't specified. The important thing is that cond is meant for dynamically changing its size and read/write status based on the values of its antecedent siblings.

Therefore the assumption will be that these global values are only available in the grammar specific to vercond, as special keywords.

Grammar Format/Notation

I have already nearly finished writing the grammars for both cond and vercond using a variant of PEG (Packrat) notation. Specifically, this variant can be parsed by Arpeggio for use by nifxml.py.

We do not have to decide on only one format however. A grammars folder has been added to the repo root and any formats we would like to support can be added to it for each grammar. If the files are not actually parsed directly by a library, etc. then at least the grammar is there for reference.

Grammar Requirements for Each Attribute

Operators

cond vercond arr* arg
Logical ✔️ ✔️
Relational ✔️ ✔️
Arithmetic ✔️ ✔️ ✔️
Bitwise ✔️ ✔️ ✔️꙳
Unary Not (!) ✔️ ✔️
Unary Minus (-) ✔️
Grouping ( ) ✔️ ✔️ ✔️ ✔️
Member access ✔️ ✔️ ✔️

꙳: In the BSVertexDesc case, some masking and bit shifting on a plain int64 inside of arg would have been enough to pass the required information to the compound. So, bitwise could be nice to introduce for arg.

Operands

cond vercond arr* arg
Local Ident ✔️ ✔️ ✔️
Global Ident꙳ ✔️
Version-as-int ✔️
Integer ✔️ ✔️ ✔️ ✔️
Float ✔️ ✔️
#ARG# ✔️ ✔️ ✔️
#TEMPLATE#

꙳: Any columns with an ❌ for this identifier/token means that the grammar would implement these as reserved keywords, but do nothing with them (or error, etc.).

As can be seen by comparing the columns, each attribute needs its own specific grammar even excluding the possible support of bitwise ops in arg, array and args still differ in allowed operands.

Edit: It was overlooked that arr1 already uses bitwise operators for UV Sets.

Example Grammars (WIP)

Still largely WIP, and I haven't implemented some operators yet, like modulus.

Cond (Gist)

Currently has the entire superset of all the grammars, i.e. nothing has been implemented that is not allowed in it that is allowed in other grammars.

  • Normally global keywords like Version and User Version appear in cond only in the Header compound. These keywords should be reserved and unavailable for use in cond, but that requires morphing them into tokens such as #VER# and #USER# for use in vercond.
  • Decide if literal floats be used at all in expressions. Will elaborate below.

There's a very odd case of floating point comparison required for BSLightingShaderProperty for FO4:

<add name="Backlight Power" type="float" cond="Rimlight Power == 0x7F7FFFFF" ...  />

To assure an exact comparison, the RHS is written as essentially hex bytes, and it is assumed that the expression parser convert the float to hex bytes as well for the LHS.

  1. Should comparing a float with a non-float receive its own special non-terminal in the grammar? This way it is clear that something special is being done.
  2. Should byte-for-byte comparisons receive an entirely separate operator?
  3. Should the use of float literals actually be removed from the grammar as they have no use in arithmetic, etc. (for the purposes of nif.xml)?

Vercond (Gist)

As discussed, only has relational and logical operators. No member access operator, bitwise, or arithmetic operators.

Current Issues:

  • Must not allow the entire string to only be a version keyword or a version number, must have both LHS/RHS
  • Disallow use of Version, User Version, etc. by reserving as keywords.
  • Replace them with tokens such as #VER# and #USER# and that way they are unambiguous.
  • Alternate version identifier using ID values formalized in #69 in the format VXX_X_X_X. This allows any language, interpreted or generated, to skip converting the XX.X.X.X format to an integer and use the enumeration value directly.

Arr (Gist)

No relational or logical operators. No floating point numbers. Has member access operators, bitwise, and arithmetic.

Arg

Haven't approached this with any grammar yet. Still not totally sure if it will be necessary.

Note

This ticket is WIP and I will be updating it more this evening, and adding the examples of the PEG grammar once I finalize some things. I do have Arpeggio working fully with the grammars I've defined, and it will be replacing the expression parsing in nifxml.py.

I have yet to elaborate on:

  • What is a valid order of operations? (I currently have *an* order, but order of operations has some subtleties to discuss)
  • A summary of what is actually changing vs what is just being formalized, i.e. what repercussions this has on existing projects.
  • The new <version> ID attribute (Issue #69), and it becoming its own identifier format in addition to--or maybe replacing--the XX.X.X.X format used now. Essentially, the version has a unique identifier and can be referenced directly now, even for version ranges, which is simply all the <version> between two specified IDs. See checklists above.
  • Version vs #VERSION# (or #VER#, etc.) in expressions. That is, we need to make Version, User Version, and BSStream Version reserved keywords or tokens, and also disallow them in cond because it can't have globals. But in the Header compound, they are local and need to be used as locals, and so can't be reserved. Thus all the Version etc. in vercond probably need to be tokenized and I will elaborate on that later. See checklists above.

Support 10.2.0.0, User Version 1

Migrated from sf.net

When I try to load a .nif file (Gamebryo File Format, Version 10.2.0.0), I have systematically an error : "9 Vertex not found"
The number is different if I change the input file, but no one works.
I joinded a file which contain a sample of my files.

I can provide more details :
In Nifscope I have this if I brower the blocks list :
"10 NiNode "
And in block details :
"Name=Name Type=stringValue="
And my error is more precisely : "9 Vertex weight not found"

Its a new file format for "nif.xml". I already managed to render a "triangle mess" which vertexes are forming a reasonable shape :). The skeleton is already rendered. The vertexes and their "uv"s are stored in an interleaved array in "SkinPartition" after all, known so far, fields.

Attachment - https://www.dropbox.com/sh/b1kbicqvevrskgb/AAC9718qXw1pHUpYBk-idgV_a?dl=0

Change TexClampMode in BSEffectShaderProperty

TexClampMode in BSEffectShaderProperty is currently interpreted as an integer rather than an enum value. It cannot be intepreted as an member of the existing TexClampMode due to that being stored as an uint and the adjacent fields in the BSEffectShaderProperty.

Preferably, it should be possible to find a way to keep the access to the tex clamp mode uniform across the BSLightingShaderProperties and return the same type.

One possible solution would be to create a bitfield to be used in both places.

<bitfield name="TextureProperties" storage="uint">
    <member width="1" pos="0" mask="0x01" name="wrap t" type="bool" />
    <member width="1" pos="1" mask="0x02" name="wrap s" type="bool" />
    <member width="8" pos="8" mask="0xFF00" name="Lighting Influence" type="byte" />
    <member width="8" pos="16" mask="0xFF0000" name="Env Map Min LOD" type="byte" />
</bitfield>

or even two, with the second one missing the extra members like so:

<bitfield name="TextureProperties" storage="uint">
    <member width="1" pos="0" mask="0x01" name="wrap t" type="bool" />
    <member width="1" pos="1" mask="0x02" name="wrap s" type="bool" />
</bitfield>

In order to prevent access to members that the rest don't have/are non-functional.

However, I want to hold off on this while the niftools addon is being overhauled. Also, since I don't actually know what the extra bytes do other than going off their name, I don't yet know of a meaningful name to assign to the field.

Conflicting info about PlatformID/RendererID

In the info for PlatformID it says "later than 30.1" and for RendererID "until 30.1", but in NiPersistentSrcTextureRendererData PlatformID is marked as until 30.1.0.0 and RendererID as since 30.1.0.1. Neither of those types have a versions attribute, so this is quite confusing.

Skyrim Havok Material

Found a material not in the nif.xml in bhkCompressedMeshShape - 401067200
~\meshes\architecture\orclonghouse\orcawningpartitionb01.nif

[Spec] nifxml linter

While rewriting nifxml.py I have written some linting functions to catch issues that simple stuff like XSD and DTD cannot.

Error Levels

CRT = Critical
ERR = Error
WRN = Warning
INF = Info

For this issue I will refer to:
ver1 as minver
ver2 as maxver

General Issues

  • CRT Missing name for XML tags: basic, compound, niobject, enum, bitflags, token
  • CRT Missing id for XML tags: version

Member-type Issues

Collision issues

  • ERR Member names that are also type names (compound, NiObject, enum, etc).
    • Member names cannot be class/enum names in case of clashing in the generated language, e.g. PascalCase is used for both members and classes.
  • ERR Expression attributes that are using type names.
    • (With cond="T" and cond="!T" being replaced in #76 this can be done)
  • ERR Cannot have both onlyT and excludeT (See #76)
  • WRN Should not have cond alongside onlyT/excludeT.
    • Reason for this is that the functionality of only/exclude used to be wrapped up in cond, and technically an easy way of adapting existing code for the new attributes is to alias it back to cond.
  • WRN Members that have (minver or maxver etc.) and vercond
    • If vercond is present, no other version-specific attributes should exist as it spreads out the version conditions and reduces readability and maintainability.
    • There are use cases where minver/maxver/vercond together would be beneficial, such as
    <add name="Switch State" type="bool" minver="10.1.0.106" vercond="User Version 2 &lt; 130" />
    As it's clear enough that the starting version is 10.1.0.106 and also User Version 2 has to be < 130.
    • Cases where this should NOT be allowed is you have Version comparisons in the vercond which means you're splitting checks on the Version across 3 attributes, e.g. ver1="X" ver2="Y" vercond="Version != Z"

Not Enough Information Issues

  • CRT Missing name or type
    • Members need both name and type.
  • ERR type that is_template but empty template string
    • A template type needs its template parameter.

Incorrect Information Issues

  • ERR name or type do not match identifier regex
    • This attribute has an invalid identifier, e.g. leading numbers, trailing spaces, invalid characters.
  • ERR template but no type or unexpected type
    • The Member does not provide a correct template type but has template filled out.
  • ERR type that should not have a default set
    • Some objects have no reason to provide a default value
  • ERR type that is Ref/Ptr but template is not a NiObject
    • A Ref/Ptr needs a template type that is an NiObject
  • ERR Type in onlyT/excludeT must be an NiObject and must inherit the member parent NiObject.
    • These attributes are meant to exclude members from certain subclasses of the NiObject.
  • ERR default contains a comma-separated list that is not parseable into a tuple of floats or ints.
  • WRN default could not be parsed into an enumerated value, int, float, or tuple of int/floats.
    • There are currently no defaults that evaluate to strings or non-numeric values. However, default strings could eventually be implemented for certain types so this will just be a warning until that time.
    • Edit: Was incorrect about the above. Default strings are used in BSConnectPoint.

Redundant Information Issues

  • WRN Members named "Unknown" that also have a description
  • WRN Members that do not appear in any <version>

Reference Issues

  • ERR Expression for vercond has identifier that is not a global name
    • vercond expression references something other than the global version identifiers from the Header.
  • WRN Referenced name does not always exist in some capacity for all of this Member's versions
    • Anything used as a reference in expressions needs to be resolveable for all rows that this Member belongs to. So you must verify they always coexist.
  • WRN Expression for cond, arr1, arr2, arg has identifier that is not a local name (Compound)
  • WRN Expression for cond, arr1, arr2, arg has identifier that is not a local name (NiObject)
  • WRN Expression for cond, arr1, arr2, arg has identifier that is not a local or inherited name (NiObject)
  • For expressions where the string is solely a reference (no logic or arithmetic):
    • WRN Referenced member for cond is not a boolean type
    • WRN Referenced member for arr1 is not an integral type
    • WRN Referenced member for arr2 is not an integral type or an integral array type
      • NOTE: Passing in arrays to arr2 is currently up for review, but as it currently stands, doing so is both valid and necessary (Strip Lengths for tri strips for example)

All of these must also filter out various tokens like #ARG# or #T# before doing checks.

Duplicate Issues

  • ERR Duplicate name that appear in the same version. For NiObject, check inheritance.
  • ERR Duplicate name that differ in castable type but do not have a suffix, regardless of version exclusivity
    • Any duplicate names without a suffix have to have castable type. For
    <add name="Value 1" type="uint" version="..." />
    <add name="Value 1" type="ushort" version="..."  />
    Value 1 casts from ushort->uint here, because the member that gets stored is of the first type, uint. A ushort can fit inside a uint, so there is no problem with this naming collision so long as they are version exclusive. But a short cannot come before an int as the data member will be declared as a short, and any int value will be truncated when trying to store in that field.

Enum-type Issues

  • ERR Option name or prefix that do not start with an alpha character
    • Even if using a Prefix, the enum name should be a valid identifier.
  • ERR default that do not exist in the enum options
    • Compares the default listed in a Member to the actual enum options to verify it exists.
  • WRN Inconsistent use of prefix/no-prefix enum strings. Prefer prefix-less.
    • The default listed in a Member should be the prefix-less enum string.
  • WRN default that use integers instead of enumeration strings (excluding bitflags)
    • Disallow use of integers as defaults for enums (but not bitflags, for now)

I've mostly written these from violations actually found in nif.xml while writing the linter, so this list is going to keep expanding as I add more linting. I will post examples of all the errors I've caught whie developing it.

Investigate depreciating <basic name ="Flags">

The basic type Flags just a wraps a number value but offers no value. The number in most cases is still presented to the user as is, and the options, usually documented in the nif.xml is unlikely to read by a user .

There are two structures which we currently use which provide what the "Flags" real functionality should.

  • - where only one "flag" value is selectable, eg different shader types.
  • a combination of "flags" value
<add name="Flags" type="Flags">
   Controller flags (usually 0x000C). Probably controls loops.
   Bit 0 : Anim type, 0=APP_TIME 1=APP_INIT
   Bit 1-2 : Cycle type  00=Loop 01=Reverse 10=Loop
   Bit 3 : Active
   Bit 4 : Play backwards
</add>

This could be reworked as

<bitflag name="AnimationFlags">
Controller flags (usually 0x000C). Probably controls loops.
    <option value="0" name="Anim type">APP_TIME</option>
    <option value="1" name="Anim type">APP_INIT</option>
    <option value="3" name="Cycle Type Loop">Loop</option>
    <option value="4" name="Cycle Type Reverse">Reverse</option>
    <option value="5" name="Cycle Type">Loop</option>
    <option value="7" name="Active">Active</option>
    <option value="8" name="Play backwards">Play backwards</option>
</bitflag>

Then the above would be simplified to this
<add name="Flags" type="AnimationFlags"></add>

This example does highlight a potential redesign in implementation. We do not have any way to to allow certain flags to be mutually exclusive, only be able to select subset of flags, ie only one Cycle Type selectable.

Additionally most tools have to add additional logic so that the value is presented in a meaningful manner to the user. eg. NifSkope know to break Col Filter into 3 options,
*LINK property,
*Collision,
*Scaled

  • 5 bits to represent the remaining options.

A partial solution, but using bitflags a generic implementation would just pull all the information directly. Less code to maintain if its generic.

The only downside is that it more bitflag types will need to be created and depending on the number of available options this will increase the size of the nif.xml. If the options can be conditional then some could be reused, like Col Filter & Skyrim Col Filter, but less readable.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.