diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index c6e5b3ba..8ca6e08f 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -23,7 +23,7 @@ jobs:
msystem: UCRT64
update: true
cache: true
- install: 'base-devel mingw-w64-ucrt-x86_64-gcc msys/p7zip mingw-w64-ucrt-x86_64-qt5-base mingw-w64-ucrt-x86_64-qt5-3d mingw-w64-ucrt-x86_64-qt5-imageformats'
+ install: 'base-devel mingw-w64-ucrt-x86_64-gcc msys/p7zip mingw-w64-ucrt-x86_64-qt5-base mingw-w64-ucrt-x86_64-qt5-3d mingw-w64-ucrt-x86_64-qt5-imageformats mingw-w64-ucrt-x86_64-qt5-tools'
- name: 'Build with qmake'
shell: msys2 {0}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 27ba85be..300a1b6c 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -23,7 +23,7 @@ jobs:
msystem: UCRT64
update: true
cache: true
- install: 'base-devel mingw-w64-ucrt-x86_64-gcc msys/p7zip mingw-w64-ucrt-x86_64-qt5-base mingw-w64-ucrt-x86_64-qt5-3d mingw-w64-ucrt-x86_64-qt5-imageformats'
+ install: 'base-devel mingw-w64-ucrt-x86_64-gcc msys/p7zip mingw-w64-ucrt-x86_64-qt5-base mingw-w64-ucrt-x86_64-qt5-3d mingw-w64-ucrt-x86_64-qt5-imageformats mingw-w64-ucrt-x86_64-qt5-tools'
- name: 'Build with qmake'
shell: msys2 {0}
diff --git a/.gitmodules b/.gitmodules
index b2b73ae0..c47c5ab1 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,3 @@
-[submodule "docsys"]
- path = build/docsys
- url = https://github.com/niftools/nifdocsys.git
[submodule "qhull"]
path = lib/qhull
url = https://github.com/qhull/qhull.git
@@ -10,3 +7,9 @@
[submodule "lib/libfo76utils"]
path = lib/libfo76utils
url = https://github.com/fo76utils/libfo76utils.git
+[submodule "build/docsys/kfmxml"]
+ path = build/docsys/kfmxml
+ url = https://github.com/niftools/kfmxml.git
+[submodule "build/docsys/nifxml"]
+ path = build/docsys/nifxml
+ url = https://github.com/niftools/nifxml.git
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3b5819e0..925e59a6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,23 @@
== CHANGELOG ==
+* Vertex selection has been implemented for Starfield, and the maximum number of shapes supported by vertex selection has been increased from 256 to 32768 for all games.
+* Fixed the 'Add Tangent Spaces and Update' spell for Starfield.
+* Importing OBJ files to Skyrim SE, Fallout 4 and Fallout 76 models now creates BSTriShape geometry instead of NiTriShape.
+* Added support for vertex colors in exported and imported OBJ files (BSTriShape only).
+* Fixes to OBJ import.
+* Disabled some of the spells for newer games where they are not implemented or should not be applicable (e.g. Fallout 4 and newer do not use triangle strips).
+
+#### NifSkope-2.0.dev9-20240804
+
+* Implemented the 'Remove Duplicate Vertices' spell for Starfield, and fixed warnings about missing triangle data for older games.
* Restored support for Oblivion, Fallout 3 and New Vegas shading.
+* The UV editor now allows selecting Oblivion and Fallout 3/NV texture slots.
+* New render setting for the number of importance samples to use at 512x512 and higher PBR cube map resolutions. The default is 1024, lower values reduce pre-filtering time at the cost of image quality.
+* The view is automatically centered after importing a glTF file.
+* Fixed saving screenshots in WebP, BMP and TGA formats.
+* Fixed crash on double clicking block names in the block list.
+* Fix to issue https://github.com/hexabits/nifskope/issues/68
+* Minor optimizations in the resource manager and renderer.
#### NifSkope-2.0.dev9-20240724
diff --git a/NifSkope.pro b/NifSkope.pro
index e61971a6..0de2c883 100644
--- a/NifSkope.pro
+++ b/NifSkope.pro
@@ -526,12 +526,6 @@ build_pass|!debug_and_release {
## QMAKE_POST_LINK
###############################
-win32:contains(QT_ARCH, i386) {
- DEP += \
- dep/NifMopp.dll
- copyFiles( $$DEP )
-}
-
XML += \
build/nif.xml \
build/docsys/kfmxml/kfm.xml
diff --git a/NifSkope_functions.pri b/NifSkope_functions.pri
index bb22e5d1..d31375e9 100644
--- a/NifSkope_functions.pri
+++ b/NifSkope_functions.pri
@@ -191,13 +191,20 @@ defineReplace(QtBins) {
$$[QT_INSTALL_BINS]/libgraphite2.dll \
$$[QT_INSTALL_BINS]/libharfbuzz-0.dll \
$$[QT_INSTALL_BINS]/libiconv-2.dll \
- $$[QT_INSTALL_BINS]/libicu*.dll \
+ $$[QT_INSTALL_BINS]/libicudt*.dll \
+ $$[QT_INSTALL_BINS]/libicuin*.dll \
+ $$[QT_INSTALL_BINS]/libicuuc*.dll \
$$[QT_INSTALL_BINS]/libintl-8.dll \
$$[QT_INSTALL_BINS]/libjpeg-8.dll \
$$[QT_INSTALL_BINS]/libmd4c.dll \
- $$[QT_INSTALL_BINS]/libpcre2-*.dll \
+ $$[QT_INSTALL_BINS]/libpcre2-16*.dll \
+ $$[QT_INSTALL_BINS]/libpcre2-8*.dll \
$$[QT_INSTALL_BINS]/libpng16-*.dll \
+ $$[QT_INSTALL_BINS]/libsharpyuv-*.dll \
$$[QT_INSTALL_BINS]/libstdc++-6.dll \
+ $$[QT_INSTALL_BINS]/libwebp-*.dll \
+ $$[QT_INSTALL_BINS]/libwebpdemux-*.dll \
+ $$[QT_INSTALL_BINS]/libwebpmux-*.dll \
$$[QT_INSTALL_BINS]/libwinpthread-1.dll \
$$[QT_INSTALL_BINS]/libzstd.dll \
$$[QT_INSTALL_BINS]/zlib1.dll
diff --git a/README.md b/README.md
index 3ef9839d..d4399772 100644
--- a/README.md
+++ b/README.md
@@ -19,16 +19,21 @@ Binary packages for Windows and Linux can be downloaded from [Releases](https://
You can also download the latest official release from [niftools/nifskope](https://github.com/niftools/nifskope/releases), or development builds from [hexabits/nifskope](https://github.com/hexabits/nifskope/releases).
-**Note:** running NifSkope under Wayland on Linux may require setting the QT\_QPA\_PLATFORM environment variable to "xcb":
+**Notes:**
+
+Running NifSkope under Wayland on Linux may require setting the QT\_QPA\_PLATFORM environment variable to "xcb":
QT_QPA_PLATFORM=xcb ./NifSkope
+The resource manager in this version of NifSkope is optimized for PCs with solid-state drives. While hard disk drives generally also work, if the number of loose resources is large, load times can be significantly shorter on an SSD when the data is not cached yet by the operating system.
+
#### Building from source code
Compiling NifSkope requires Qt 5.15. On Windows, [MSYS2](https://www.msys2.org/) can be used for building. After running the MSYS2 installer, use the following commands in the MSYS2-UCRT64 shell to install required packages:
pacman -S base-devel mingw-w64-ucrt-x86_64-gcc
- pacman -S mingw-w64-ucrt-x86_64-qt5-base mingw-w64-ucrt-x86_64-qt5-3d mingw-w64-ucrt-x86_64-qt5-imageformats
+ pacman -S mingw-w64-ucrt-x86_64-qt5-base mingw-w64-ucrt-x86_64-qt5-3d
+ pacman -S mingw-w64-ucrt-x86_64-qt5-imageformats mingw-w64-ucrt-x86_64-qt5-tools
pacman -S git
All installed MSYS2 packages can be updated anytime later by running the command '**pacman -Syu**'. To download the complete NifSkope source code, use '**git clone**' as follows:
@@ -37,6 +42,7 @@ All installed MSYS2 packages can be updated anytime later by running the command
Finally, run '**qmake**' and then '**make**' in MSYS2-UCRT64 to build the source code (the -j 8 option sets the number of processes to run in parallel). The resulting binaries and required DLL files and resources are placed under '**release**'.
+ cd nifskope
qmake NifSkope.pro
make -j 8
diff --git a/build/README.md.in b/build/README.md.in
index ce7d340d..5fae514d 100644
--- a/build/README.md.in
+++ b/build/README.md.in
@@ -10,16 +10,21 @@ Binary packages for Windows and Linux can be downloaded from [Releases](https://
You can also download the latest official release from [niftools/nifskope](https://github.com/niftools/nifskope/releases), or development builds from [hexabits/nifskope](https://github.com/hexabits/nifskope/releases).
-**Note:** running NifSkope under Wayland on Linux may require setting the QT\_QPA\_PLATFORM environment variable to "xcb":
+**Notes:**
+
+Running NifSkope under Wayland on Linux may require setting the QT\_QPA\_PLATFORM environment variable to "xcb":
QT_QPA_PLATFORM=xcb ./NifSkope
+The resource manager in this version of NifSkope is optimized for PCs with solid-state drives. While hard disk drives generally also work, if the number of loose resources is large, load times can be significantly shorter on an SSD when the data is not cached yet by the operating system.
+
#### Building from source code
Compiling NifSkope requires Qt 5.15. On Windows, [MSYS2](https://www.msys2.org/) can be used for building. After running the MSYS2 installer, use the following commands in the MSYS2-UCRT64 shell to install required packages:
pacman -S base-devel mingw-w64-ucrt-x86_64-gcc
- pacman -S mingw-w64-ucrt-x86_64-qt5-base mingw-w64-ucrt-x86_64-qt5-3d mingw-w64-ucrt-x86_64-qt5-imageformats
+ pacman -S mingw-w64-ucrt-x86_64-qt5-base mingw-w64-ucrt-x86_64-qt5-3d
+ pacman -S mingw-w64-ucrt-x86_64-qt5-imageformats mingw-w64-ucrt-x86_64-qt5-tools
pacman -S git
All installed MSYS2 packages can be updated anytime later by running the command '**pacman -Syu**'. To download the complete NifSkope source code, use '**git clone**' as follows:
@@ -28,6 +33,7 @@ All installed MSYS2 packages can be updated anytime later by running the command
Finally, run '**qmake**' and then '**make**' in MSYS2-UCRT64 to build the source code (the -j 8 option sets the number of processes to run in parallel). The resulting binaries and required DLL files and resources are placed under '**release**'.
+ cd nifskope
qmake NifSkope.pro
make -j 8
diff --git a/build/docsys b/build/docsys
deleted file mode 160000
index 0ffd95a5..00000000
--- a/build/docsys
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 0ffd95a50c277e761ab664b68a827502d163c083
diff --git a/build/docsys/.project b/build/docsys/.project
new file mode 100644
index 00000000..d8d55d7d
--- /dev/null
+++ b/build/docsys/.project
@@ -0,0 +1,17 @@
+
+
+ nifdocsys
+
+
+
+
+
+ org.python.pydev.PyDevBuilder
+
+
+
+
+
+ org.python.pydev.pythonNature
+
+
diff --git a/build/docsys/__init__.py b/build/docsys/__init__.py
new file mode 100644
index 00000000..596cb5cd
--- /dev/null
+++ b/build/docsys/__init__.py
@@ -0,0 +1 @@
+__all__ = ["nifxml"]
diff --git a/build/docsys/doc/docsys.css b/build/docsys/doc/docsys.css
new file mode 100644
index 00000000..018254bc
--- /dev/null
+++ b/build/docsys/doc/docsys.css
@@ -0,0 +1,27 @@
+hr { border: 1px; color: black; background: black; height: 1px; }
+body { font-family: Verdana, Arial, Helvetica, sans-serif; }
+p, td, th, div, pre, li { color: black; font-size: 10pt; }
+td { vertical-align: text-top; }
+a { color: #006699; text-decoration: none;}
+a:link { color: #006699; text-decoration: none;}
+a:visited { color: #006699; text-decoration: none;}
+a:active { text-decoration: none;}
+a:hover { color: #DD6900; text-decoration: underline;}
+
+th { font-weight: bold; color: black; background: #D3DCE3}
+h1 { text-align: center; color: black; background: #FF6600; }
+h2 { text-align: center; color: black; background: #FFCC33; }
+h3 { text-align: left; color: black; background: #FFCC66; }
+a:link { text-decoration: none; color: #006699; background: transparent; }
+a:visited { text-decoration: none; color: #006699; background: transparent; }
+a:hover { text-decoration: underline; color: #DD6900; background: transparent; }
+a:link.nav { color: #000000; background: transparent; }
+a:visited.nav { color: #000000; background: transparent; }
+a:hover.nav { color: #FF0000; background: transparent; }
+.nav { color: #000000; background: transparent; }
+tr.reg0 { background: #CCCCCC; color: black; }
+tr.reg1 { background: #DDDDDD; color: black; }
+tr.inact0 { background: #CCAAAA; color: black; }
+tr.inact1 { background: #DDBBBB; color: black; }
+tr.extrnl { background: #AADDAA; color: black; }
+
diff --git a/build/docsys/doc/favicon.ico b/build/docsys/doc/favicon.ico
new file mode 100644
index 00000000..fc12e799
Binary files /dev/null and b/build/docsys/doc/favicon.ico differ
diff --git a/build/docsys/epydoc.bat b/build/docsys/epydoc.bat
new file mode 100644
index 00000000..fc351207
--- /dev/null
+++ b/build/docsys/epydoc.bat
@@ -0,0 +1,3 @@
+set PATH=%PATH%;C:\Program Files\Python24
+
+python -c "import sys, os.path; from os.path import join; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'epydoc') ] + sys.path; script_path = os.path.abspath(sys.path[0]); sys.path = [p for p in sys.path if os.path.abspath(p) != script_path]; from epydoc.gui import gui; gui()"
diff --git a/build/docsys/gen_niflib.py b/build/docsys/gen_niflib.py
new file mode 100755
index 00000000..f7ce49f8
--- /dev/null
+++ b/build/docsys/gen_niflib.py
@@ -0,0 +1,907 @@
+#!/usr/bin/python
+
+# gen_niflib.py
+#
+# This script generates C++ code for Niflib.
+#
+# --------------------------------------------------------------------------
+# Command line options
+#
+# -p /path/to/niflib : specifies the path where niflib can be found
+#
+# -b : enable bootstrap mode (generates templates)
+#
+# -i : do NOT generate implmentation; place all code in defines.h
+#
+# -a : generate accessors for data in classes
+#
+# -n : generate only files which match the specified name
+#
+# --------------------------------------------------------------------------
+# ***** BEGIN LICENSE BLOCK *****
+#
+# Copyright (c) 2005, NIF File Format Library and Tools
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution.
+#
+# * Neither the name of the NIF File Format Library and Tools
+# project nor the names of its contributors may be used to endorse
+# or promote products derived from this software without specific
+# prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# ***** END LICENSE BLOCK *****
+# --------------------------------------------------------------------------
+
+from nifxml import *
+from distutils.dir_util import mkpath
+import os
+import hashlib
+import itertools
+
+#
+# global data
+#
+
+ROOT_DIR = ".."
+BOOTSTRAP = False
+GENIMPL = True
+GENACCESSORS = False
+GENBLOCKS = []
+GENALLFILES = True
+
+prev = ""
+for i in sys.argv:
+ if prev == "-p":
+ ROOT_DIR = i
+ elif i == "-b":
+ BOOTSTRAP = True
+ elif i == "-i":
+ GENIMPL = False
+ elif i == "-a":
+ GENACCESSORS = True
+ elif prev == "-n":
+ GENBLOCKS.append(i)
+ GENALLFILES = False
+
+ prev = i
+
+
+# Fix known manual update attributes. For now hard code here.
+block_types["NiKeyframeData"].find_member("Num Rotation Keys").is_manual_update = True
+#block_types["NiTriStripsData"].find_member("Num Triangles").is_manual_update = True
+
+#
+# Function to extract custom code from existing file
+#
+def ExtractCustomCode( file_name ):
+ custom_lines = {}
+ custom_lines['MISC'] = []
+ custom_lines['FILE HEAD'] = []
+ custom_lines['FILE FOOT'] = []
+ custom_lines['PRE-READ'] = []
+ custom_lines['POST-READ'] = []
+ custom_lines['PRE-WRITE'] = []
+ custom_lines['POST-WRITE'] = []
+ custom_lines['PRE-STRING'] = []
+ custom_lines['POST-STRING'] = []
+ custom_lines['PRE-FIXLINKS'] = []
+ custom_lines['POST-FIXLINKS'] = []
+ custom_lines['CONSTRUCTOR'] = []
+ custom_lines['DESTRUCTOR'] = []
+
+ if os.path.isfile( file_name ) == False:
+ custom_lines['MISC'].append( "\n" )
+ custom_lines['FILE HEAD'].append( "\n" )
+ custom_lines['FILE FOOT'].append( "\n" )
+ custom_lines['PRE-READ'].append( "\n" )
+ custom_lines['POST-READ'].append( "\n" )
+ custom_lines['PRE-WRITE'].append( "\n" )
+ custom_lines['POST-WRITE'].append( "\n" )
+ custom_lines['PRE-STRING'].append( "\n" )
+ custom_lines['POST-STRING'].append( "\n" )
+ custom_lines['PRE-FIXLINKS'].append( "\n" )
+ custom_lines['POST-FIXLINKS'].append( "\n" )
+ custom_lines['CONSTRUCTOR'].append( "\n" )
+ custom_lines['DESTRUCTOR'].append( "\n" )
+ return custom_lines
+
+ f = file( file_name, 'r' )
+ lines = f.readlines()
+ f.close()
+
+ custom_flag = False
+ custom_name = ""
+
+ for l in lines:
+ if custom_flag == True:
+ if l.find( '//--END CUSTOM CODE--//' ) != -1:
+ custom_flag = False
+ else:
+ if not custom_lines[custom_name]:
+ custom_lines[custom_name] = [l]
+ else:
+ custom_lines[custom_name].append(l)
+ if l.find( '//--BEGIN MISC CUSTOM CODE--//' ) != -1:
+ custom_flag = True
+ custom_name = 'MISC'
+ elif l.find( '//--BEGIN FILE HEAD CUSTOM CODE--//' ) != -1:
+ custom_flag = True
+ custom_name = 'FILE HEAD'
+ elif l.find( '//--BEGIN FILE FOOT CUSTOM CODE--//' ) != -1:
+ custom_flag = True
+ custom_name = 'FILE FOOT'
+ elif l.find( '//--BEGIN PRE-READ CUSTOM CODE--//' ) != -1:
+ custom_flag = True
+ custom_name = 'PRE-READ'
+ elif l.find( '//--BEGIN POST-READ CUSTOM CODE--//' ) != -1:
+ custom_flag = True
+ custom_name = 'POST-READ'
+ elif l.find( '//--BEGIN PRE-WRITE CUSTOM CODE--//' ) != -1:
+ custom_flag = True
+ custom_name = 'PRE-WRITE'
+ elif l.find( '//--BEGIN POST-WRITE CUSTOM CODE--//' ) != -1:
+ custom_flag = True
+ custom_name = 'POST-WRITE'
+ elif l.find( '//--BEGIN PRE-STRING CUSTOM CODE--//' ) != -1:
+ custom_flag = True
+ custom_name = 'PRE-STRING'
+ elif l.find( '//--BEGIN POST-STRING CUSTOM CODE--//' ) != -1:
+ custom_flag = True
+ custom_name = 'POST-STRING'
+ elif l.find( '//--BEGIN PRE-FIXLINKS CUSTOM CODE--//' ) != -1:
+ custom_flag = True
+ custom_name = 'PRE-FIXLINKS'
+ elif l.find( '//--BEGIN POST-FIXLINKS CUSTOM CODE--//' ) != -1:
+ custom_flag = True
+ custom_name = 'POST-FIXLINKS'
+ elif l.find( '//--BEGIN CONSTRUCTOR CUSTOM CODE--//' ) != -1:
+ custom_flag = True
+ custom_name = 'CONSTRUCTOR'
+ elif l.find( '//--BEGIN DESTRUCTOR CUSTOM CODE--//' ) != -1:
+ custom_flag = True
+ custom_name = 'DESTRUCTOR'
+ elif l.find( '//--BEGIN INCLUDE CUSTOM CODE--//' ) != -1:
+ custom_flag = True
+ custom_name = 'INCLUDE'
+
+ return custom_lines
+
+#
+# Function to compare two files
+#
+
+def OverwriteIfChanged( original_file, candidate_file ):
+ files_differ = False
+
+ if os.path.isfile( original_file ):
+ f1 = file( original_file, 'r' )
+ f2 = file( candidate_file, 'r' )
+
+ s1 = f1.read()
+ s2 = f2.read()
+
+ f1.close()
+ f2.close()
+
+ if s1 != s2:
+ files_differ = True
+ #remove original file
+ os.unlink( original_file )
+ else:
+ files_differ = True
+
+ if files_differ:
+ #Files differ, so overwrite original with candidate
+ os.rename( candidate_file, original_file )
+
+#
+# generate compound code
+#
+
+mkpath(os.path.join(ROOT_DIR, "include/obj"))
+mkpath(os.path.join(ROOT_DIR, "include/gen"))
+
+mkpath(os.path.join(ROOT_DIR, "src/obj"))
+mkpath(os.path.join(ROOT_DIR, "src/gen"))
+
+for n in compound_names:
+ x = compound_types[n]
+
+ # skip natively implemented types
+ if x.niflibtype: continue
+ if n[:3] == 'ns ': continue
+
+ if not GENALLFILES and not x.cname in GENBLOCKS:
+ continue
+
+ #Get existing custom code
+ file_name = ROOT_DIR + '/include/gen/' + x.cname + '.h'
+ custom_lines = ExtractCustomCode( file_name );
+
+ h = CFile(file_name, 'w')
+ h.code( '/* Copyright (c) 2006, NIF File Format Library and Tools' )
+ h.code( 'All rights reserved. Please see niflib.h for license. */' )
+ h.code()
+ h.code( '//---THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT---//' )
+ h.code()
+ h.code( '//To change this file, alter the niftools/docsys/gen_niflib.py Python script.' )
+ h.code()
+ h.code( '#ifndef _' + x.cname.upper() + '_H_' )
+ h.code( '#define _' + x.cname.upper() + '_H_' )
+ h.code()
+ h.code( '#include "../NIF_IO.h"' )
+ if n in ["Header", "Footer"]:
+ h.code( '#include "../obj/NiObject.h"' )
+ h.code( x.code_include_h() )
+ h.write( "namespace Niflib {\n" )
+ h.code( x.code_fwd_decl() )
+ h.code()
+ # header
+ h.comment(x.description)
+ hdr = "struct %s"%x.cname
+ if x.template: hdr = "template \n%s"%hdr
+ hdr += " {"
+ h.code(hdr)
+
+ #constructor/destructor/assignment
+ if not x.template:
+ h.code( '/*! Default Constructor */' )
+ h.code( "NIFLIB_API %s();"%x.cname )
+ h.code( '/*! Default Destructor */' )
+ h.code( "NIFLIB_API ~%s();"%x.cname )
+ h.code( '/*! Copy Constructor */' )
+ h.code( 'NIFLIB_API %s( const %s & src );'%(x.cname, x.cname) )
+ h.code( '/*! Copy Operator */' )
+ h.code( 'NIFLIB_API %s & operator=( const %s & src );'%(x.cname, x.cname) )
+
+
+
+ # declaration
+ h.declare(x)
+
+ # header and footer functions
+ if n == "Header":
+ h.code( 'NIFLIB_HIDDEN NifInfo Read( istream& in );' )
+ h.code( 'NIFLIB_HIDDEN void Write( ostream& out, const NifInfo & info = NifInfo() ) const;' )
+ h.code( 'NIFLIB_HIDDEN string asString( bool verbose = false ) const;' )
+
+ if n == "Footer":
+ h.code( 'NIFLIB_HIDDEN void Read( istream& in, list & link_stack, const NifInfo & info );' )
+ h.code( 'NIFLIB_HIDDEN void Write( ostream& out, const map & link_map, list & missing_link_stack, const NifInfo & info ) const;' )
+ h.code( 'NIFLIB_HIDDEN string asString( bool verbose = false ) const;' )
+
+ h.code( '//--BEGIN MISC CUSTOM CODE--//' )
+
+ #Preserve Custom code from before
+ for l in custom_lines['MISC']:
+ h.write(l);
+
+ h.code( '//--END CUSTOM CODE--//' )
+
+ # done
+ h.code("};")
+ h.code()
+ h.write( "}\n" )
+ h.code( '#endif' )
+ h.close()
+
+ if not x.template:
+ #Get existing custom code
+ file_name = ROOT_DIR + '/src/gen/' + x.cname + '.cpp'
+ custom_lines = ExtractCustomCode( file_name );
+
+ cpp = CFile(file_name, 'w')
+ cpp.code( '/* Copyright (c) 2006, NIF File Format Library and Tools' )
+ cpp.code( 'All rights reserved. Please see niflib.h for license. */' )
+ cpp.code()
+ cpp.code( '//---THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT---//' )
+ cpp.code()
+ cpp.code( '//To change this file, alter the niftools/docsys/gen_niflib.py Python script.' )
+ cpp.code()
+ cpp.code( x.code_include_cpp( True, "../../include/gen/", "../../include/obj/" ) )
+ cpp.code( "using namespace Niflib;" )
+ cpp.code()
+ cpp.code( '//Constructor' )
+
+ # constructor
+ x_code_construct = x.code_construct()
+ #if x_code_construct:
+ cpp.code("%s::%s()"%(x.cname,x.cname) + x_code_construct + " {};")
+ cpp.code()
+
+ cpp.code('//Copy Constructor')
+ cpp.code( '%s::%s( const %s & src ) {'%(x.cname,x.cname,x.cname) )
+ cpp.code( '*this = src;' )
+ cpp.code('};')
+ cpp.code()
+
+ cpp.code('//Copy Operator')
+ cpp.code( '%s & %s::operator=( const %s & src ) {'%(x.cname,x.cname,x.cname) )
+ for m in x.members:
+ if not m.is_duplicate:
+ cpp.code('this->%s = src.%s;'%(m.cname, m.cname) )
+ cpp.code('return *this;')
+ cpp.code('};')
+ cpp.code()
+
+ cpp.code( '//Destructor' )
+
+ # destructor
+ cpp.code("%s::~%s()"%(x.cname,x.cname) + " {};")
+
+ # header and footer functions
+ if n == "Header":
+ cpp.code( 'NifInfo ' + x.cname + '::Read( istream& in ) {' )
+ cpp.code( '//Declare NifInfo structure' )
+ cpp.code( 'NifInfo info;' )
+ cpp.code()
+ cpp.stream(x, ACTION_READ)
+ cpp.code()
+ cpp.code( '//Copy info.version to local version var.' )
+ cpp.code( 'version = info.version;' )
+ cpp.code()
+ cpp.code( '//Fill out and return NifInfo structure.' )
+ cpp.code( 'info.userVersion = userVersion;' )
+ cpp.code( 'info.userVersion2 = userVersion2;' )
+ cpp.code( 'info.endian = EndianType(endianType);' )
+ cpp.code( 'info.creator = exportInfo.creator.str;' )
+ cpp.code( 'info.exportInfo1 = exportInfo.exportInfo1.str;' )
+ cpp.code( 'info.exportInfo2 = exportInfo.exportInfo2.str;' )
+ cpp.code()
+ cpp.code( 'return info;' )
+ cpp.code()
+ cpp.code( '}' )
+ cpp.code()
+ cpp.code( 'void ' + x.cname + '::Write( ostream& out, const NifInfo & info ) const {' )
+ cpp.stream(x, ACTION_WRITE)
+ cpp.code( '}' )
+ cpp.code()
+ cpp.code( 'string ' + x.cname + '::asString( bool verbose ) const {' )
+ cpp.stream(x, ACTION_OUT)
+ cpp.code( '}' )
+
+ if n == "Footer":
+ cpp.code()
+ cpp.code( 'void ' + x.cname + '::Read( istream& in, list & link_stack, const NifInfo & info ) {' )
+ cpp.stream(x, ACTION_READ)
+ cpp.code( '}' )
+ cpp.code()
+ cpp.code( 'void ' + x.cname + '::Write( ostream& out, const map & link_map, list & missing_link_stack, const NifInfo & info ) const {' )
+ cpp.stream(x, ACTION_WRITE)
+ cpp.code( '}' )
+ cpp.code()
+ cpp.code( 'string ' + x.cname + '::asString( bool verbose ) const {' )
+ cpp.stream(x, ACTION_OUT)
+ cpp.code( '}' )
+
+ cpp.code()
+ cpp.code( '//--BEGIN MISC CUSTOM CODE--//' )
+
+ #Preserve Custom code from before
+ for l in custom_lines['MISC']:
+ cpp.write(l);
+
+ cpp.code( '//--END CUSTOM CODE--//' )
+
+ cpp.close()
+
+ # Write out Public Enumeration header Enumerations
+if GENALLFILES:
+ out = CFile(ROOT_DIR + '/include/gen/enums.h', 'w')
+ out.code( '/* Copyright (c) 2006, NIF File Format Library and Tools' )
+ out.code( 'All rights reserved. Please see niflib.h for license. */' )
+ out.code('#ifndef _NIF_ENUMS_H_')
+ out.code('#define _NIF_ENUMS_H_')
+ out.code()
+ out.code( '//---THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT---//' )
+ out.code()
+ out.code( '//To change this file, alter the niftools/docsys/gen_niflib.py Python script.' )
+ out.code()
+ out.code( '#include ' )
+ out.code( 'using namespace std;' )
+ out.code()
+ out.write('namespace Niflib {\n')
+ out.code()
+ for n, x in itertools.chain(enum_types.iteritems(), flag_types.iteritems()):
+ if x.options:
+ if x.description:
+ out.comment(x.description)
+ out.code('enum %s {'%(x.cname))
+ for o in x.options:
+ out.code('%s = %s, /*!< %s */'%(o.cname, o.value, o.description))
+ out.code('};')
+ out.code()
+ out.code('ostream & operator<<( ostream & out, %s const & val );'%x.cname)
+ out.code()
+
+ out.write('}\n')
+ out.code('#endif')
+ out.close()
+
+ # Write out Internal Enumeration header (NifStream functions)
+if GENALLFILES:
+ out = CFile(ROOT_DIR + '/include/gen/enums_intl.h', 'w')
+ out.code( '/* Copyright (c) 2006, NIF File Format Library and Tools' )
+ out.code( 'All rights reserved. Please see niflib.h for license. */' )
+ out.code()
+ out.code( '//---THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT---//' )
+ out.code()
+ out.code( '//To change this file, alter the niftools/docsys/gen_niflib.py Python script.' )
+ out.code()
+ out.code('#ifndef _NIF_ENUMS_INTL_H_')
+ out.code('#define _NIF_ENUMS_INTL_H_')
+ out.code()
+ out.code( '#include ' )
+ out.code( 'using namespace std;' )
+ out.code()
+ out.code('#include "../nif_basic_types.h"')
+ out.code()
+ out.write('namespace Niflib {\n')
+ out.code()
+ for n, x in itertools.chain(enum_types.iteritems(), flag_types.iteritems()):
+ if x.options:
+ if x.description:
+ out.code()
+ out.code( '//---' + x.cname + '---//')
+ out.code()
+ out.code('void NifStream( %s & val, istream& in, const NifInfo & info = NifInfo() );'%x.cname)
+ out.code('void NifStream( %s const & val, ostream& out, const NifInfo & info = NifInfo() );'%x.cname)
+ out.code()
+
+ out.write('}\n')
+ out.code('#endif')
+ out.close()
+
+ #Write out Enumeration Implementation
+if GENALLFILES:
+ out = CFile(ROOT_DIR + '/src/gen/enums.cpp', 'w')
+ out.code( '/* Copyright (c) 2006, NIF File Format Library and Tools' )
+ out.code( 'All rights reserved. Please see niflib.h for license. */' )
+ out.code()
+ out.code( '//---THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT---//' )
+ out.code()
+ out.code( '//To change this file, alter the niftools/docsys/gen_niflib.py Python script.' )
+ out.code()
+ out.code('#include ')
+ out.code('#include ')
+ out.code('#include "../../include/NIF_IO.h"')
+ out.code('#include "../../include/gen/enums.h"')
+ out.code('#include "../../include/gen/enums_intl.h"')
+ out.code()
+ out.code('using namespace std;')
+ out.code()
+ out.write('namespace Niflib {\n')
+ out.code()
+
+ out.code()
+ for n, x in itertools.chain(enum_types.iteritems(), flag_types.iteritems()):
+ if x.options:
+ out.code()
+ out.code('//--' + x.cname + '--//')
+ out.code()
+ out.code('void NifStream( %s & val, istream& in, const NifInfo & info ) {'%(x.cname))
+ out.code('%s temp;'%(x.storage))
+ out.code('NifStream( temp, in, info );')
+ out.code('val = %s(temp);'%(x.cname))
+ out.code('}')
+ out.code()
+ out.code('void NifStream( %s const & val, ostream& out, const NifInfo & info ) {'%(x.cname))
+ out.code('NifStream( (%s)(val), out, info );'%(x.storage))
+ out.code('}')
+ out.code()
+ out.code('ostream & operator<<( ostream & out, %s const & val ) { '%(x.cname))
+ out.code('switch ( val ) {')
+ for o in x.options:
+ out.code('case %s: return out << "%s";'%(o.cname, o.name))
+ out.code('default: return out << "Invalid Value! - " << (unsigned int)(val);')
+ out.code('}')
+ out.code('}')
+ out.code()
+
+ out.write('}\n')
+ out.close()
+
+ #
+ # NiObject Registration Function
+ #
+ out = CFile(ROOT_DIR + '/src/gen/register.cpp', 'w')
+ out.code( '/* Copyright (c) 2006, NIF File Format Library and Tools' )
+ out.code( 'All rights reserved. Please see niflib.h for license. */' )
+ out.code()
+ out.code( '//---THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT---//' )
+ out.code()
+ out.code( '//To change this file, alter the niftools/docsys/gen_niflib.py Python script.' )
+ out.code()
+ out.code( '#include "../../include/ObjectRegistry.h"' )
+ for n in block_names:
+ x = block_types[n]
+ out.code( '#include "../../include/obj/' + x.cname + '.h"' )
+ out.code()
+ out.code( 'namespace Niflib {' )
+ out.code( 'void RegisterObjects() {' )
+ out.code()
+ for n in block_names:
+ x = block_types[n]
+ out.code( 'ObjectRegistry::RegisterObject( "' + x.cname + '", ' + x.cname + '::Create );' )
+ out.code()
+ out.code( '}' )
+ out.code( '}' )
+ out.close()
+
+
+#
+# NiObject Files
+#
+for n in block_names:
+ x = block_types[n]
+ x_define_name = define_name(x.cname)
+
+ if not GENALLFILES and not x.cname in GENBLOCKS:
+ continue
+
+ #
+ # NiObject Header File
+ #
+
+ #Get existing custom code
+ file_name = ROOT_DIR + '/include/obj/' + x.cname + '.h'
+ custom_lines = ExtractCustomCode( file_name );
+
+ #output new file
+ out = CFile(file_name, 'w')
+ out.code( '/* Copyright (c) 2006, NIF File Format Library and Tools' )
+ out.code( 'All rights reserved. Please see niflib.h for license. */' )
+ out.code()
+ out.code( '//-----------------------------------NOTICE----------------------------------//' )
+ out.code( '// Some of this file is automatically filled in by a Python script. Only //' )
+ out.code( '// add custom code in the designated areas or it will be overwritten during //' )
+ out.code( '// the next update. //' )
+ out.code( '//-----------------------------------NOTICE----------------------------------//' )
+ out.code()
+ out.code( '#ifndef _' + x.cname.upper() + '_H_' )
+ out.code( '#define _' + x.cname.upper() + '_H_' )
+ out.code()
+ out.code( '//--BEGIN FILE HEAD CUSTOM CODE--//' )
+
+ #Preserve Custom code from before
+ for l in custom_lines['FILE HEAD']:
+ out.write(l);
+
+ out.code( '//--END CUSTOM CODE--//' )
+ out.code()
+ out.code( x.code_include_h() )
+ out.write( "namespace Niflib {\n" )
+ if not x.inherit:
+ out.code( 'using namespace std;' )
+ out.code( x.code_fwd_decl() )
+ out.code( 'class ' + x.cname + ';' )
+ out.code( 'typedef Ref<' + x.cname + '> ' + x.cname + 'Ref;' )
+ out.code()
+ out.comment( x.description )
+ if x.inherit:
+ out.code( 'class ' + x.cname + ' : public ' + x.inherit.cname + ' {' )
+ else:
+ out.code( 'class ' + x.cname + ' : public RefObject {' )
+ out.code( 'public:' )
+ out.code( '/*! Constructor */' )
+ out.code( 'NIFLIB_API ' + x.cname + '();' )
+ out.code()
+ out.code( '/*! Destructor */' )
+ out.code( 'NIFLIB_API virtual ~' + x.cname + '();' )
+ out.code()
+ out.code( '/*!' )
+ out.code( ' * A constant value which uniquly identifies objects of this type.' )
+ out.code( ' */' )
+ out.code( 'NIFLIB_API static const Type TYPE;' )
+ out.code()
+ out.code( '/*!' )
+ out.code( ' * A factory function used during file reading to create an instance of this type of object.' )
+ out.code( ' * \\return A pointer to a newly allocated instance of this type of object.' )
+ out.code( ' */' )
+ out.code( 'NIFLIB_API static NiObject * Create();' )
+ out.code()
+ out.code( '/*!' )
+ out.code( ' * Summarizes the information contained in this object in English.' )
+ out.code( ' * \\param[in] verbose Determines whether or not detailed information about large areas of data will be printed out.' )
+ out.code( ' * \\return A string containing a summary of the information within the object in English. This is the function that Niflyze calls to generate its analysis, so the output is the same.' )
+ out.code( ' */' )
+ out.code( 'NIFLIB_API virtual string asString( bool verbose = false ) const;' )
+ out.code()
+ out.code( '/*!' )
+ out.code( ' * Used to determine the type of a particular instance of this object.' )
+ out.code( ' * \\return The type constant for the actual type of the object.' )
+ out.code( ' */' )
+ out.code( 'NIFLIB_API virtual const Type & GetType() const;' )
+ out.code()
+
+ #
+ # Show example naive implementation if requested
+ #
+
+ # Create a list of members eligable for functions
+ if GENACCESSORS:
+ func_members = []
+ for y in x.members:
+ if not y.arr1_ref and not y.arr2_ref and y.cname.lower().find("unk") == -1:
+ func_members.append(y)
+
+ if len(func_members) > 0:
+ out.code( '/***Begin Example Naive Implementation****' )
+ out.code()
+ for y in func_members:
+ out.comment( y.description + "\n\\return The current value.", False )
+ out.code( y.getter_declare("", ";") )
+ out.code()
+ out.comment( y.description + "\n\\param[in] value The new value.", False )
+ out.code( y.setter_declare("", ";") )
+ out.code()
+ out.code( '****End Example Naive Implementation***/' )
+ else:
+ out.code ( '//--This object has no eligable attributes. No example implementation generated--//' )
+ out.code()
+
+ out.code( '//--BEGIN MISC CUSTOM CODE--//' )
+
+ #Preserve Custom code from before
+ for l in custom_lines['MISC']:
+ out.write(l);
+
+ out.code( '//--END CUSTOM CODE--//' )
+ if x.members:
+ out.code( 'protected:' )
+ out.declare(x)
+ out.code( 'public:' )
+ out.code( '/*! NIFLIB_HIDDEN function. For internal use only. */' )
+ out.code( 'NIFLIB_HIDDEN virtual void Read( istream& in, list & link_stack, const NifInfo & info );' )
+ out.code( '/*! NIFLIB_HIDDEN function. For internal use only. */' )
+ out.code( 'NIFLIB_HIDDEN virtual void Write( ostream& out, const map & link_map, list & missing_link_stack, const NifInfo & info ) const;' )
+ out.code( '/*! NIFLIB_HIDDEN function. For internal use only. */' )
+ out.code( 'NIFLIB_HIDDEN virtual void FixLinks( const map & objects, list & link_stack, list & missing_link_stack, const NifInfo & info );' )
+ out.code( '/*! NIFLIB_HIDDEN function. For internal use only. */' )
+ out.code( 'NIFLIB_HIDDEN virtual list GetRefs() const;' )
+ out.code( '/*! NIFLIB_HIDDEN function. For internal use only. */' )
+ out.code( 'NIFLIB_HIDDEN virtual list GetPtrs() const;' )
+ out.code( '};' )
+ out.code()
+ out.code( '//--BEGIN FILE FOOT CUSTOM CODE--//' )
+
+ #Preserve Custom code from before
+ for l in custom_lines['FILE FOOT']:
+ out.write(l);
+
+ out.code( '//--END CUSTOM CODE--//' )
+ out.code()
+ out.write( "} //End Niflib namespace\n" )
+ out.code( '#endif' )
+ out.close()
+
+ ##Check if the temp file is identical to the target file
+ #OverwriteIfChanged( file_name, 'temp' )
+
+ #
+ # NiObject Implementation File
+ #
+
+ #Get existing custom code
+ file_name = ROOT_DIR + '/src/obj/' + x.cname + '.cpp'
+ custom_lines = ExtractCustomCode( file_name );
+
+ out = CFile( file_name, 'w')
+ out.code( '/* Copyright (c) 2006, NIF File Format Library and Tools' )
+ out.code( 'All rights reserved. Please see niflib.h for license. */' )
+ out.code()
+ out.code( '//-----------------------------------NOTICE----------------------------------//' )
+ out.code( '// Some of this file is automatically filled in by a Python script. Only //' )
+ out.code( '// add custom code in the designated areas or it will be overwritten during //' )
+ out.code( '// the next update. //' )
+ out.code( '//-----------------------------------NOTICE----------------------------------//' )
+ out.code()
+ out.code( '//--BEGIN FILE HEAD CUSTOM CODE--//' )
+
+ #Preserve Custom code from before
+ for l in custom_lines['FILE HEAD']:
+ out.write(l);
+
+ out.code( '//--END CUSTOM CODE--//' )
+ out.code()
+ out.code( '#include "../../include/FixLink.h"' )
+ out.code( '#include "../../include/ObjectRegistry.h"' )
+ out.code( '#include "../../include/NIF_IO.h"' )
+ out.code( x.code_include_cpp( True, "../../include/gen/", "../../include/obj/" ) )
+ out.code( "using namespace Niflib;" );
+ out.code()
+ out.code( '//Definition of TYPE constant' )
+ if x.inherit:
+ out.code ( 'const Type ' + x.cname + '::TYPE(\"' + x.cname + '\", &' + x.inherit.cname + '::TYPE );' )
+ else:
+ out.code ( 'const Type ' + x.cname + '::TYPE(\"' + x.cname + '\", &RefObject::TYPE );' )
+ out.code()
+ x_code_construct = x.code_construct()
+ if x_code_construct:
+ out.code( x.cname + '::' + x.cname + '()' + x_code_construct + ' {' )
+ else:
+ out.code( x.cname + '::' + x.cname + '() {' )
+ out.code ( '//--BEGIN CONSTRUCTOR CUSTOM CODE--//' )
+
+ #Preserve Custom code from before
+ for l in custom_lines['CONSTRUCTOR']:
+ out.write(l);
+
+ out.code ( '//--END CUSTOM CODE--//')
+ out.code ( '}' )
+
+ out.code()
+ out.code( x.cname + '::' + '~' + x.cname + '() {' )
+ out.code ( '//--BEGIN DESTRUCTOR CUSTOM CODE--//' )
+
+ #Preserve Custom code from before
+ for l in custom_lines['DESTRUCTOR']:
+ out.write(l);
+
+ out.code ( '//--END CUSTOM CODE--//')
+ out.code ( '}' )
+ out.code()
+ out.code( 'const Type & %s::GetType() const {'%x.cname )
+ out.code( 'return TYPE;' )
+ out.code( '}' )
+ out.code()
+ out.code( 'NiObject * ' + x.cname + '::Create() {' )
+ out.code( 'return new ' + x.cname + ';' )
+ out.code( '}' )
+ out.code()
+
+ out.code("void %s::Read( istream& in, list & link_stack, const NifInfo & info ) {"%x.cname)
+ out.code( '//--BEGIN PRE-READ CUSTOM CODE--//' )
+
+ #Preserve Custom code from before
+ for l in custom_lines['PRE-READ']:
+ out.write(l);
+
+ out.code( '//--END CUSTOM CODE--//' )
+ out.code()
+ out.stream(x, ACTION_READ)
+ out.code()
+ out.code( '//--BEGIN POST-READ CUSTOM CODE--//' )
+
+ #Preserve Custom code from before
+ for l in custom_lines['POST-READ']:
+ out.write(l);
+
+ out.code( '//--END CUSTOM CODE--//' )
+ out.code("}")
+ out.code()
+
+ out.code("void %s::Write( ostream& out, const map & link_map, list & missing_link_stack, const NifInfo & info ) const {"%x.cname)
+ out.code( '//--BEGIN PRE-WRITE CUSTOM CODE--//' )
+
+ #Preserve Custom code from before
+ for l in custom_lines['PRE-WRITE']:
+ out.write(l);
+
+ out.code( '//--END CUSTOM CODE--//' )
+ out.code()
+ out.stream(x, ACTION_WRITE)
+ out.code()
+ out.code( '//--BEGIN POST-WRITE CUSTOM CODE--//' )
+
+ #Preserve Custom code from before
+ for l in custom_lines['POST-WRITE']:
+ out.write(l);
+
+ out.code( '//--END CUSTOM CODE--//' )
+ out.code("}")
+ out.code()
+
+ out.code("std::string %s::asString( bool verbose ) const {"%x.cname)
+ out.code( '//--BEGIN PRE-STRING CUSTOM CODE--//' )
+
+ #Preserve Custom code from before
+ for l in custom_lines['PRE-STRING']:
+ out.write(l);
+
+ out.code( '//--END CUSTOM CODE--//' )
+ out.code()
+ out.stream(x, ACTION_OUT)
+ out.code()
+ out.code( '//--BEGIN POST-STRING CUSTOM CODE--//' )
+
+ #Preserve Custom code from before
+ for l in custom_lines['POST-STRING']:
+ out.write(l);
+
+ out.code( '//--END CUSTOM CODE--//' )
+ out.code("}")
+ out.code()
+
+ out.code("void %s::FixLinks( const map & objects, list & link_stack, list & missing_link_stack, const NifInfo & info ) {"%x.cname)
+
+ out.code( '//--BEGIN PRE-FIXLINKS CUSTOM CODE--//' )
+
+ #Preserve Custom code from before
+ for l in custom_lines['PRE-FIXLINKS']:
+ out.write(l);
+
+ out.code( '//--END CUSTOM CODE--//' )
+ out.code()
+ out.stream(x, ACTION_FIXLINKS)
+ out.code()
+ out.code( '//--BEGIN POST-FIXLINKS CUSTOM CODE--//' )
+ #Preserve Custom code from before
+ for l in custom_lines['POST-FIXLINKS']:
+ out.write(l);
+
+ out.code( '//--END CUSTOM CODE--//' )
+ out.code("}")
+ out.code()
+
+ out.code("std::list %s::GetRefs() const {"%x.cname)
+ out.stream(x, ACTION_GETREFS)
+ out.code("}")
+ out.code()
+
+ out.code("std::list %s::GetPtrs() const {"%x.cname)
+ out.stream(x, ACTION_GETPTRS)
+ out.code("}")
+ out.code()
+
+ # Output example implementation of public getter/setter Mmthods if requested
+ if GENACCESSORS:
+ func_members = []
+ for y in x.members:
+ if not y.arr1_ref and not y.arr2_ref and y.cname.lower().find("unk") == -1:
+ func_members.append(y)
+
+ if len(func_members) > 0:
+ out.code( '/***Begin Example Naive Implementation****' )
+ out.code()
+ for y in func_members:
+ out.code( y.getter_declare(x.name + "::", " {") )
+ out.code( "return %s;"%y.cname )
+ out.code( "}" )
+ out.code()
+
+ out.code( y.setter_declare(x.name + "::", " {") )
+ out.code( "%s = value;"%y.cname )
+ out.code( "}" )
+ out.code()
+ out.code( '****End Example Naive Implementation***/' )
+ else:
+ out.code ( '//--This object has no eligable attributes. No example implementation generated--//' )
+ out.code()
+
+ out.code( '//--BEGIN MISC CUSTOM CODE--//' )
+
+ #Preserve Custom code from before
+ for l in custom_lines['MISC']:
+ out.write(l);
+
+ out.code( '//--END CUSTOM CODE--//' )
+
+ ##Check if the temp file is identical to the target file
+ #OverwriteIfChanged( file_name, 'temp' )
+
+ out.close()
diff --git a/build/docsys/kfmxml b/build/docsys/kfmxml
new file mode 160000
index 00000000..91eff92d
--- /dev/null
+++ b/build/docsys/kfmxml
@@ -0,0 +1 @@
+Subproject commit 91eff92daf197f0c5376740ed4b000d064138ec2
diff --git a/build/docsys/nifxml b/build/docsys/nifxml
new file mode 160000
index 00000000..970a6238
--- /dev/null
+++ b/build/docsys/nifxml
@@ -0,0 +1 @@
+Subproject commit 970a6238218a106daaeb89a61bcda0eeaf9d08c4
diff --git a/build/docsys/nifxml.py b/build/docsys/nifxml.py
new file mode 100644
index 00000000..9559ddb4
--- /dev/null
+++ b/build/docsys/nifxml.py
@@ -0,0 +1,1801 @@
+# TODO: split in multiple files
+
+"""
+This module generates C++ code for Niflib from the NIF file format specification XML.
+
+@author: Amorilia
+@author: Shon
+
+@contact: http://niftools.sourceforge.net
+
+@copyright:
+Copyright (c) 2005, NIF File Format Library and Tools.
+All rights reserved.
+
+@license:
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+ - Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ - Neither the name of the NIF File Format Library and Tools
+ project nor the names of its contributors may be used to endorse
+ or promote products derived from this software without specific
+ prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+@var native_types: Maps name of basic or compound type to name of type implemented manually in Niflib.
+ These are the types tagged by the niflibtype tag in the XML. For example,
+ if a (basic or compound) type with C{name="ferrari"} has C{niflibtype="car"}
+ then C{native_types["ferrari"]} equals the string C{"car"}.
+@type native_types: C{dictionary}
+
+@var basic_types: Maps name of basic type to L{Basic} instance.
+@type basic_types: C{dictionary}
+
+@var compound_types: Maps name of compound type to a L{Compound} instance.
+@type compound_types: C{dictionary}
+
+@var block_types: Maps name of the block name to a L{Block} instance.
+@type block_types: C{list}
+
+@var basic_names: Sorted keys of L{basic_types}.
+@type basic_names: C{list}
+
+@var compound_names: Sorted keys of L{compound_types}.
+@type compound_names: C{list}
+
+@var block_names: Sorted keys of L{block_types}.
+@type block_names: C{list}
+
+@var ACTION_READ: Constant for use with CFile::stream. Causes it to generate Niflib's Read function.
+@type ACTION_READ: C{int}
+
+@var ACTION_WRITE: Constant for use with CFile::stream. Causes it to generate Niflib's Write function.
+@type ACTION_WRITE: C{int}
+
+@var ACTION_OUT: Constant for use with CFile::stream. Causes it to generate Niflib's asString function.
+@type ACTION_OUT: C{int}
+
+@var ACTION_FIXLINKS: Constant for use with CFile::stream. Causes it to generate Niflib's FixLinks function.
+@type ACTION_FIXLINKS: C{int}
+
+@var ACTION_GETREFS: Constant for use with CFile::stream. Causes it to generate Niflib's GetRefs function.
+@type ACTION_GETREFS: C{int}
+
+@var ACTION_GETPTRS: Constant for use with CFile::stream. Causes it to generate Niflib's GetPtrs function.
+@type ACTION_GETPTRS: C{int}
+"""
+
+from xml.dom.minidom import *
+from textwrap import fill
+
+import sys
+import os
+import re
+import types
+
+#
+# global data
+#
+
+native_types = {}
+native_types['TEMPLATE'] = 'T'
+basic_types = {}
+enum_types = {}
+flag_types = {}
+compound_types = {}
+block_types = {}
+version_types = {}
+
+basic_names = []
+compound_names = []
+enum_names = []
+flag_names = []
+block_names = []
+version_names = []
+
+ACTION_READ = 0
+ACTION_WRITE = 1
+ACTION_OUT = 2
+ACTION_FIXLINKS = 3
+ACTION_GETREFS = 4
+ACTION_GETPTRS = 5
+
+#
+# HTML Template class
+#
+
+class Template:
+ """
+ This class processes template files. These files have tags enclosed
+ in curly brackets like this: {tag}, which are replaced when a template
+ is processed.
+ """
+ def __init__(self):
+ #Initialize variable dictionary
+ self.vars = {}
+
+ def set_var(self, var_name, value):
+ self.vars[var_name] = value
+
+ def parse(self, file_name):
+ #Open file and read contents to txt variable
+ f = file(file_name, 'r')
+ txt = f.read()
+ f.close()
+
+ #Loop through all variables, replacing them in the template text
+ for i in self.vars:
+ txt = txt.replace( "{" + i + "}", str(self.vars[i]) )
+
+ #return result
+ return txt
+
+#
+# C++ code formatting functions
+#
+
+class CFile(file):
+ """
+ This class represents a C++ source file. It is used to open the file for output
+ and automatically handles indentation by detecting brackets and colons.
+ It also handles writing the generated Niflib C++ code.
+ @ivar indent: The current level of indentation.
+ @type indent: int
+ @ivar backslash_mode: Determines whether a backslash is appended to each line for creation of multi-line defines
+ @type backslash_mode: bool
+ """
+ def __init__(self, filename, mode):
+ """
+ This constructor requires the name of the file to open and the IO mode to open it in.
+ @param filename: The name of the ouput file to open
+ @type filename: string
+ @param mode: The IO Mode. Same as fopen? Usually should be 'r', 'w', or 'a'
+ @type mode: char
+ """
+ file.__init__(self, filename, mode)
+ self.indent = 0
+ self.backslash_mode = False
+
+
+ def code(self, txt = None):
+ r"""
+ Formats a line of C++ code; the returned result always ends with a newline.
+ If txt starts with "E{rb}", indent is decreased, if it ends with "E{lb}", indent is increased.
+ Text ending in "E{:}" de-indents itself. For example "publicE{:}"
+ Result always ends with a newline
+ @param txt: None means just a line break. This will also break the backslash, which is kind of handy.
+ "\n" will create a backslashed newline in backslash mode.
+ @type txt: string, None
+ """
+ # txt
+ # this will also break the backslash, which is kind of handy
+ # call code("\n") if you want a backslashed newline in backslash mode
+ if txt == None:
+ self.write("\n")
+ return
+
+ # block end
+ if txt[:1] == "}": self.indent -= 1
+ # special, private:, public:, and protected:
+ if txt[-1:] == ":": self.indent -= 1
+ # endline string
+ if self.backslash_mode:
+ endl = " \\\n"
+ else:
+ endl = "\n"
+ # indent string
+ prefix = "\t" * self.indent
+ # strip trailing whitespace, including newlines
+ txt = txt.rstrip()
+ # indent, and add newline
+ result = prefix + txt.replace("\n", endl + prefix) + endl
+ # block start
+ if txt[-1:] == "{": self.indent += 1
+ # special, private:, public:, and protected:
+ if txt[-1:] == ":": self.indent += 1
+
+ self.write(result)
+
+
+ #
+ def comment(self, txt, doxygen = True):
+ """
+ Wraps text in C++ comments and outputs it to the file. Handles multilined comments as well.
+ Result always ends with a newline
+ @param txt: The text to enclose in a Doxygen comment
+ @type txt: string
+ """
+
+ # skip comments when we are in backslash mode
+ if self.backslash_mode: return
+
+ lines = txt.split( '\n' )
+
+ txt = ""
+ for l in lines:
+ txt = txt + fill(l, 80) + "\n"
+
+ txt = txt.strip()
+
+ num_line_ends = txt.count( '\n' )
+
+
+ if doxygen:
+ if num_line_ends > 0:
+ txt = txt.replace("\n", "\n * ")
+ self.code("/*!\n * " + txt + "\n */")
+ else:
+ self.code("/*! " + txt + " */" )
+ else:
+ lines = txt.split('\n')
+ for l in lines:
+ self.code( "// " + l )
+
+ def declare(self, block):
+ """
+ Formats the member variables for a specific class as described by the XML and outputs the result to the file.
+ @param block: The class or struct to generate member functions for.
+ @type block: Block, Compound
+ """
+ if isinstance(block, Block):
+ #self.code('protected:')
+ prot_mode = True
+ for y in block.members:
+ if not y.is_duplicate:
+ if isinstance(block, Block):
+ if y.is_public and prot_mode:
+ self.code('public:')
+ prot_mode = False
+ elif not y.is_public and not prot_mode:
+ self.code('protected:')
+ prot_mode = True
+ self.comment(y.description)
+ self.code(y.code_declare())
+ if y.func:
+ self.comment(y.description)
+ self.code("%s %s() const;"%(y.ctype,y.func))
+
+ def stream(self, block, action, localprefix = "", prefix = "", arg_prefix = "", arg_member = None):
+ """
+ Generates the function code for various functions in Niflib and outputs it to the file.
+ @param block: The class or struct to generate the function for.
+ @type block: Block, Compound
+ @param action: The type of function to generate, valid values are::
+ ACTION_READ - Read function.
+ ACTION_WRITE - Write function
+ ACTION_OUT - asString function
+ ACTION_FIXLINKS - FixLinks function
+ ACTION_GETREFS - GetRefs function
+ ACTION_GETPTRS - GetPtrs function
+ @type action: ACTION_X constant
+ @param localprefix: ?
+ @type localprefix: string
+ @param prefix: ?
+ @type prefix: string
+ @param arg_prefix: ?
+ @type arg_prefix: string
+ @param arg_member: ?
+ @type arg_member: None, ?
+ """
+ lastver1 = None
+ lastver2 = None
+ lastuserver = None
+ lastcond = None
+ lastvercond = None
+ # stream name
+ if action == ACTION_READ:
+ stream = "in"
+ else:
+ stream = "out"
+
+
+ # preperation
+ if isinstance(block, Block) or block.name in ["Footer", "Header"]:
+ if action == ACTION_READ:
+ if block.has_links or block.has_crossrefs:
+ self.code("unsigned int block_num;")
+ if action == ACTION_OUT:
+ self.code("stringstream out;")
+ # declare array_output_count, only if it will actually be used
+ for y in block.members:
+ if y.arr1.lhs or (y.ctype in ["BoundingVolume", "ByteArray", "KeyGroup"]):
+ self.code("unsigned int array_output_count = 0;")
+ break
+ if action == ACTION_GETREFS:
+ self.code("list[ > refs;")
+ if action == ACTION_GETPTRS:
+ self.code("list] ptrs;")
+
+ # stream the ancestor
+ if isinstance(block, Block):
+ if block.inherit:
+ if action == ACTION_READ:
+ self.code("%s::Read( %s, link_stack, info );"%(block.inherit.cname, stream))
+ elif action == ACTION_WRITE:
+ self.code("%s::Write( %s, link_map, missing_link_stack, info );"%(block.inherit.cname, stream))
+ elif action == ACTION_OUT:
+ self.code("%s << %s::asString();"%(stream, block.inherit.cname))
+ elif action == ACTION_FIXLINKS:
+ self.code("%s::FixLinks( objects, link_stack, missing_link_stack, info );"%block.inherit.cname)
+ elif action == ACTION_GETREFS:
+ self.code("refs = %s::GetRefs();"%block.inherit.cname)
+ elif action == ACTION_GETPTRS:
+ self.code("ptrs = %s::GetPtrs();"%block.inherit.cname)
+
+ # declare and calculate local variables (TODO: GET RID OF THIS; PREFERABLY NO LOCAL VARIABLES AT ALL)
+ if action in [ACTION_READ, ACTION_WRITE, ACTION_OUT]:
+ block.members.reverse() # calculated data depends on data further down the structure
+ for y in block.members:
+ if not y.is_duplicate and not y.is_manual_update and action in [ACTION_WRITE, ACTION_OUT]:
+ if y.func:
+ self.code('%s%s = %s%s();'%(prefix, y.cname, prefix, y.func))
+ elif y.is_calculated:
+ if action in [ACTION_READ, ACTION_WRITE]:
+ self.code('%s%s = %s%sCalc(info);'%(prefix, y.cname, prefix, y.cname))
+ # ACTION_OUT is in asString(), which doesn't take version info
+ # so let's simply not print the field in this case
+ elif y.arr1_ref:
+ if not y.arr1 or not y.arr1.lhs: # Simple Scalar
+ cref = block.find_member(y.arr1_ref[0], True)
+ # if not cref.is_duplicate and not cref.next_dup and (not cref.cond.lhs or cref.cond.lhs == y.name):
+ # self.code('assert(%s%s == (%s)(%s%s.size()));'%(prefix, y.cname, y.ctype, prefix, cref.cname))
+ self.code('%s%s = (%s)(%s%s.size());'%(prefix, y.cname, y.ctype, prefix, cref.cname))
+ elif y.arr2_ref: # 1-dimensional dynamic array
+ cref = block.find_member(y.arr2_ref[0], True)
+ if not y.arr1 or not y.arr1.lhs: # Second dimension
+ # if not cref.is_duplicate and not cref.next_dup (not cref.cond.lhs or cref.cond.lhs == y.name):
+ # self.code('assert(%s%s == (%s)((%s%s.size() > 0) ? %s%s[0].size() : 0));'%(prefix, y.cname, y.ctype, prefix, cref.cname, prefix, cref.cname))
+ self.code('%s%s = (%s)((%s%s.size() > 0) ? %s%s[0].size() : 0);'%(prefix, y.cname, y.ctype, prefix, cref.cname, prefix, cref.cname))
+ else:
+ # index of dynamically sized array
+ self.code('for (unsigned int i%i = 0; i%i < %s%s.size(); i%i++)'%(self.indent, self.indent, prefix, cref.cname, self.indent))
+ self.code('\t%s%s[i%i] = (%s)(%s%s[i%i].size());'%(prefix, y.cname, self.indent, y.ctype, prefix, cref.cname, self.indent))
+ # else: #has duplicates needs to be selective based on version
+ # self.code('assert(!"%s");'%(y.name))
+ block.members.reverse() # undo reverse
+
+
+ # now comes the difficult part: processing all members recursively
+ for y in block.members:
+ # get block
+ if y.type in basic_types:
+ subblock = basic_types[y.type]
+ elif y.type in compound_types:
+ subblock = compound_types[y.type]
+ elif y.type in enum_types:
+ subblock = enum_types[y.type]
+ elif y.type in flag_types:
+ subblock = flag_types[y.type]
+
+ # check for links
+ if action in [ACTION_FIXLINKS, ACTION_GETREFS, ACTION_GETPTRS]:
+ if not subblock.has_links and not subblock.has_crossrefs:
+ continue # contains no links, so skip this member!
+ if action == ACTION_OUT:
+ if y.is_duplicate:
+ continue # don't write variables twice
+ # resolve array & cond references
+ y_arr1_lmember = None
+ y_arr2_lmember = None
+ y_cond_lmember = None
+ y_arg = None
+ y_arr1_prefix = ""
+ y_arr2_prefix = ""
+ y_cond_prefix = ""
+ y_arg_prefix = ""
+ if y.arr1.lhs or y.arr2.lhs or y.cond.lhs or y.arg:
+ for z in block.members:
+ if not y_arr1_lmember and y.arr1.lhs == z.name:
+ y_arr1_lmember = z
+ if not y_arr2_lmember and y.arr2.lhs == z.name:
+ y_arr2_lmember = z
+ if not y_cond_lmember:
+ if y.cond.lhs == z.name:
+ y_cond_lmember = z
+ elif y.cond.op == '&&' and y.cond.lhs == z.name:
+ y_cond_lmember = z
+ elif y.cond.op == '||' and y.cond.lhs == z.name:
+ y_cond_lmember = z
+ if not y_arg and y.arg == z.name:
+ y_arg = z
+ if y_arr1_lmember:
+ y_arr1_prefix = prefix
+ if y_arr2_lmember:
+ y_arr2_prefix = prefix
+ if y_cond_lmember:
+ y_cond_prefix = prefix
+ if y_arg:
+ y_arg_prefix = prefix
+ # resolve this prefix
+ y_prefix = prefix
+ # resolve arguments
+ if y.arr1 and y.arr1.lhs == 'ARG':
+ y.arr1.lhs = arg_member.name
+ y.arr1.clhs = arg_member.cname
+ y_arr1_prefix = arg_prefix
+ if y.arr2 and y.arr2.lhs == 'ARG':
+ y.arr2.lhs = arg_member.name
+ y.arr2.clhs = arg_member.cname
+ y_arr2_prefix = arg_prefix
+ if y.cond and y.cond.lhs == 'ARG':
+ y.cond.lhs = arg_member.name
+ y.cond.clhs = arg_member.cname
+ y_cond_prefix = arg_prefix
+ # conditioning
+ y_cond = y.cond.code(y_cond_prefix)
+ y_vercond = y.vercond.code('info.')
+ if action in [ACTION_READ, ACTION_WRITE, ACTION_FIXLINKS]:
+ if lastver1 != y.ver1 or lastver2 != y.ver2 or lastuserver != y.userver or lastvercond != y_vercond:
+ # we must switch to a new version block
+ # close old version block
+ if lastver1 or lastver2 or lastuserver or lastvercond: self.code("};")
+ # close old condition block as well
+ if lastcond:
+ self.code("};")
+ lastcond = None
+ # start new version block
+
+ concat = ''
+ verexpr = ''
+ if y.ver1:
+ verexpr = "( info.version >= 0x%08X )"%y.ver1
+ concat = " && "
+ if y.ver2:
+ verexpr = "%s%s( info.version <= 0x%08X )"%(verexpr, concat, y.ver2)
+ concat = " && "
+ if y.userver != None:
+ verexpr = "%s%s( info.userVersion == %s )"%(verexpr, concat, y.userver)
+ concat = " && "
+ if y_vercond:
+ verexpr = "%s%s( %s )"%(verexpr, concat, y_vercond)
+ if verexpr:
+ # remove outer redundant parenthesis
+ bleft, bright = scanBrackets(verexpr)
+ if bleft == 0 and bright == (len(verexpr) - 1):
+ self.code("if %s {"%verexpr)
+ else:
+ self.code("if ( %s ) {"%verexpr)
+
+ # start new condition block
+ if lastcond != y_cond and y_cond:
+ self.code("if ( %s ) {"%y_cond)
+ else:
+ # we remain in the same version block
+ # check condition block
+ if lastcond != y_cond:
+ if lastcond:
+ self.code("};")
+ if y_cond:
+ self.code("if ( %s ) {"%y_cond)
+ elif action == ACTION_OUT:
+ # check condition block
+ if lastcond != y_cond:
+ if lastcond:
+ self.code("};")
+ if y_cond:
+ self.code("if ( %s ) {"%y_cond)
+
+ # loop over arrays
+ # and resolve variable name
+ if not y.arr1.lhs:
+ z = "%s%s"%(y_prefix, y.cname)
+ else:
+ if action == ACTION_OUT:
+ self.code("array_output_count = 0;")
+ if y.arr1.lhs.isdigit() == False:
+ if action == ACTION_READ:
+ # default to local variable, check if variable is in current scope if not then try to use
+ # definition from resized child
+ memcode = "%s%s.resize(%s);"%(y_prefix, y.cname, y.arr1.code(y_arr1_prefix))
+ mem = block.find_member(y.arr1.lhs, True) # find member in self or parents
+ self.code(memcode)
+
+ self.code(\
+ "for (unsigned int i%i = 0; i%i < %s%s.size(); i%i++) {"%(self.indent, self.indent, y_prefix, y.cname, self.indent))
+ else:
+ self.code(\
+ "for (unsigned int i%i = 0; i%i < %s; i%i++) {"\
+ %(self.indent, self.indent, y.arr1.code(y_arr1_prefix), self.indent))
+ if action == ACTION_OUT:
+ self.code('if ( !verbose && ( array_output_count > MAXARRAYDUMP ) ) {')
+ self.code('%s << "" << endl;'%stream)
+ self.code('break;')
+ self.code('};')
+
+ if not y.arr2.lhs:
+ z = "%s%s[i%i]"%(y_prefix, y.cname, self.indent-1)
+ else:
+ if not y.arr2_dynamic:
+ if y.arr2.lhs.isdigit() == False:
+ if action == ACTION_READ:
+ self.code("%s%s[i%i].resize(%s);"%(y_prefix, y.cname, self.indent-1, y.arr2.code(y_arr2_prefix)))
+ self.code(\
+ "for (unsigned int i%i = 0; i%i < %s%s[i%i].size(); i%i++) {"\
+ %(self.indent, self.indent, y_prefix, y.cname, self.indent-1, self.indent))
+ else:
+ self.code(\
+ "for (unsigned int i%i = 0; i%i < %s; i%i++) {"\
+ %(self.indent, self.indent, y.arr2.code(y_arr2_prefix), self.indent))
+ else:
+ if action == ACTION_READ:
+ self.code("%s%s[i%i].resize(%s[i%i]);"%(y_prefix, y.cname, self.indent-1, y.arr2.code(y_arr2_prefix), self.indent-1))
+ self.code(\
+ "for (unsigned int i%i = 0; i%i < %s[i%i]; i%i++) {"\
+ %(self.indent, self.indent, y.arr2.code(y_arr2_prefix), self.indent-1, self.indent))
+ z = "%s%s[i%i][i%i]"%(y_prefix, y.cname, self.indent-2, self.indent-1)
+
+ if native_types.has_key(y.type):
+ # these actions distinguish between refs and non-refs
+ if action in [ACTION_READ, ACTION_WRITE, ACTION_FIXLINKS, ACTION_GETREFS, ACTION_GETPTRS]:
+ if (not subblock.is_link) and (not subblock.is_crossref):
+ # not a ref
+ if action in [ACTION_READ, ACTION_WRITE]:
+ # hack required for vector
+ if y.type == "bool" and y.arr1.lhs:
+ self.code("{");
+ if action == ACTION_READ:
+ self.code("bool tmp;")
+ self.code("NifStream( tmp, %s, info );"%(stream))
+ self.code("%s = tmp;" % z)
+ else: # ACTION_WRITE
+ self.code("bool tmp = %s;" % z)
+ self.code("NifStream( tmp, %s, info );"%(stream))
+ self.code("};")
+ # the usual thing
+ elif not y.arg:
+ self.code("NifStream( %s, %s, info );"%(z, stream))
+ else:
+ self.code("NifStream( %s, %s, info, %s%s );"%(z, stream, y_prefix, y.carg))
+ else:
+ # a ref
+ if action == ACTION_READ:
+ self.code("NifStream( block_num, %s, info );"%stream)
+ self.code("link_stack.push_back( block_num );")
+ elif action == ACTION_WRITE:
+ self.code("if ( info.version < VER_3_3_0_13 ) {")
+ self.code("WritePtr32( &(*%s), %s );"%(z, stream))
+ self.code("} else {")
+ self.code("if ( %s != NULL ) {"%z)
+ self.code("map::const_iterator it = link_map.find( StaticCast(%s) );" % z)
+ self.code("if (it != link_map.end()) {")
+ self.code("NifStream( it->second, %s, info );"%stream)
+ self.code("missing_link_stack.push_back( NULL );")
+ self.code("} else {")
+ self.code("NifStream( 0xFFFFFFFF, %s, info );"%stream)
+ self.code("missing_link_stack.push_back( %s );" %z)
+ self.code("}")
+ self.code("} else {")
+ self.code("NifStream( 0xFFFFFFFF, %s, info );"%stream)
+ self.code("missing_link_stack.push_back( NULL );")
+ self.code("}")
+ self.code("}")
+ elif action == ACTION_FIXLINKS:
+ self.code("%s = FixLink<%s>( objects, link_stack, missing_link_stack, info );"%(z,y.ctemplate))
+
+ elif action == ACTION_GETREFS and subblock.is_link:
+ if not y.is_duplicate:
+ self.code('if ( %s != NULL )\n\trefs.push_back(StaticCast(%s));'%(z,z))
+ elif action == ACTION_GETPTRS and subblock.is_crossref:
+ if not y.is_duplicate:
+ self.code('if ( %s != NULL )\n\tptrs.push_back((NiObject *)(%s));'%(z,z))
+ # the following actions don't distinguish between refs and non-refs
+ elif action == ACTION_OUT:
+ if not y.arr1.lhs:
+ self.code('%s << "%*s%s: " << %s << endl;'%(stream, 2*self.indent, "", y.name, z))
+ else:
+ self.code('if ( !verbose && ( array_output_count > MAXARRAYDUMP ) ) {')
+ self.code('break;')
+ self.code('};')
+ self.code('%s << "%*s%s[" << i%i << "]: " << %s << endl;'%(stream, 2*self.indent, "", y.name, self.indent-1, z))
+ self.code('array_output_count++;')
+ else:
+ subblock = compound_types[y.type]
+ if not y.arr1.lhs:
+ self.stream(subblock, action, "%s%s_"%(localprefix, y.cname), "%s."%z, y_arg_prefix, y_arg)
+ elif not y.arr2.lhs:
+ self.stream(subblock, action, "%s%s_"%(localprefix, y.cname), "%s."%z, y_arg_prefix, y_arg)
+ else:
+ self.stream(subblock, action, "%s%s_"%(localprefix, y.cname), "%s."%z, y_arg_prefix, y_arg)
+
+ # close array loops
+ if y.arr1.lhs:
+ self.code("};")
+ if y.arr2.lhs:
+ self.code("};")
+
+ lastver1 = y.ver1
+ lastver2 = y.ver2
+ lastuserver = y.userver
+ lastcond = y_cond
+ lastvercond = y_vercond
+
+ if action in [ACTION_READ, ACTION_WRITE, ACTION_FIXLINKS]:
+ if lastver1 or lastver2 or not(lastuserver is None) or lastvercond:
+ self.code("};")
+ if action in [ACTION_READ, ACTION_WRITE, ACTION_FIXLINKS, ACTION_OUT]:
+ if lastcond:
+ self.code("};")
+
+ # the end
+ if isinstance(block, Block) or block.name in ["Header", "Footer"]:
+ if action == ACTION_OUT:
+ self.code("return out.str();")
+ if action == ACTION_GETREFS:
+ self.code("return refs;")
+ if action == ACTION_GETPTRS:
+ self.code("return ptrs;")
+
+ # declaration
+ # print "$t Get$n() const; \nvoid Set$n($t value);\n\n";
+ def getset_declare(self, block, prefix = ""): # prefix is used to tag local variables only
+ for y in block.members:
+ if not y.func:
+ if y.cname.lower().find("unk") == -1:
+ self.code( y.getter_declare("", ";") )
+ self.code( y.setter_declare("", ";") )
+ self.code()
+
+
+def class_name(n):
+ """
+ Formats a valid C++ class name from the name format used in the XML.
+ @param n: The class name to format in C++ style.
+ @type n: string
+ @return The resulting valid C++ class name
+ @rtype: string
+ """
+ if n == None: return None
+ try:
+ return native_types[n]
+ except KeyError:
+ return n.replace(' ', '_')
+
+ if n == None: return None
+ try:
+ return native_types[n]
+ except KeyError:
+ pass
+ if n == 'TEMPLATE': return 'T'
+ n2 = ''
+ for i, c in enumerate(n):
+ if ('A' <= c) and (c <= 'Z'):
+ if i > 0: n2 += '_'
+ n2 += c.lower()
+ elif (('a' <= c) and (c <= 'z')) or (('0' <= c) and (c <= '9')):
+ n2 += c
+ else:
+ n2 += '_'
+ return n2
+
+def define_name(n):
+ """
+ Formats an all-uppercase version of the name for use in C++ defines.
+ @param n: The class name to format in define style.
+ @type n: string
+ @return The resulting valid C++ define name
+ @rtype: string
+ """
+ n2 = ''
+ for i, c in enumerate(n):
+ if ('A' <= c) and (c <= 'Z'):
+ if i > 0:
+ n2 += '_'
+ n2 += c
+ else:
+ n2 += c
+ elif (('a' <= c) and (c <= 'z')) or (('0' <= c) and (c <= '9')):
+ n2 += c.upper()
+ else:
+ n2 += '_'
+ return n2
+
+def member_name(n):
+ """
+ Formats a version of the name for use as a C++ member variable.
+ @param n: The attribute name to format in variable style.
+ @type n: string
+ @return The resulting valid C++ variable name
+ @rtype: string
+ """
+ if n == None: return None
+ if n == 'ARG': return 'ARG'
+ n2 = ''
+ lower = True
+ for i, c in enumerate(n):
+ if c == ' ':
+ lower = False
+ elif (('A' <= c) and (c <= 'Z')) or (('a' <= c) and (c <= 'z')) or (('0' <= c) and (c <= '9')):
+ if lower:
+ n2 += c.lower()
+ else:
+ n2 += c.upper()
+ lower = True
+ else:
+ n2 += '_'
+ lower = True
+ return n2
+
+def version2number(s):
+ """
+ Translates a legible NIF version number to the packed-byte numeric representation. For example, "10.0.1.0" is translated to 0x0A000100.
+ @param s: The version string to translate into numeric form.
+ @type s: string
+ @return The resulting numeric version of the given version string.
+ @rtype: int
+ """
+ if not s: return None
+ l = s.split('.')
+ if len(l) > 4:
+ assert(False)
+ return int(s)
+
+ if len(l) == 2:
+ version = 0
+ version += int(l[0]) << (3 * 8)
+ if len(l[1]) >= 1:
+ version += int(l[1][0]) << (2 * 8)
+ if len(l[1]) >= 2:
+ version += int(l[1][1]) << (1 * 8)
+ if len(l[1]) >= 3:
+ version += int(l[1][2:])
+ return version
+ else:
+ version = 0
+ for i in range( 0, len(l) ):
+ version += int(l[i]) << ((3-i) * 8)
+ #return (int(l[0]) << 24) + (int(l[1]) << 16) + (int(l[2]) << 8) + int(l[3])
+ return version
+
+
+def userversion2number(s):
+ """
+ Translates a legible NIF user version number to the packed-byte numeric representation.
+ Currently just converts the string to an int as this may be a raw number.
+ Probably to be used just in case this understanding changes.
+ @param s: The version string to translate into numeric form.
+ @type s: string
+ @return The resulting numeric version of the given version string.
+ @rtype: int
+ """
+ if not s: return None
+ return int(s)
+
+def scanBrackets(expr_str, fromIndex = 0):
+ """Looks for matching brackets.
+
+ >>> scanBrackets('abcde')
+ (-1, -1)
+ >>> scanBrackets('()')
+ (0, 1)
+ >>> scanBrackets('(abc(def))g')
+ (0, 9)
+ >>> s = ' (abc(dd efy 442))xxg'
+ >>> startpos, endpos = scanBrackets(s)
+ >>> print s[startpos+1:endpos]
+ abc(dd efy 442)
+ """
+ startpos = -1
+ endpos = -1
+ scandepth = 0
+ for scanpos in xrange(fromIndex, len(expr_str)):
+ scanchar = expr_str[scanpos]
+ if scanchar == "(":
+ if startpos == -1:
+ startpos = scanpos
+ scandepth += 1
+ elif scanchar == ")":
+ scandepth -= 1
+ if scandepth == 0:
+ endpos = scanpos
+ break
+ else:
+ if startpos != -1 or endpos != -1:
+ raise ValueError("expression syntax error (non-matching brackets?)")
+ return (startpos, endpos)
+
+class Expression(object):
+ """This class represents an expression.
+
+ >>> class A(object):
+ ... x = False
+ ... y = True
+ >>> a = A()
+ >>> e = Expression('x || y')
+ >>> e.eval(a)
+ 1
+ >>> Expression('99 & 15').eval(a)
+ 3
+ >>> bool(Expression('(99&15)&&y').eval(a))
+ True
+ >>> a.hello_world = False
+ >>> def nameFilter(s):
+ ... return 'hello_' + s.lower()
+ >>> bool(Expression('(99 &15) &&WoRlD', name_filter = nameFilter).eval(a))
+ False
+ >>> Expression('c && d').eval(a)
+ Traceback (most recent call last):
+ ...
+ AttributeError: 'A' object has no attribute 'c'
+ >>> bool(Expression('1 == 1').eval())
+ True
+ >>> bool(Expression('1 != 1').eval())
+ False
+ """
+ operators = [ '==', '!=', '>=', '<=', '&&', '||', '&', '|', '-', '+', '>', '<' ]
+ def __init__(self, expr_str, name_filter = None):
+ self._code = expr_str
+ left, self._op, right = self._partition(expr_str)
+ self._left = self._parse(left, name_filter)
+ if right:
+ self._right = self._parse(right, name_filter)
+ else:
+ self._right = ''
+
+ def eval(self, data = None):
+ """Evaluate the expression to an integer."""
+
+ if isinstance(self._left, Expression):
+ left = self._left.eval(data)
+ elif isinstance(self._left, basestring):
+ left = getattr(data, self._left) if self._left != '""' else ""
+ else:
+ assert(isinstance(self._left, (int, long))) # debug
+ left = self._left
+
+ if not self._op:
+ return left
+
+ if isinstance(self._right, Expression):
+ right = self._right.eval(data)
+ elif isinstance(self._right, basestring):
+ right = getattr(data, self._right) if self._right != '""' else ""
+ else:
+ assert(isinstance(self._right, (int, long))) # debug
+ right = self._right
+
+ if self._op == '==':
+ return int(left == right)
+ elif self._op == '!=':
+ return int(left != right)
+ elif self._op == '>=':
+ return int(left >= right)
+ elif self._op == '<=':
+ return int(left <= right)
+ elif self._op == '&&':
+ return int(left and right)
+ elif self._op == '||':
+ return int(left or right)
+ elif self._op == '&':
+ return left & right
+ elif self._op == '|':
+ return left | right
+ elif self._op == '-':
+ return left - right
+ elif self._op == '+':
+ return left + right
+ elif self._op == '!':
+ return not left
+ else:
+ raise NotImplementedError("expression syntax error: operator '" + op + "' not implemented")
+
+ def __str__(self):
+ """Reconstruct the expression to a string."""
+
+ left = str(self._left)
+ if not self._op: return left
+ right = str(self._right)
+ return left + ' ' + self._op + ' ' + right
+
+ @classmethod
+ def _parse(cls, expr_str, name_filter = None):
+ """Returns an Expression, string, or int, depending on the
+ contents of ."""
+ # brackets or operators => expression
+ if ("(" in expr_str) or (")" in expr_str):
+ return Expression(expr_str, name_filter)
+ for op in cls.operators:
+ if expr_str.find(op) != -1:
+ return Expression(expr_str, name_filter)
+
+ mver = re.compile("[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+")
+ iver = re.compile("[0-9]+")
+ # try to convert it to an integer
+ try:
+ if mver.match(expr_str):
+ return "0x%08X"%(version2number(expr_str))
+ elif iver.match(expr_str):
+ return str(int(expr_str))
+ except ValueError:
+ pass
+ # failed, so return the string, passed through the name filter
+ return name_filter(expr_str) if name_filter else expr_str
+
+ @classmethod
+ def _partition(cls, expr_str):
+ """Partitions expr_str. See examples below.
+
+ >>> Expression._partition('abc || efg')
+ ('abc', '||', 'efg')
+ >>> Expression._partition('abc||efg')
+ ('abc', '||', 'efg')
+ >>> Expression._partition('abcdefg')
+ ('abcdefg', '', '')
+ >>> Expression._partition(' abcdefg ')
+ ('abcdefg', '', '')
+ >>> Expression._partition(' (a | b) & c ')
+ ('a | b', '&', 'c')
+ >>> Expression._partition('(a | b)!=(b&c)')
+ ('a | b', '!=', 'b&c')
+ >>> Expression._partition('(a== b) &&(( b!=c)||d )')
+ ('a== b', '&&', '( b!=c)||d')
+ """
+ # check for unary operators
+ if expr_str.strip().startswith('!'):
+ return expr_str.lstrip(' !'), '!', None
+ lenstr = len(expr_str)
+ # check if the left hand side starts with brackets
+ # and if so, find the position of the starting bracket and the ending
+ # bracket
+ left_startpos, left_endpos = cls._scanBrackets(expr_str)
+ if left_startpos >= 0:
+ # yes, it is a bracketted expression
+ # so remove brackets and whitespace,
+ # and let that be the left hand side
+ left_str = expr_str[left_startpos+1:left_endpos].strip()
+
+ # the next token should be the operator
+ # find the position where the operator should start
+ op_startpos = left_endpos+1
+ while op_startpos < lenstr and expr_str[op_startpos] == " ":
+ op_startpos += 1
+ if op_startpos < lenstr:
+ # to avoid confusion between && and &, and || and |,
+ # let's first scan for operators of two characters
+ # and then for operators of one character
+ for op_endpos in xrange(op_startpos+1, op_startpos-1, -1):
+ op_str = expr_str[op_startpos:op_endpos+1]
+ if op_str in cls.operators:
+ break
+ else:
+ raise ValueError("expression syntax error: expected operator at '%s'"%expr_str[op_startpos:])
+ else:
+ return cls._partition(left_str)
+ else:
+ # it's not... so we need to scan for the first operator
+ for op_startpos, ch in enumerate(expr_str):
+ if ch == ' ': continue
+ if ch == '(' or ch == ')':
+ raise ValueError("expression syntax error: expected operator before '%s'"%expr_str[op_startpos:])
+ # to avoid confusion between && and &, and || and |,
+ # let's first scan for operators of two characters
+ for op_endpos in xrange(op_startpos+1, op_startpos-1, -1):
+ op_str = expr_str[op_startpos:op_endpos+1]
+ if op_str in cls.operators:
+ break
+ else:
+ continue
+ break
+ else:
+ # no operator found, so we are done
+ left_str = expr_str.strip()
+ op_str = ''
+ right_str = ''
+ return left_str, op_str, right_str
+ # operator found! now get the left hand side
+ left_str = expr_str[:op_startpos].strip()
+
+ return left_str, op_str, expr_str[op_endpos+1:].strip()
+
+ @staticmethod
+ def _scanBrackets(expr_str, fromIndex = 0):
+ """Looks for matching brackets.
+
+ >>> Expression._scanBrackets('abcde')
+ (-1, -1)
+ >>> Expression._scanBrackets('()')
+ (0, 1)
+ >>> Expression._scanBrackets('(abc(def))g')
+ (0, 9)
+ >>> s = ' (abc(dd efy 442))xxg'
+ >>> startpos, endpos = Expression._scanBrackets(s)
+ >>> print s[startpos+1:endpos]
+ abc(dd efy 442)
+ """
+ startpos = -1
+ endpos = -1
+ scandepth = 0
+ for scanpos in xrange(fromIndex, len(expr_str)):
+ scanchar = expr_str[scanpos]
+ if scanchar == "(":
+ if startpos == -1:
+ startpos = scanpos
+ scandepth += 1
+ elif scanchar == ")":
+ scandepth -= 1
+ if scandepth == 0:
+ endpos = scanpos
+ break
+ else:
+ if startpos != -1 or endpos != -1:
+ raise ValueError("expression syntax error (non-matching brackets?)")
+ return (startpos, endpos)
+
+ def code(self, prefix = '', brackets = True, name_filter = None):
+ """Format an expression as a string.
+ @param prefix: An optional prefix.
+ @type prefix: string
+ @param brackets: If C{True}, then put expression between brackets.
+ @type prefix: string
+ @return The expression formatted into a string.
+ @rtype: string
+ """
+ lbracket = "(" if brackets else ""
+ rbracket = ")" if brackets else ""
+ if not self._op:
+ if not self.lhs: return ''
+ if isinstance(self.lhs, types.IntType):
+ return self.lhs
+ elif self.lhs in block_types:
+ return 'IsDerivedType(%s::TYPE)' % self.lhs
+ else:
+ return prefix + (name_filter(self.lhs) if name_filter else self.lhs)
+ elif self._op == '!':
+ lhs = self.lhs
+ if isinstance(lhs, Expression):
+ lhs = lhs.code(prefix, True, name_filter)
+ elif lhs in block_types:
+ lhs = 'IsDerivedType(%s::TYPE)' % lhs
+ elif lhs and not lhs.isdigit() and not lhs.startswith('0x'):
+ lhs = prefix + (name_filter(lhs) if name_filter else lhs)
+ return '%s%s%s%s'%(lbracket, self._op, lhs, rbracket)
+ else:
+ lhs = self.lhs
+ rhs = self.rhs
+ if isinstance(lhs, Expression):
+ lhs = lhs.code(prefix, True, name_filter)
+ elif lhs in block_types:
+ lhs = 'IsDerivedType(%s::TYPE)' % lhs
+ elif lhs and not lhs.isdigit() and not lhs.startswith('0x'):
+ lhs = prefix + (name_filter(lhs) if name_filter else lhs)
+ if isinstance(rhs, Expression):
+ rhs = rhs.code(prefix, True, name_filter)
+ elif rhs in block_types:
+ rhs = 'IsDerivedType(%s::TYPE)' % rhs
+ elif rhs and not rhs.isdigit() and not rhs.startswith('0x'):
+ rhs = prefix + (name_filter(rhs) if name_filter else rhs)
+ return '%s%s %s %s%s'%(lbracket, lhs, self._op, rhs, rbracket)
+
+ def get_terminals(self):
+ """Return all terminal names (without operators or brackets)."""
+ if isinstance(self.lhs, Expression):
+ for terminal in self.lhs.get_terminals():
+ yield terminal
+ elif self.lhs:
+ yield self.lhs
+ if isinstance(self.rhs, Expression):
+ for terminal in self.rhs.get_terminals():
+ yield terminal
+ elif self.rhs:
+ yield self.rhs
+
+ def __getattr__(self, name):
+ if (name == 'lhs'):
+ return getattr(self, '_left')
+ if (name == 'rhs'):
+ return getattr(self, '_right')
+ if (name == 'op'):
+ return getattr(self, '_op')
+ return object.__getattribute__(self, name)
+
+ # ducktyping: pretend we're also a string with isdigit() method
+ def isdigit(self):
+ return False
+
+class Expr(Expression):
+ """
+ Represents a mathmatical expression?
+ @ivar lhs: The left hand side of the expression?
+ @type lhs: string
+ @ivar clhs: The C++ formatted version of the left hand side of the expression?
+ @type clhs: string
+ @ivar op: The operator used in the expression. ==, &&, !=, etc.
+ @type op: string
+ @ivar rhs: The right hand side of the expression?
+ @type rhs: string
+ """
+ def __init__(self, n, name_filter = None):
+ """
+ This constructor takes the expression in the form of a string and tokenizes it into left-hand side, operator, right hand side, and something called clhs.
+ @param n: The expression to tokenize.
+ @type n: string
+ """
+ Expression.__init__(self, n, name_filter)
+
+ def code(self, prefix = '', brackets = True, name_filter = None):
+ if not name_filter:
+ name_filter = member_name
+ return Expression.code(self, prefix, brackets, name_filter)
+
+class Option:
+ """
+ This class represents an option in an option list.
+ @ivar value: The C++ value of option variable. Comes from the "value" attribute of the tag.
+ @type value: string
+ @ivar name: The name of this member variable. Comes from the "name" attribute of the tag.
+ @type name: string
+ @ivar description: The description of this option. Comes from the text between and .
+ @type description: string
+ @ivar cname: The name of this member for use in C++.
+ @type cname: string
+ """
+ def __init__(self, element):
+ """
+ This constructor converts an XML element into an Option object.
+ """
+ assert element.tagName == 'option'
+ parent = element.parentNode
+ #sisters = parent.getElementsByTagName('option')
+
+ # member attributes
+ self.value = element.getAttribute('value')
+ self.name = element.getAttribute('name')
+ if element.firstChild:
+ assert element.firstChild.nodeType == Node.TEXT_NODE
+ self.description = element.firstChild.nodeValue.strip()
+ else:
+ self.description = self.name
+ self.cname = self.name.upper().replace(" ", "_").replace("-", "_").replace("/", "_").replace("=", "_")
+
+class Member:
+ """
+ This class represents a member variable?
+ @ivar name: The name of this member variable. Comes from the "name" attribute of the tag.
+ @type name: string
+ @ivar type: The type of this member variable. Comes from the "type" attribute of the tag.
+ @type type: string
+ @ivar arg: The argument of this member variable. Comes from the "arg" attribute of the tag.
+ @type arg: string
+ @ivar template: The template type of this member variable. Comes from the "template" attribute of the tag.
+ @type template: string
+ @ivar arr1: The first array size of this member variable. Comes from the "arr1" attribute of the tag.
+ @type arr1: Eval
+ @ivar arr2: The first array size of this member variable. Comes from the "arr2" attribute of the tag.
+ @type arr2: Eval
+ @ivar cond: The condition of this member variable. Comes from the "cond" attribute of the tag.
+ @type cond: Eval
+ @ivar func: The function of this member variable. Comes from the "func" attribute of the tag.
+ @type func: string
+ @ivar default: The default value of this member variable. Comes from the "default" attribute of the tag.
+ Formatted to be ready to use in a C++ constructor initializer list.
+ @type default: string
+ @ivar ver1: The first version this member exists. Comes from the "ver1" attribute of the tag.
+ @type ver1: string
+ @ivar ver2: The last version this member exists. Comes from the "ver2" attribute of the tag.
+ @type ver2: string
+ @ivar userver: The user version where this member exists. Comes from the "userver" attribute of the tag.
+ @type userver: string
+ @ivar vercond: The version condition of this member variable. Comes from the "vercond" attribute of the tag.
+ @type vercond: Eval
+ @ivar is_public: Whether this member will be declared public. Comes from the "public" attribute of the tag.
+ @type is_public: string
+ @ivar description: The description of this member variable. Comes from the text between and .
+ @type description: string
+ @ivar uses_argument: Specifies whether this attribute uses an argument.
+ @type uses_argument: bool
+ @ivar type_is_native: Specifies whether the type is implemented natively
+ @type type_is_native: bool
+ @ivar is_duplicate: Specifies whether this is a duplicate of a previously declared member
+ @type is_duplicate: bool
+ @ivar arr2_dynamic: Specifies whether arr2 refers to an array (?)
+ @type arr2_dynamic: bool
+ @ivar arr1_ref: Names of the attributes it is a (unmasked) size of (?)
+ @type arr1_ref: string array?
+ @ivar arr2_ref: Names of the attributes it is a (unmasked) size of (?)
+ @type arr2_ref: string array?
+ @ivar cond_ref: Names of the attributes it is a condition of (?)
+ @type cond_ref: string array?
+ @ivar cname: Unlike default, name isn't formatted for C++ so use this instead?
+ @type cname: string
+ @ivar ctype: Unlike default, type isn't formatted for C++ so use this instead?
+ @type ctype: string
+ @ivar carg: Unlike default, arg isn't formatted for C++ so use this instead?
+ @type carg: string
+ @ivar ctemplate: Unlike default, template isn't formatted for C++ so use this instead?
+ @type ctemplate: string
+ @ivar carr1_ref: Unlike default, arr1_ref isn't formatted for C++ so use this instead?
+ @type carr1_ref: string
+ @ivar carr2_ref: Unlike default, arr2_ref isn't formatted for C++ so use this instead?
+ @type carr2_ref: string
+ @ivar ccond_ref: Unlike default, cond_ref isn't formatted for C++ so use this instead?
+ @type ccond_ref: string
+ @ivar next_dup: Next duplicate member
+ @type next_dup: Member
+ @ivar is_manual_update: True if the member value is manually updated by the code
+ @type is_manual_update: bool
+ """
+ def __init__(self, element):
+ """
+ This constructor converts an XML element into a Member object.
+ Some sort of processing is applied to the various variables that are copied from the XML tag...
+ Seems to be trying to set reasonable defaults for certain types, and put things into C++ format generally.
+ @param prefix: An optional prefix used in some situations?
+ @type prefix: string
+ @return The expression formatted into a string?
+ @rtype: string?
+ """
+ assert element.tagName == 'add'
+ parent = element.parentNode
+ sisters = parent.getElementsByTagName('add')
+
+ # member attributes
+ self.name = element.getAttribute('name')
+ self.type = element.getAttribute('type')
+ self.arg = element.getAttribute('arg')
+ self.template = element.getAttribute('template')
+ self.arr1 = Expr(element.getAttribute('arr1'))
+ self.arr2 = Expr(element.getAttribute('arr2'))
+ self.cond = Expr(element.getAttribute('cond'))
+ self.func = element.getAttribute('function')
+ self.default = element.getAttribute('default')
+ self.orig_ver1 = element.getAttribute('ver1')
+ self.orig_ver2 = element.getAttribute('ver2')
+ self.ver1 = version2number(element.getAttribute('ver1'))
+ self.ver2 = version2number(element.getAttribute('ver2'))
+ self.userver = userversion2number(element.getAttribute('userver'))
+ self.vercond = Expr(element.getAttribute('vercond'))
+ self.is_public = (element.getAttribute('public') == "1")
+ self.next_dup = None
+ self.is_manual_update = False
+ self.is_calculated = (element.getAttribute('calculated') == "1")
+
+ #Get description from text between start and end tags
+ if element.firstChild:
+ assert element.firstChild.nodeType == Node.TEXT_NODE
+ self.description = element.firstChild.nodeValue.strip()
+ else:
+ self.description = "Unknown."
+
+ # Format default value so that it can be used in a C++ initializer list
+ if not self.default and (not self.arr1.lhs and not self.arr2.lhs):
+ if self.type in ["unsigned int", "unsigned short", "byte", "int", "short", "char"]:
+ self.default = "0"
+ elif self.type == "bool":
+ self.default = "false"
+ elif self.type in ["Ref", "Ptr"]:
+ self.default = "NULL"
+ elif self.type in "float":
+ self.default = "0.0"
+ elif self.type == "HeaderString":
+ pass
+ elif self.type == "Char8String":
+ pass
+ elif self.type == "StringOffset":
+ self.default = "-1";
+ elif self.type in basic_names:
+ self.default = "0"
+ elif self.type in flag_names or self.type in enum_names:
+ self.default = "0"
+ if self.default:
+ if self.default[0] == '(' and self.default[-1] == ')':
+ self.default = self.default[1:-1]
+ if self.arr1.lhs: # handle static array types
+ if self.arr1.lhs.isdigit():
+ sep = (',(%s)'%class_name(self.type))
+ self.default = self.arr1.lhs + sep + sep.join(self.default.split(' ', int(self.arr1.lhs)))
+ elif self.type == "string" or self.type == "IndexString":
+ self.default = "\"" + self.default + "\""
+ elif self.type == "float":
+ self.default += "f"
+ elif self.type in ["Ref", "Ptr", "bool", "Vector3"]:
+ pass
+ elif self.default.find(',') != -1:
+ pass
+ else:
+ self.default = "(%s)%s"%(class_name(self.type), self.default)
+
+ # calculate other stuff
+ self.uses_argument = (self.cond.lhs == '(ARG)' or self.arr1.lhs == '(ARG)' or self.arr2.lhs == '(ARG)')
+ self.type_is_native = native_types.has_key(self.name) # true if the type is implemented natively
+
+ # calculate stuff from reference to previous members
+ # true if this is a duplicate of a previously declared member
+ self.is_duplicate = False
+ self.arr2_dynamic = False # true if arr2 refers to an array
+ sis = element.previousSibling
+ while sis:
+ if sis.nodeType == Node.ELEMENT_NODE:
+ sis_name = sis.getAttribute('name')
+ if sis_name == self.name:
+ self.is_duplicate = True
+ sis_arr1 = Expr(sis.getAttribute('arr1'))
+ sis_arr2 = Expr(sis.getAttribute('arr2'))
+ if sis_name == self.arr2.lhs and sis_arr1.lhs:
+ self.arr2_dynamic = True
+ sis = sis.previousSibling
+
+ # calculate stuff from reference to next members
+ self.arr1_ref = [] # names of the attributes it is a (unmasked) size of
+ self.arr2_ref = [] # names of the attributes it is a (unmasked) size of
+ self.cond_ref = [] # names of the attributes it is a condition of
+ sis = element.nextSibling
+ while sis != None:
+ if sis.nodeType == Node.ELEMENT_NODE:
+ sis_name = sis.getAttribute('name')
+ sis_arr1 = Expr(sis.getAttribute('arr1'))
+ sis_arr2 = Expr(sis.getAttribute('arr2'))
+ sis_cond = Expr(sis.getAttribute('cond'))
+ if sis_arr1.lhs == self.name and (not sis_arr1.rhs or sis_arr1.rhs.isdigit()):
+ self.arr1_ref.append(sis_name)
+ if sis_arr2.lhs == self.name and (not sis_arr2.rhs or sis_arr2.rhs.isdigit()):
+ self.arr2_ref.append(sis_name)
+ if sis_cond.lhs == self.name:
+ self.cond_ref.append(sis_name)
+ sis = sis.nextSibling
+
+ # C++ names
+ self.cname = member_name(self.name)
+ self.ctype = class_name(self.type)
+ self.carg = member_name(self.arg)
+ self.ctemplate = class_name(self.template)
+ self.carr1_ref = [member_name(n) for n in self.arr1_ref]
+ self.carr2_ref = [member_name(n) for n in self.arr2_ref]
+ self.ccond_ref = [member_name(n) for n in self.cond_ref]
+
+ # construction
+ # don't construct anything that hasn't been declared
+ # don't construct if it has no default
+ def code_construct(self):
+ if self.default and not self.is_duplicate:
+ return "%s(%s)"%(self.cname, self.default)
+
+ # declaration
+ def code_declare(self, prefix = ""): # prefix is used to tag local variables only
+ result = self.ctype
+ suffix1 = ""
+ suffix2 = ""
+ keyword = ""
+ if not self.is_duplicate: # is dimension for one or more arrays
+ if self.arr1_ref:
+ if not self.arr1 or not self.arr1.lhs: # Simple Scalar
+ keyword = "mutable "
+ elif self.arr2_ref: # 1-dimensional dynamic array
+ keyword = "mutable "
+ elif self.is_calculated:
+ keyword = "mutable "
+
+ if self.ctemplate:
+ if result != "*":
+ result += "<%s >"%self.ctemplate
+ else:
+ result = "%s *"%self.ctemplate
+ if self.arr1.lhs:
+ if self.arr1.lhs.isdigit():
+ if self.arr2.lhs and self.arr2.lhs.isdigit():
+ result = "array< %s, array<%s,%s > >"%(self.arr1.lhs, self.arr2.lhs, result)
+ else:
+ result = "array<%s,%s >"%(self.arr1.lhs, result)
+ else:
+ if self.arr2.lhs and self.arr2.lhs.isdigit():
+ result = "vector< array<%s,%s > >"%(self.arr2.lhs, result)
+ else:
+ if self.arr2.lhs:
+ result = "vector< vector<%s > >"%result
+ else:
+ result = "vector<%s >"%result
+ result = keyword + result + " " + prefix + self.cname + suffix1 + suffix2 + ";"
+ return result
+
+ def getter_declare(self, scope = "", suffix = ""):
+ ltype = self.ctype
+ if self.ctemplate:
+ if ltype != "*":
+ ltype += "<%s >"%self.ctemplate
+ else:
+ ltype = "%s *"%self.ctemplate
+ if self.arr1.lhs:
+ if self.arr1.lhs.isdigit():
+ ltype = "array<%s,%s > "%(self.arr1.lhs, ltype)
+ # ltype = ltype
+ else:
+ if self.arr2.lhs and self.arr2.lhs.isdigit():
+ ltype = "vector< array<%s,%s > >"%(self.arr2.lhs, ltype)
+ else:
+ ltype = "vector<%s >"%ltype
+ if self.arr2.lhs:
+ if self.arr2.lhs.isdigit():
+ if self.arr1.lhs.isdigit():
+ ltype = "array<%s,%s >"%(self.arr2.lhs,ltype)
+ # ltype = ltype
+ else:
+ ltype = "vector<%s >"%ltype
+ result = ltype + " " + scope + "Get" + self.cname[0:1].upper() + self.cname[1:] + "() const" + suffix
+ return result
+
+ def setter_declare(self, scope = "", suffix = ""):
+ ltype = self.ctype
+ if self.ctemplate:
+ if ltype != "*":
+ ltype += "<%s >"%self.ctemplate
+ else:
+ ltype = "%s *"%self.ctemplate
+ if self.arr1.lhs:
+ if self.arr1.lhs.isdigit():
+ # ltype = "const %s&"%ltype
+ if self.arr2.lhs and self.arr2.lhs.isdigit():
+ ltype = "const array< %s, array<%s,%s > >&"%(self.arr1.lhs,self.arr2.lhs, ltype)
+ else:
+ ltype = "const array<%s,%s >& "%(self.arr1.lhs,ltype)
+
+ else:
+ if self.arr2.lhs and self.arr2.lhs.isdigit():
+ ltype = "const vector< array<%s,%s > >&"%(self.arr2.lhs, ltype)
+ else:
+ ltype = "const vector<%s >&"%ltype
+ else:
+ if not self.type in basic_names:
+ ltype = "const %s &"%ltype
+
+ result = "void " + scope + "Set" + self.cname[0:1].upper() + self.cname[1:] + "( " + ltype + " value )" + suffix
+ return result
+
+class Version:
+ def __init__(self, element):
+ self.num = element.getAttribute('num')
+ self.description = element.firstChild.nodeValue.strip()
+
+class Basic:
+ def __init__(self, element):
+ global native_types
+
+ self.name = element.getAttribute('name')
+ assert(self.name) # debug
+ self.cname = class_name(self.name)
+ self.niflibtype = element.getAttribute('niflibtype')
+ if element.firstChild and element.firstChild.nodeType == Node.TEXT_NODE:
+ self.description = element.firstChild.nodeValue.strip()
+ else:
+ self.description = "Unknown."
+
+ self.count = element.getAttribute('count')
+
+ self.is_link = False
+ self.is_crossref = False
+ self.has_links = False
+ self.has_crossrefs = False
+
+ if self.niflibtype:
+ native_types[self.name] = self.niflibtype
+ if self.niflibtype == "Ref":
+ self.is_link = True
+ self.has_links = True
+ if self.niflibtype == "*":
+ self.is_crossref = True
+ self.has_crossrefs = True
+
+ self.template = (element.getAttribute('istemplate') == "1")
+ self.options = []
+
+class Enum(Basic):
+ def __init__(self, element):
+ Basic.__init__(self, element)
+
+ self.storage = element.getAttribute('storage')
+ #find the Niflib type of the storage
+ self.storage = basic_types[self.storage].niflibtype
+ self.description = element.firstChild.nodeValue.strip()
+
+ self.niflibtype = self.cname
+ native_types[self.name] = self.niflibtype
+
+ # Locate all special enumeration options
+ for option in element.getElementsByTagName('option'):
+ x = Option(option)
+ self.options.append(x)
+
+class Flag(Enum):
+ def __init__(self, element):
+ Enum.__init__(self, element)
+ for option in self.options:
+ option.bit = option.value
+ option.value = 1 << int(option.value)
+
+class Compound(Basic):
+ # create a compound type from the XML attributes
+ def __init__(self, element):
+ Basic.__init__(self, element)
+
+ #the relative path to files in the gen folder
+ self.gen_file_prefix = ""
+ #the relative path to files in the obj folder
+ self.obj_file_prefix = "../obj/"
+ #the relative path to files in the root folder
+ self.root_file_prefix = "../"
+
+ self.members = [] # list of all members (list of Member)
+ self.argument = False # does it use an argument?
+
+ # store all attribute data & calculate stuff
+ for member in element.getElementsByTagName('add'):
+ x = Member(member)
+ #***********************
+ #** NIFLIB HACK BEGIN **
+ #***********************
+ if self.name == "BoundingVolume" and x.name == "Union":
+ # ignore this one because niflib cannot handle
+ # recursively defined structures... so we remove
+ # this one to avoid the problem
+ # as a result a minority of nifs won't load
+ continue
+ #*********************
+ #** NIFLIB HACK END **
+ #*********************
+ self.members.append(x)
+
+ # detect templates
+ #if x.type == 'TEMPLATE':
+ # self.template = True
+ #if x.template == 'TEMPLATE':
+ # self.template = True
+
+ # detect argument
+ if x.uses_argument:
+ self.argument = True
+ else:
+ self.argument = False
+
+ # detect links & crossrefs
+ y = None
+ try:
+ y = basic_types[x.type]
+ except KeyError:
+ try:
+ y = compound_types[x.type]
+ except KeyError:
+ pass
+ if y:
+ if y.has_links:
+ self.has_links = True
+ if y.has_crossrefs:
+ self.has_crossrefs = True
+
+ # create duplicate chains for items that need it (only valid in current object scope)
+ # prefer to use iterators to avoid O(n^2) but I dont know how to reset iterators
+ for x in self.members:
+ atx = False
+ for y in self.members:
+ if atx:
+ if x.name == y.name: # duplicate
+ x.next_dup = y
+ break
+ elif x == y:
+ atx = True
+
+ def code_construct(self):
+ # constructor
+ result = ''
+ first = True
+ for y in self.members:
+ y_code_construct = y.code_construct()
+ if y_code_construct:
+ if not first:
+ result += ', ' + y_code_construct
+ else:
+ result += ' : ' + y_code_construct
+ first = False
+ return result
+
+ def code_include_h(self):
+ if self.niflibtype: return ""
+
+ result = ""
+
+ # include all required structures
+ used_structs = []
+ for y in self.members:
+ file_name = None
+ if y.type != self.name:
+ if y.type in compound_names:
+ if not compound_types[y.type].niflibtype:
+ file_name = "%s%s.h"%(self.gen_file_prefix, y.ctype)
+ elif y.type in basic_names:
+ if basic_types[y.type].niflibtype == "Ref":
+ file_name = "%sRef.h"%(self.root_file_prefix)
+ if file_name and file_name not in used_structs:
+ used_structs.append( file_name )
+ if used_structs:
+ result += "\n// Include structures\n"
+ for file_name in used_structs:
+ result += '#include "%s"\n'%file_name
+ return result
+
+ def code_fwd_decl(self):
+ if self.niflibtype: return ""
+
+ result = ""
+
+ # forward declaration of blocks
+ used_blocks = []
+ for y in self.members:
+ if y.template in block_names and y.template != self.name:
+ if not y.ctemplate in used_blocks:
+ used_blocks.append( y.ctemplate )
+ if used_blocks:
+ result += '\n// Forward define of referenced NIF objects\n'
+ for fwd_class in used_blocks:
+ result += 'class %s;\n'%fwd_class
+
+ return result
+
+ def code_include_cpp(self, usedirs=False, gen_dir=None, obj_dir=None):
+ if self.niflibtype: return ""
+
+ if not usedirs:
+ gen_dir = self.gen_file_prefix
+ obj_dir = self.obj_file_prefix
+
+ result = ""
+
+ if self.name in compound_names:
+ result += '#include "%s%s.h"\n'%(gen_dir, self.cname)
+ elif self.name in block_names:
+ result += '#include "%s%s.h"\n'%(obj_dir, self.cname)
+ else: assert(False) # bug
+
+ # include referenced blocks
+ used_blocks = []
+ for y in self.members:
+ if y.template in block_names and y.template != self.name:
+ file_name = "%s%s.h"%(obj_dir, y.ctemplate)
+ if file_name not in used_blocks:
+ used_blocks.append( file_name )
+ if y.type in compound_names:
+ subblock = compound_types[y.type]
+ result += subblock.code_include_cpp(True, gen_dir, obj_dir)
+ for terminal in y.cond.get_terminals():
+ if terminal in block_types:
+ used_blocks.append("%s%s.h"%(obj_dir, terminal))
+ for file_name in sorted(set(used_blocks)):
+ result += '#include "%s"\n'%file_name
+
+ return result
+
+ # find member by name
+ def find_member(self, name, inherit=False):
+ for y in self.members:
+ if y.name == name:
+ return y
+ return None
+
+ # find first reference of name in class
+ def find_first_ref(self, name):
+ for y in self.members:
+ if y.arr1 and y.arr1.lhs == name:
+ return y
+ elif y.arr2 and y.arr2.lhs == name:
+ return y
+ return None
+
+
+class Block(Compound):
+ def __init__(self, element):
+ Compound.__init__(self, element)
+ #the relative path to files in the gen folder
+ self.gen_file_prefix = "../gen/"
+ #the relative path to files in the obj folder
+ self.obj_file_prefix = ""
+
+ self.is_ancestor = (element.getAttribute('abstract') == "1")
+ inherit = element.getAttribute('inherit')
+ if inherit:
+ self.inherit = block_types[inherit]
+ else:
+ self.inherit = None
+ self.has_interface = (element.getElementsByTagName('interface') != [])
+
+ def code_include_h(self):
+ result = ""
+ if self.inherit:
+ result += '#include "%s.h"\n'%self.inherit.cname
+ else:
+ result += """#include "../RefObject.h"
+#include "../Type.h"
+#include "../Ref.h"
+#include "../nif_basic_types.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include """
+ result += Compound.code_include_h(self)
+ return result
+
+ # find member by name
+ def find_member(self, name, inherit=False):
+ ret = Compound.find_member(self, name)
+ if not ret and inherit and self.inherit:
+ ret = self.inherit.find_member(name, inherit)
+ return ret
+
+ # find first reference of name in class
+ def find_first_ref(self, name):
+ ret = None
+ if self.inherit:
+ ret = self.inherit.find_first_ref(name)
+ if not ret:
+ ret = Compound.find_first_ref(self, name)
+ return ret
+
+#
+# import elements into our code generating classes
+#
+
+# import via "import nifxml" from .
+if os.path.exists("nif.xml"):
+ doc = parse("nif.xml")
+# import via "import docsys" from ..
+elif os.path.exists("docsys/nif.xml"):
+ doc = parse("docsys/nif.xml")
+# new submodule system
+elif os.path.exists("nifxml/nif.xml"):
+ doc = parse("nifxml/nif.xml")
+else:
+ raise ImportError("nif.xml not found")
+
+for element in doc.getElementsByTagName('version'):
+ x = Version(element)
+ version_types[x.num] = x
+ version_names.append(x.num)
+
+for element in doc.getElementsByTagName('basic'):
+ x = Basic(element)
+ assert not basic_types.has_key(x.name)
+ basic_types[x.name] = x
+ basic_names.append(x.name)
+
+for element in doc.getElementsByTagName('enum'):
+ x = Enum(element)
+ assert not enum_types.has_key(x.name)
+ enum_types[x.name] = x
+ enum_names.append(x.name)
+
+for element in doc.getElementsByTagName('bitflags'):
+ x = Flag(element)
+ assert not flag_types.has_key(x.name)
+ flag_types[x.name] = x
+ flag_names.append(x.name)
+
+for element in doc.getElementsByTagName("compound"):
+ x = Compound(element)
+ assert not compound_types.has_key(x.name)
+ compound_types[x.name] = x
+ compound_names.append(x.name)
+
+for element in doc.getElementsByTagName("niobject"):
+ x = Block(element)
+ assert not block_types.has_key(x.name)
+ block_types[x.name] = x
+ block_names.append(x.name)
diff --git a/build/docsys/nifxml_doc.py b/build/docsys/nifxml_doc.py
new file mode 100644
index 00000000..46822a02
--- /dev/null
+++ b/build/docsys/nifxml_doc.py
@@ -0,0 +1,554 @@
+# nifxml_doc.py
+#
+# This script generates HTML documentation from the XML file.
+#
+# --------------------------------------------------------------------------
+# Command line options
+#
+# -p /path/to/doc : specifies the path where HTML documentation must be created
+#
+# --------------------------------------------------------------------------
+# ***** BEGIN LICENSE BLOCK *****
+#
+# Copyright (c) 2005, 2006, 2007 NIF File Format Library and Tools
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution.
+#
+# * Neither the name of the NIF File Format Library and Tools
+# project nor the names of its contributors may be used to endorse
+# or promote products derived from this software without specific
+# prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# ***** END LICENCE BLOCK *****
+# --------------------------------------------------------------------------
+
+from nifxml import *
+from distutils.dir_util import mkpath
+import os
+import itertools
+
+#
+# global data
+#
+
+ROOT_DIR = "."
+
+
+prev = ""
+for i in sys.argv:
+ if prev == "-p":
+ ROOT_DIR = i
+ prev = i
+
+#
+# Sort Name Lists
+#
+
+block_names.sort()
+compound_names.sort()
+basic_names.sort()
+enum_names.sort()
+flag_names.sort()
+
+def tohex(value, nbytes=4):
+ """Improved version of hex."""
+ return ("0x%%0%dX" % (2*nbytes)) % (long(str(value)) & (2**(nbytes*8)-1))
+
+def ListAttributes( compound ):
+ attr_list = ""
+ count = 0
+
+ #Create Attribute List
+ for a in compound.members:
+ temp.set_var( "attr-name", a.name )
+ attr_type = '%s '%(a.type, a.type)
+ if a.template:
+ attr_type += '<%s >'%(a.template.replace("\\", "_"), a.template)
+
+ temp.set_var( "attr-type", attr_type )
+ temp.set_var( "attr-arg", a.arg )
+ temp.set_var( "attr-arr1", a.arr1.lhs )
+ temp.set_var( "attr-arr2", a.arr2.lhs )
+ cond_string = a.cond.code("", brackets = False)
+ if cond_string:
+ temp.set_var( "attr-cond", cond_string )
+ else:
+ temp.set_var( "attr-cond", "" )
+
+ if count % 2 == 0:
+ temp.set_var( "row-class", "reg0" )
+ else:
+ temp.set_var( "row-class", "reg1" )
+
+ temp.set_var( "attr-desc", a.description.replace("\n", " ") )
+
+ temp.set_var( "attr-from", a.orig_ver1 )
+ temp.set_var( "attr-to", a.orig_ver2 )
+
+ attr_list += temp.parse( "templates/attr_row.html" )
+
+ count += 1
+
+ return attr_list
+
+#
+# Generate Version List Page
+#
+
+temp = Template()
+temp.set_var( "title", "NIF File Format Versions" )
+
+#List each Version with Description
+
+count = 0
+version_list = ""
+for n in version_names:
+ x = version_types[n]
+
+ if count % 2 == 0:
+ temp.set_var( "row-class", "reg0" )
+ else:
+ temp.set_var( "row-class", "reg1" )
+
+ temp.set_var( "list-name", x.num )
+ temp.set_var( "list-desc", x.description.replace("\n", " ") )
+
+ version_list += temp.parse( "templates/version_row.html" )
+
+ count += 1
+
+
+temp.set_var( "list", version_list )
+
+temp.set_var( "contents", temp.parse( "templates/version_list.html") )
+
+f = file(ROOT_DIR + '/doc/version_list.html', 'w')
+f.write( temp.parse( "templates/main.html" ) )
+f.close()
+
+
+#
+# Generate Basic List Page
+#
+
+temp = Template()
+temp.set_var( "title", "Basic Data Types" )
+
+#List each Basic Type with Description
+
+count = 0
+basic_list = ""
+for n in basic_names:
+ x = basic_types[n]
+
+ if count % 2 == 0:
+ temp.set_var( "row-class", "reg0" )
+ else:
+ temp.set_var( "row-class", "reg1" )
+
+ temp.set_var( "list-name", x.name )
+ temp.set_var( "list-desc", x.description.replace("\n", " ") )
+
+ basic_list += temp.parse( "templates/list_row.html" )
+
+ count += 1
+
+
+temp.set_var( "list", basic_list )
+
+temp.set_var( "contents", temp.parse( "templates/list.html") )
+
+f = file(ROOT_DIR + '/doc/basic_list.html', 'w')
+f.write( temp.parse( "templates/main.html" ) )
+f.close()
+
+
+
+#
+# Generate Basic Pages
+#
+
+count = 0
+for n in basic_names:
+ x = basic_types[n]
+
+ temp = Template()
+ temp.set_var( "title", x.name )
+ temp.set_var( "name", x.name )
+ temp.set_var( "description", x.description.replace("\n", " ") )
+ if count == "1":
+ temp.set_var( "count", "Yes
" )
+ else:
+ temp.set_var( "count", "No
" )
+
+ #Create Found In list
+ found_in = ""
+
+ for b in block_names:
+ for m in block_types[b].members:
+ if m.type == n:
+ found_in += "" + b + " \n"
+ break
+
+ for b in compound_names:
+ for m in compound_types[b].members:
+ if m.type == n:
+ found_in += "" + b + " \n"
+ break
+
+ temp.set_var( "found-in", found_in );
+
+ temp.set_var( "contents", temp.parse( "templates/basic.html") )
+
+ f = file(ROOT_DIR + '/doc/' + x.cname.replace('\\', '_') + '.html', 'w')
+ f.write( temp.parse( "templates/main.html" ) )
+ f.close()
+
+#
+# Generate Enum List Page
+#
+
+temp = Template()
+temp.set_var( "title", "Enum Data Types" )
+
+#List each Enum Type with Description
+
+count = 0
+enum_list = ""
+for n, x in itertools.chain(enum_types.iteritems(), flag_types.iteritems()):
+ if count % 2 == 0:
+ temp.set_var( "row-class", "reg0" )
+ else:
+ temp.set_var( "row-class", "reg1" )
+
+ temp.set_var( "list-name", x.name )
+ temp.set_var( "list-desc", x.description.replace("\n", " ") )
+
+ enum_list += temp.parse( "templates/list_row.html" )
+
+ count += 1
+
+
+temp.set_var( "list", enum_list )
+
+temp.set_var( "contents", temp.parse( "templates/list.html") )
+
+f = file(ROOT_DIR + '/doc/enum_list.html', 'w')
+f.write( temp.parse( "templates/main.html" ) )
+f.close()
+
+
+
+#
+# Generate Enum Pages
+#
+
+count = 0
+for n, x in itertools.chain(enum_types.iteritems(), flag_types.iteritems()):
+
+ temp = Template()
+ temp.set_var( "title", x.name )
+ temp.set_var( "name", x.name )
+ temp.set_var( "storage", x.storage )
+ temp.set_var( "description", x.description.replace("\n", " ") )
+
+ #Create Found In list
+ found_in = ""
+
+ for b in block_names:
+ for m in block_types[b].members:
+ if m.type == n:
+ found_in += "" + b + " \n"
+ break
+
+ for b in compound_names:
+ for m in compound_types[b].members:
+ if m.type == n:
+ found_in += "" + b + " \n"
+ break
+
+ temp.set_var( "found-in", found_in );
+
+ #Create Choice List
+
+ count = 0
+ choice_list = ""
+ for o in x.options:
+ if count % 2 == 0:
+ temp.set_var( "row-class", "reg0" )
+ else:
+ temp.set_var( "row-class", "reg1" )
+
+ # represent bit flags with hex
+ if (hasattr(o, "bit")):
+ temp.set_var( "enum-number", tohex(o.value) )
+ else:
+ temp.set_var( "enum-number", o.value )
+ temp.set_var( "enum-name", o.name )
+ temp.set_var( "enum-desc", o.description.replace("\n", " ") )
+
+ choice_list += temp.parse( "templates/enum_row.html" )
+
+ count += 1
+
+ temp.set_var( "choices", choice_list )
+
+ temp.set_var( "contents", temp.parse( "templates/enum.html") )
+
+ f = file(ROOT_DIR + '/doc/' + x.cname.replace("\\", "_") + '.html', 'w')
+ f.write( temp.parse( "templates/main.html" ) )
+ f.close()
+
+
+#
+# Generate Compound List Page
+#
+
+temp = Template()
+temp.set_var( "title", "Compound Data Types" )
+
+#List each Compound with Description
+
+count = 0
+compound_list = ""
+for n in compound_names:
+ x = compound_types[n]
+
+ if count % 2 == 0:
+ temp.set_var( "row-class", "reg0" )
+ else:
+ temp.set_var( "row-class", "reg1" )
+
+ temp.set_var( "list-name", x.name )
+ temp.set_var( "list-desc", x.description.replace("\n", " ") )
+
+ compound_list += temp.parse( "templates/list_row.html" )
+
+ count += 1
+
+
+temp.set_var( "list", compound_list )
+
+temp.set_var( "contents", temp.parse( "templates/list.html") )
+
+f = file(ROOT_DIR + '/doc/compound_list.html', 'w')
+f.write( temp.parse( "templates/main.html" ) )
+f.close()
+
+
+
+#
+# Generate Compound Pages
+#
+
+count = 0
+for n in compound_names:
+ x = compound_types[n]
+
+ temp = Template()
+ temp.set_var( "title", x.name )
+ temp.set_var( "name", x.name )
+ temp.set_var( "description", x.description.replace("\n", " ") )
+
+ #Create Found In list
+ found_in = ""
+
+ for b in block_names:
+ for m in block_types[b].members:
+ if m.type == n:
+ found_in += "" + b + " \n"
+ break
+
+ for b in compound_names:
+ for m in compound_types[b].members:
+ if m.type == n:
+ found_in += "" + b + " \n"
+ break
+
+ temp.set_var( "found-in", found_in );
+
+ #Create Attribute List
+ attr_list = ListAttributes( x)
+
+ temp.set_var( "attributes", attr_list )
+
+ temp.set_var( "contents", temp.parse( "templates/compound.html") )
+
+ f = file(ROOT_DIR + '/doc/' + x.cname.replace("\\", "_") + '.html', 'w')
+ f.write( temp.parse( "templates/main.html" ) )
+ f.close()
+
+#
+# Generate NiObject List Page
+#
+
+temp = Template()
+temp.set_var( "title", "NIF Object List" )
+
+#List each NiObject with Description
+
+count = 0
+niobject_list = ""
+for n in block_names:
+ x = block_types[n]
+
+ if count % 2 == 0:
+ temp.set_var( "row-class", "reg0" )
+ else:
+ temp.set_var( "row-class", "reg1" )
+
+ temp.set_var( "list-name", x.name )
+ temp.set_var( "list-desc", x.description.replace("\n", " ") )
+
+ niobject_list += temp.parse( "templates/list_row.html" )
+
+ count += 1
+
+
+temp.set_var( "list", niobject_list )
+
+temp.set_var( "niobject-contents", temp.parse( "templates/list.html") )
+temp.set_var( "contents", temp.parse( "templates/niobject_nav.html") )
+
+f = file(ROOT_DIR + '/doc/niobject_list.html', 'w')
+f.write( temp.parse( "templates/main.html" ) )
+f.close()
+
+
+
+#
+# Generate NiObject Pages
+#
+
+count = 0
+for n in block_names:
+ x = block_types[n]
+
+ temp = Template()
+ temp.set_var( "title", x.name )
+ temp.set_var( "name", x.name )
+ temp.set_var( "description", x.description.replace("\n", " ") )
+
+ #Create Ancestor List
+
+ ancestors = []
+ b = x
+ while b:
+ ancestors.append(b)
+ b = b.inherit
+
+ ancestors.reverse()
+
+ #Create Attribute List
+ attr_list = ""
+ count = 0
+
+ for a in ancestors:
+ temp.set_var( "inherit", a.name )
+ attr_list += temp.parse( "templates/inherit_row.html" )
+
+ inherit_list = ""
+ inherit_list = ListAttributes( a )
+
+ attr_list += inherit_list
+
+ temp.set_var( "attributes", attr_list )
+
+ #Create Parent Of list
+ parent_of = ""
+ for b in block_names:
+ if block_types[b].inherit == x:
+ parent_of += "" + b + " \n"
+
+ temp.set_var( "parent-of", parent_of );
+
+ temp.set_var( "contents", temp.parse( "templates/niobject.html") )
+
+ f = file(ROOT_DIR + '/doc/' + x.cname.replace("\\", "_") + '.html', 'w')
+ f.write( temp.parse( "templates/main.html" ) )
+ f.close()
+
+#global value
+object_tree = ""
+
+def ListObjectTree( root ):
+
+ global object_tree
+
+ #get first line of description
+ lines = root.description.splitlines(False)
+ if len(lines) > 0:
+ desc = lines[0]
+ else:
+ desc = ""
+
+ #add a new list for this ancestor
+ object_tree += "" + root.name + " | " + desc + " \n"
+ """
+
+
+ NiObject
+ | Abstract block type.
+
+ """
+
+ #Create Child List
+
+ children = []
+ for b in block_names:
+ if block_types[b].inherit == root:
+ children.append(block_types[b])
+
+ if len(children) > 0:
+ object_tree += "\n"
+
+ for c in children:
+ ListObjectTree(c)
+
+ object_tree += " \n"
+
+#
+# Generate NiObject Hierarchy Page
+#
+
+temp = Template()
+temp.set_var( "title", "NIF Object Hierarchy" )
+
+# Build Tree
+
+object_tree = ""
+ListObjectTree( block_types["NiObject"] )
+temp.set_var( "object-tree", object_tree )
+
+
+temp.set_var( "niobject-contents", temp.parse( "templates/hierarchy.html") )
+temp.set_var( "contents", temp.parse( "templates/niobject_nav.html") )
+
+f = file(ROOT_DIR + '/doc/index.html', 'w')
+f.write( temp.parse( "templates/main.html" ) )
+f.close()
+
diff --git a/build/docsys/templates/attr_row.html b/build/docsys/templates/attr_row.html
new file mode 100644
index 00000000..e72a4daf
--- /dev/null
+++ b/build/docsys/templates/attr_row.html
@@ -0,0 +1,11 @@
+
+ {attr-name}
+ {attr-type}
+ {attr-arg}
+ {attr-arr1}
+ {attr-arr2}
+ {attr-cond}
+ {attr-desc}
+ {attr-from}
+ {attr-to}
+
diff --git a/build/docsys/templates/basic.html b/build/docsys/templates/basic.html
new file mode 100644
index 00000000..d0aa9be7
--- /dev/null
+++ b/build/docsys/templates/basic.html
@@ -0,0 +1,10 @@
+{name}
+{description}
+
+Can Be Used As Array Size
+{count}
+
+Found In
+
\ No newline at end of file
diff --git a/build/docsys/templates/class.html b/build/docsys/templates/class.html
new file mode 100644
index 00000000..4714324f
--- /dev/null
+++ b/build/docsys/templates/class.html
@@ -0,0 +1,58 @@
+{name}
+{description}
+Attribute of...
+
+Attributes
+
+
+Name
+Type
+Arg
+Arr1
+
+Arr2
+Cond
+Description
+From To
+
+
+Num Keys
+uint
+
+
+
+
+
+Number of keys in the array.
+Any Any
+
+Interpolation
+KeyType
+
+
+
+Num Keys != 0
+
+The key type.
+Any Any
+
+Keys
+Key
+Interpolation
+Num Keys
+
+
+The keys.
+
+Any Any
+
+
+ NIF File Format Documentation
+ NIF Objects | Compound Types | Enum Types | Basic Types | File Versions
+ {contents}
+
\ No newline at end of file
diff --git a/build/docsys/templates/compound.html b/build/docsys/templates/compound.html
new file mode 100644
index 00000000..b0a4be36
--- /dev/null
+++ b/build/docsys/templates/compound.html
@@ -0,0 +1,22 @@
+
\ No newline at end of file
diff --git a/build/docsys/templates/enum.html b/build/docsys/templates/enum.html
new file mode 100644
index 00000000..47575ad3
--- /dev/null
+++ b/build/docsys/templates/enum.html
@@ -0,0 +1,20 @@
+
\ No newline at end of file
diff --git a/build/docsys/templates/enum_row.html b/build/docsys/templates/enum_row.html
new file mode 100644
index 00000000..ff6ec55c
--- /dev/null
+++ b/build/docsys/templates/enum_row.html
@@ -0,0 +1,5 @@
+
diff --git a/build/docsys/templates/hierarchy.html b/build/docsys/templates/hierarchy.html
new file mode 100644
index 00000000..5948453b
--- /dev/null
+++ b/build/docsys/templates/hierarchy.html
@@ -0,0 +1,2 @@
+
\ No newline at end of file
diff --git a/build/docsys/templates/inherit_row.html b/build/docsys/templates/inherit_row.html
new file mode 100644
index 00000000..f19d2166
--- /dev/null
+++ b/build/docsys/templates/inherit_row.html
@@ -0,0 +1,3 @@
+
diff --git a/build/docsys/templates/list.html b/build/docsys/templates/list.html
new file mode 100644
index 00000000..74ec048a
--- /dev/null
+++ b/build/docsys/templates/list.html
@@ -0,0 +1,8 @@
+
\ No newline at end of file
diff --git a/build/docsys/templates/list_row.html b/build/docsys/templates/list_row.html
new file mode 100644
index 00000000..88fb91a2
--- /dev/null
+++ b/build/docsys/templates/list_row.html
@@ -0,0 +1,4 @@
+
diff --git a/build/docsys/templates/main.html b/build/docsys/templates/main.html
new file mode 100644
index 00000000..93bfa21f
--- /dev/null
+++ b/build/docsys/templates/main.html
@@ -0,0 +1,13 @@
+
+
+