-
Notifications
You must be signed in to change notification settings - Fork 43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Spec] Version tag changes #69
Comments
I have added definition of custom NIF file extensions to the proposed spec and updated the issue text. Example of the changes in the WIP gist: Current WIP revision |
I have also analyzed the Divinity 2 files and found that <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>
<version id="V20_3_0_9_DIV2" num="20.3.0.9" user="0x20000 0x30000" ext="item">Divinity 2</version> I also actually decoded CStreamableAssetData in the process though that's off-topic. The only DIV2 file that errors is a character rig that is filled entirely with custom blocks that I will never decode. Excluding the XML files which are actually NIFs with only custom blocks holding text. They are also not within the scope of nif.xml in my opinion, as they do not contain geometry. |
As per the discussion in Discord, I have updated the specification for version tags to include that games listed in the element content for each
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 I have updated the WIP Gist in the ticket as well. |
Here is a visualization of what the # Example Usage
class Version(object):
# Primary Version for Game
primary = r"(?:[{]{2})([^{,}]+)(?:[}]{2})"
re_prim = re.compile(primary)
game_all = r"[^{ ,}]{2}[^{,}]+[^{,}]{2}"
re_all = re.compile(game_all)
# __init__ not shown
def all_games(self):
"""All games listed for this version"""
return self.re_all.findall(self.description)
def primary_games(self):
"""Primary games listed for this version"""
return self.re_prim.findall(self.description) For ver= ver.all_games()
=> ['Bully SE', 'Warhammer', 'Lazeska', 'Howling Sword', 'Ragnarok Online 2', 'Divinity 2 (0x10000)']
ver.primary_games()
=> ['Bully SE'] This was done in alternative to creating an entire reverse mapping for game->version. |
Made `<version>` mean distinct combination of header values which produce identical block layouts. This meant splitting up several main Versions into many based on User Version or Bethesda Version. id = Used to uniquely identify this collection of NIF versions. Can also be used for enum generation, map/dictionary keys. user = List of User Versions which are functionally equivalent bsver = List of Bethesda Versions which are functionally equivalent custom = If user/bsver are default but the Version is actually specific to a developer and not an official Gamebryo version supported = Whether or not the XML fully supports reading/writing of this version. ext = List of custom file extensions associated with this version.
A concise way of listing which versions are most important to each game. Each game should have only one set of `{{}}`. 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).
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 anid
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 version10.0.1.8
. Despite this, there need not be any distinction between0
andnull
for User Version. All versions shall report a User Version of0
unless it is overridden in the attributes.<List[int]>
will be explained in Grouping of User and Bethesda versionsBethesda Versions:
bsver = <List[int]>
Alternative name:
bsstream
, named after theBSStreamHeader
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
andbsver
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:
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 totrue
denotes that it is not an official Gamebryo version and has customization of one or more blocks. For versions with auser
orbsver
,custom="true"
is implied and redundant to specify.Even though this is listed under Version Granularity, two otherwise identical
<version>
but one withcustom="true"
and one withcustom="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 tofalse
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:
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.{{}}
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.{{}}
around its name.<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.
The text was updated successfully, but these errors were encountered: