diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..19e45c6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,14 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# Generated html
+*.html
+
+# VS Code
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
diff --git a/.project b/.project
deleted file mode 100644
index d8d55d7..0000000
--- a/.project
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
- nifdocsys
-
-
-
-
-
- org.python.pydev.PyDevBuilder
-
-
-
-
-
- org.python.pydev.pythonNature
-
-
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..f288702
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+ .
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/__init__.py b/__init__.py
index 596cb5c..e81ca65 100644
--- a/__init__.py
+++ b/__init__.py
@@ -1 +1,7 @@
__all__ = ["nifxml"]
+
+from .nifxml import parse_xml
+from .nifxml import Member, Version, Basic, Compound, Block, Enum, Flag
+from .nifxml import TYPES_VERSION, TYPES_BASIC, TYPES_BLOCK, TYPES_COMPOUND, TYPES_ENUM, TYPES_FLAG, TYPES_NATIVE
+from .nifxml import NAMES_VERSION, NAMES_BASIC, NAMES_BLOCK, NAMES_COMPOUND, NAMES_ENUM, NAMES_FLAG
+from .nifxml import class_name, define_name, member_name, version2number, scanBrackets
diff --git a/doc/__init__.py b/doc/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/doc/favicon.ico b/doc/favicon.ico
index fc12e79..e8740d4 100644
Binary files a/doc/favicon.ico and b/doc/favicon.ico differ
diff --git a/doc/docsys.css b/doc/nifdoc.css
similarity index 53%
rename from doc/docsys.css
rename to doc/nifdoc.css
index 018254b..9d3a235 100644
--- a/doc/docsys.css
+++ b/doc/nifdoc.css
@@ -2,26 +2,31 @@ 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 { color: #2087bb; text-decoration: none;}
+a:link { color: #2087bb; text-decoration: none;}
+a:visited { color: #2087bb; text-decoration: none;}
a:active { text-decoration: none;}
a:hover { color: #DD6900; text-decoration: underline;}
+table { width: 100%; }
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:link { text-decoration: none; color: #2087bb; background: transparent; }
+a:visited { text-decoration: none; color: #2087bb; 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; }
+tr { background: #DDDDDD; color: black; }
+tr.odd { background: #DDDDDD; color: black; }
+tr.even { background: #EEEEEE; color: black; }
+td.aname { white-space: nowrap; }
+td.atype { white-space: nowrap; }
+td.aarg { font-size: small; font-family: 'Courier New', Courier, monospace; }
+td.aarr1 { font-size: small; font-family: 'Courier New', Courier, monospace; }
+td.aarr2 { font-size: small; font-family: 'Courier New', Courier, monospace; }
+td.acond { font-size: small; font-family: 'Courier New', Courier, monospace; }
diff --git a/doc/nifdoc_tmpl.py b/doc/nifdoc_tmpl.py
new file mode 100644
index 0000000..57187e8
--- /dev/null
+++ b/doc/nifdoc_tmpl.py
@@ -0,0 +1,207 @@
+#!/usr/bin/python
+"""
+ This file is part of nifxml
+ Copyright (c) 2017 NifTools
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, version 3.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+"""
+from __future__ import unicode_literals
+
+UL_ITEM = '\n'
+LI_LINK = '\t{1} \n'
+LI_LINK_DESC = '{1} | {2} \n'
+TYPE_LINK = '{1} '
+TMPL_LINK = '<{1} >'
+
+MAIN_BEG = """
+
+
+\tNIF File Format Documentation - {title}
+\t
+\t
+
+"""
+MAIN_END = """
+\tNIF Objects | Compound Types
+\t| Enum Types | Basic Types | File Versions
+\t{contents}
+
+"""
+
+# Main HTML template with and without H1 heading
+MAIN_H1 = MAIN_BEG + 'NIF File Format Documentation ' + MAIN_END
+MAIN_NO_H1 = MAIN_BEG + MAIN_END
+
+# Attribute row with metadata
+ATTR = """
+
+\t{attr_name}
+\t{attr_type}
+\t{attr_arg}
+\t{attr_arr1}
+\t{attr_arr2}
+\t{attr_cond}
+\t{attr_desc}
+\t{attr_from}
+\t{attr_to}
+
+"""
+
+# Attribute row without metadata
+ATTR_NO_META = """
+
+\t{attr_name}
+\t{attr_type}
+\t{attr_desc}
+
+"""
+
+# List for Found In
+FOUND_IN = """
+Found In
+
+"""
+
+# List for Parent Of
+PARENT_OF = """
+Parent Of
+"""
+
+# Basic layout
+BASIC = """
+{name}
+{description}
+
+Can Be Used As Array Size
+{count}
+""" + FOUND_IN
+
+# Block with metadata columns
+BLOCK = """
+{name}
+{description}
+Attributes
+
+\t
+\t\tName
+\t\tType
+\t\tArg
+\t\tArr1
+\t\tArr2
+\t\tCond
+\t\tDescription
+\t\tFrom To
+\t
+\t{attributes}
+
+"""
+
+# Block without metadata columns
+BLOCK_NM = """
+{name}
+{description}
+Attributes
+
+\t
+\t\tName
+\t\tType
+\t\tDescription
+\t
+\t{attributes}
+
+"""
+
+# Compound and NiObject with and without metadata columns
+COMPOUND = BLOCK + FOUND_IN
+NIOBJECT = BLOCK + PARENT_OF
+COMPOUND_NO_META = BLOCK_NM + FOUND_IN
+NIOBJECT_NO_META = BLOCK_NM + PARENT_OF
+
+# Object tree for index.html
+HIERARCHY = """
+"""
+
+# Object list for *_list.html
+LIST = """
+
+\t
+\t\t{list_header}
+\t\tDescription
+\t
+{list}
+
"""
+
+# Navbar
+NAV = """Hierarchy | List
+{title}
+"""
+
+# Nav plus contents
+NAV_LIST = NAV + LIST
+NAV_HIER = NAV + HIERARCHY
+
+# Enum layout
+ENUM = """
+{name}
+{description}
+
+Choices
+
+\t
+\t\tNumber
+\t\tName
+\t\tDescription
+\t
+\t{choices}
+
+
+Storage Type
+{storage}
+""" + FOUND_IN
+
+ENUM_ROW = """
+
+\t{enum_number}
+\t{enum_name}
+\t{enum_desc}
+
+"""
+
+INHERIT_ROW = """
+
+\tFrom {inherit}
+
+"""
+
+INHERIT_NO_META = """
+
+\tFrom {inherit}
+
+"""
+
+LIST_ROW = """
+
+\t{list_name}
+\t{list_desc}
+ """
+
+VERSION_ROW = """
+
+\t{list_name}
+\t{list_desc}
+
+"""
diff --git a/gen_niflib.py b/gen_niflib.py
index f7ce49f..2a4bfa3 100755
--- a/gen_niflib.py
+++ b/gen_niflib.py
@@ -7,10 +7,10 @@
# --------------------------------------------------------------------------
# Command line options
#
-# -p /path/to/niflib : specifies the path where niflib can be found
+# -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
@@ -55,17 +55,477 @@
#
# ***** END LICENSE BLOCK *****
# --------------------------------------------------------------------------
+"""
+@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 __future__ import unicode_literals
-from nifxml import *
+from textwrap import fill
from distutils.dir_util import mkpath
+import sys
import os
-import hashlib
+import io
import itertools
+from nifxml import Member, Compound, Block
+from nifxml import TYPES_BLOCK, TYPES_BASIC, TYPES_COMPOUND, TYPES_ENUM, TYPES_FLAG, TYPES_NATIVE
+from nifxml import NAMES_BLOCK, NAMES_BASIC, NAMES_COMPOUND
+from nifxml import scanBrackets, define_name, parse_xml
+
+
+# The relative path to the project root for compounds and NiObjects (Compound and Block)
+ROOT_FILE_PREFIX = "../"
+
+# The relative path to NiObject for compounds (Block and Compound)
+CMP_OBJ_FILE_PREFIX = "../obj/"
+CMP_GEN_FILE_PREFIX = ""
+
+# The relative path to compounds for NiObject (Compound and Block)
+BLK_GEN_FILE_PREFIX = "../gen/"
+BLK_OBJ_FILE_PREFIX = ""
+
+# The XML to niflib type mapping
+NATIVETYPES = {
+ 'bool' : 'bool',
+ 'byte' : 'byte',
+ 'uint' : 'unsigned int',
+ 'ulittle32' : 'unsigned int',
+ 'ushort' : 'unsigned short',
+ 'int' : 'int',
+ 'short' : 'short',
+ 'BlockTypeIndex' : 'unsigned short',
+ 'char' : 'byte',
+ 'FileVersion' : 'unsigned int',
+ 'Flags' : 'unsigned short',
+ 'float' : 'float',
+ 'hfloat' : 'hfloat',
+ 'HeaderString' : 'HeaderString',
+ 'LineString' : 'LineString',
+ 'Ptr' : '*',
+ 'Ref' : 'Ref',
+ 'StringOffset' : 'unsigned int',
+ 'StringIndex' : 'IndexString',
+ 'SizedString' : 'string',
+ 'string' : 'IndexString',
+ 'Color3' : 'Color3',
+ 'Color4' : 'Color4',
+ #'ByteColor3' : 'ByteColor3', # TODO: Niflib type
+ 'ByteColor4' : 'ByteColor4',
+ 'FilePath' : 'IndexString',
+ 'Vector3' : 'Vector3',
+ 'Vector4' : 'Vector4',
+ 'Quaternion' : 'Quaternion',
+ 'Matrix22' : 'Matrix22',
+ 'Matrix33' : 'Matrix33',
+ 'Matrix34' : 'Matrix34',
+ 'Matrix44' : 'Matrix44',
+ 'hkMatrix3' : 'InertiaMatrix',
+ 'ShortString' : 'ShortString',
+ 'Key' : 'Key',
+ 'QuatKey' : 'Key',
+ 'TexCoord' : 'TexCoord',
+ 'Triangle' : 'Triangle',
+ 'BSVertexData' : 'BSVertexData',
+ 'BSVertexDataSSE' : 'BSVertexData',
+ #'BSVertexDesc' : 'BSVertexDesc'
+}
+
+#
+# Member Patching
+#
+
+def member_code_construct(self):
+ """
+ Class construction
+ don't construct anything that hasn't been declared
+ don't construct if it has no default
+ """
+ if self.default and not self.is_duplicate:
+ return "%s(%s)"%(self.cname, self.default)
+
+def member_code_declare(self, prefix=""):
+ """
+ Class member declaration
+ 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 = "Niflib::array< %s, Niflib::array<%s,%s > >"%(self.arr1.lhs, self.arr2.lhs, result)
+ else:
+ result = "Niflib::array<%s,%s >"%(self.arr1.lhs, result)
+ else:
+ if self.arr2.lhs and self.arr2.lhs.isdigit():
+ result = "vector< Niflib::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 member_getter_declare(self, scope="", suffix=""):
+ """Getter member function declaration."""
+ 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 = "Niflib::array<%s,%s > "%(self.arr1.lhs, ltype)
+ # ltype = ltype
+ else:
+ if self.arr2.lhs and self.arr2.lhs.isdigit():
+ ltype = "vector< Niflib::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 = "Niflib::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 member_setter_declare(self, scope="", suffix=""):
+ """Setter member function declaration."""
+ 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 Niflib::array< %s, Niflib::array<%s,%s > >&"%(self.arr1.lhs, self.arr2.lhs, ltype)
+ else:
+ ltype = "const Niflib::array<%s,%s >& "%(self.arr1.lhs, ltype)
+ else:
+ if self.arr2.lhs and self.arr2.lhs.isdigit():
+ ltype = "const vector< Niflib::array<%s,%s > >&"%(self.arr2.lhs, ltype)
+ else:
+ ltype = "const vector<%s >&"%ltype
+ else:
+ if not self.type in NAMES_BASIC:
+ ltype = "const %s &"%ltype
+
+ result = "void " + scope + "Set" + self.cname[0:1].upper() + self.cname[1:] + "( " + ltype + " value )" + suffix
+ return result
+
+Member.code_construct = member_code_construct
+Member.code_declare = member_code_declare
+Member.getter_declare = member_getter_declare
+Member.setter_declare = member_setter_declare
+
+#
+# Compound Patching
+#
+
+def compound_code_construct(self):
+ # constructor
+ result = ''
+ first = True
+ for mem in self.members:
+ y_code_construct = mem.code_construct()
+ if y_code_construct:
+ if not first:
+ result += ', ' + y_code_construct
+ else:
+ result += ' : ' + y_code_construct
+ first = False
+ return result
+
+def compound_code_include_h(self):
+ if self.nativetype:
+ return ""
+
+ result = ""
+
+ # include all required structures
+ used_structs = []
+ for mem in self.members:
+ file_name = None
+ if mem.type != self.name:
+ if mem.type in NAMES_COMPOUND:
+ if not TYPES_COMPOUND[mem.type].nativetype:
+ file_name = "%s%s.h"%(self.gen_file_prefix, mem.ctype)
+ elif mem.type in NAMES_BASIC:
+ if TYPES_BASIC[mem.type].nativetype == "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 compound_code_fwd_decl(self):
+ if self.nativetype:
+ return ""
+ result = ""
+
+ # forward declaration of blocks
+ used_blocks = []
+ for mem in self.members:
+ if mem.template in NAMES_BLOCK and mem.template != self.name:
+ if not mem.ctemplate in used_blocks:
+ used_blocks.append( mem.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 compound_code_include_cpp_set(self, usedirs=False, gen_dir=None, obj_dir=None):
+ if self.nativetype:
+ return ""
+
+ if not usedirs:
+ gen_dir = self.gen_file_prefix
+ obj_dir = self.obj_file_prefix
+
+ result = []
+
+ if self.name in NAMES_COMPOUND:
+ result.append('#include "%s%s.h"\n'%(gen_dir, self.cname))
+ elif self.name in NAMES_BLOCK:
+ result.append('#include "%s%s.h"\n'%(obj_dir, self.cname))
+ else: assert False # bug
+
+ # include referenced blocks
+ used_blocks = []
+ for mem in self.members:
+ if mem.template in NAMES_BLOCK and mem.template != self.name:
+ file_name = '#include "%s%s.h"\n'%(obj_dir, mem.ctemplate)
+ if file_name not in used_blocks:
+ used_blocks.append( file_name )
+ if mem.type in NAMES_COMPOUND:
+ subblock = TYPES_COMPOUND[mem.type]
+ used_blocks.extend(subblock.code_include_cpp_set(True, gen_dir, obj_dir))
+ for terminal in mem.cond.get_terminals():
+ if terminal in TYPES_BLOCK:
+ used_blocks.append('#include "%s%s.h"\n'%(obj_dir, terminal))
+ for file_name in sorted(set(used_blocks)):
+ result.append(file_name)
+
+ return result
+
+def compound_code_include_cpp(self, usedirs=False, gen_dir=None, obj_dir=None):
+ return ''.join(self.code_include_cpp_set(True, gen_dir, obj_dir))
+
+
+Compound.root_file_prefix = ROOT_FILE_PREFIX
+Compound.gen_file_prefix = CMP_GEN_FILE_PREFIX
+Compound.obj_file_prefix = CMP_OBJ_FILE_PREFIX
+Compound.code_construct = compound_code_construct
+Compound.code_include_h = compound_code_include_h
+Compound.code_fwd_decl = compound_code_fwd_decl
+Compound.code_include_cpp_set = compound_code_include_cpp_set
+Compound.code_include_cpp = compound_code_include_cpp
+
+#
+# Block Patching
+#
+
+def block_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
+
+Block.gen_file_prefix = BLK_GEN_FILE_PREFIX
+Block.obj_file_prefix = BLK_OBJ_FILE_PREFIX
+Block.code_include_h = block_code_include_h
+
+#
+# Parse XML after patching classes
+#
+
+parse_xml(NATIVETYPES)
+
#
# global data
#
+COPYRIGHT_YEAR = 2017
+
+COPYRIGHT_NOTICE = r'''/* Copyright (c) {0}, NIF File Format Library and Tools
+All rights reserved. Please see niflib.h for license. */'''.format(COPYRIGHT_YEAR)
+
+INCL_GUARD = r'''
+#ifndef _{0}_H_
+#define _{0}_H_
+'''
+
+# Partially generated notice
+PARTGEN_NOTICE = COPYRIGHT_NOTICE + r'''
+
+//-----------------------------------NOTICE----------------------------------//
+// Some of this file is automatically filled in by a Python script. Only //
+// add custom code in the designated areas or it will be overwritten during //
+// the next update. //
+//-----------------------------------NOTICE----------------------------------//'''
+
+# Fully generated notice
+FULLGEN_NOTICE = COPYRIGHT_NOTICE + r'''
+
+//---THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT---//
+
+// To change this file, alter the gen_niflib.py script.'''
+
+# NiObject standard declaration
+CLASS_DECL = r'''/*! Constructor */
+NIFLIB_API {0}();
+
+/*! Destructor */
+NIFLIB_API virtual ~{0}();
+
+/*!
+ * A constant value which uniquly identifies objects of this type.
+ */
+NIFLIB_API static const Type TYPE;
+
+/*!
+ * A factory function used during file reading to create an instance of this type of object.
+ * \return A pointer to a newly allocated instance of this type of object.
+ */
+NIFLIB_API static NiObject * Create();
+
+/*!
+ * Summarizes the information contained in this object in English.
+ * \param[in] verbose Determines whether or not detailed information about large areas of data will be printed out.
+ * \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.
+ */
+NIFLIB_API virtual string asString( bool verbose = false ) const;
+
+/*!
+ * Used to determine the type of a particular instance of this object.
+ * \return The type constant for the actual type of the object.
+ */
+NIFLIB_API virtual const Type & GetType() const;'''
+
+# NiObject internals
+CLASS_INTL = r'''/*! NIFLIB_HIDDEN function. For internal use only. */
+NIFLIB_HIDDEN virtual void Read( istream& in, list & link_stack, const NifInfo & info );
+/*! NIFLIB_HIDDEN function. For internal use only. */
+NIFLIB_HIDDEN virtual void Write( ostream& out, const map & link_map, list & missing_link_stack, const NifInfo & info ) const;
+/*! NIFLIB_HIDDEN function. For internal use only. */
+NIFLIB_HIDDEN virtual void FixLinks( const map & objects, list & link_stack, list & missing_link_stack, const NifInfo & info );
+/*! NIFLIB_HIDDEN function. For internal use only. */
+NIFLIB_HIDDEN virtual list GetRefs() const;
+/*! NIFLIB_HIDDEN function. For internal use only. */
+NIFLIB_HIDDEN virtual list GetPtrs() const;'''
+
+# Compound standard declaration
+COMPOUND_DECL = r'''/*! Default Constructor */
+NIFLIB_API {0}();
+/*! Default Destructor */
+NIFLIB_API ~{0}();
+/*! Copy Constructor */
+NIFLIB_API {0}( const {0} & src );
+/*! Copy Operator */
+NIFLIB_API {0} & operator=( const {0} & src );'''
+
+# Enum stream implementation
+ENUM_IMPL = r'''//--{0}--//
+
+void NifStream( {0} & val, istream& in, const NifInfo & info ) {{
+ {1} temp;
+ NifStream( temp, in, info );
+ val = {0}(temp);
+}}
+
+void NifStream( {0} const & val, ostream& out, const NifInfo & info ) {{
+ NifStream( ({1})(val), out, info );
+}}
+
+ostream & operator<<( ostream & out, {0} const & val ) {{
+ switch ( val ) {{
+ {2}default: return out << "Invalid Value! - " << ({1})(val);
+ }}
+}}'''
+
+# Enum stream implementation switch case
+ENUM_IMPL_CASE = r'''case {0}: return out << "{1}";
+ '''
+
+
+# Custom Code section comments
+
+BEG_MISC = '//--BEGIN MISC CUSTOM CODE--//'
+BEG_HEAD = '//--BEGIN FILE HEAD CUSTOM CODE--//'
+BEG_FOOT = '//--BEGIN FILE FOOT CUSTOM CODE--//'
+BEG_PRE_READ = '//--BEGIN PRE-READ CUSTOM CODE--//'
+BEG_POST_READ = '//--BEGIN POST-READ CUSTOM CODE--//'
+BEG_PRE_WRITE = '//--BEGIN PRE-WRITE CUSTOM CODE--//'
+BEG_POST_WRITE = '//--BEGIN POST-WRITE CUSTOM CODE--//'
+BEG_PRE_STRING = '//--BEGIN PRE-STRING CUSTOM CODE--//'
+BEG_POST_STRING = '//--BEGIN POST-STRING CUSTOM CODE--//'
+BEG_PRE_FIXLINK = '//--BEGIN PRE-FIXLINKS CUSTOM CODE--//'
+BEG_POST_FIXLINK = '//--BEGIN POST-FIXLINKS CUSTOM CODE--//'
+BEG_CTOR = '//--BEGIN CONSTRUCTOR CUSTOM CODE--//'
+BEG_DTOR = '//--BEGIN DESTRUCTOR CUSTOM CODE--//'
+BEG_INCL = '//--BEGIN INCLUDE CUSTOM CODE--//'
+
+END_CUSTOM = '//--END CUSTOM CODE--//'
+
ROOT_DIR = ".."
BOOTSTRAP = False
GENIMPL = True
@@ -86,128 +546,677 @@
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
+TYPES_BLOCK["NiKeyframeData"].find_member("Num Rotation Keys").is_manual_update = True
+#TYPES_BLOCK["NiTriStripsData"].find_member("Num Triangles").is_manual_update = True
+
+
+ACTION_READ = 0
+ACTION_WRITE = 1
+ACTION_OUT = 2
+ACTION_FIXLINKS = 3
+ACTION_GETREFS = 4
+ACTION_GETPTRS = 5
+
+
+#
+# C++ code formatting functions
+#
+
+class CFile(io.TextIOWrapper):
+ """
+ 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, buffer, encoding='utf-8', errors=None, newline=None, line_buffering=False, write_through=False):
+ io.TextIOWrapper.__init__(self, buffer, encoding, errors, newline, line_buffering)
+ self.indent = 0
+ self.backslash_mode = False
+ self.guarding = False
+ self.namespaced = False
+
+ def end(self):
+ """
+ Closes any namespaces and include guards and closes the file.
+ """
+ if self.namespaced:
+ self.write('}\n')
+ self.namespaced = False
+ if self.guarding:
+ self.code('#endif')
+ self.guarding = False
+ self.close()
+
+ 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 is 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.encode('utf-8').decode('utf-8', 'strict'))
+
+ def guard(self, txt):
+ """
+ Begins an include guard scope for the file
+ @param txt: The unique identifier for the header.
+ @type txt: str
+ """
+ if self.guarding:
+ return
+ self.guarding = True
+ self.code( INCL_GUARD.format(txt) )
+
+ def namespace(self, txt):
+ """
+ Begins a namespace scope for the file
+ @param txt: The namespace.
+ @type txt: str
+ """
+ if self.namespaced:
+ return
+ self.namespaced = True
+ self.write( 'namespace {0} {{\n'.format(txt) )
+
+ def include(self, txt):
+ """
+ Includes a file
+ @param txt: The include filepath.
+ @type txt: str
+ """
+ if (txt.startswith('<') and txt.endswith('>')) or (txt.startswith('"') and txt.endswith('"')):
+ self.write( '#include {0}\n'.format(txt) )
+ else:
+ self.write( '#include "{0}"\n'.format(txt) )
+
+ 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 com in lines:
+ txt = txt + fill(com, 80) + "\n"
+
+ txt = txt.strip()
+ if not txt:
+ return
+
+ 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 com in lines:
+ self.code("// " + com)
+
+ 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 mem in block.members:
+ if not mem.is_duplicate:
+ if isinstance(block, Block):
+ if mem.is_public and prot_mode:
+ self.code('public:')
+ prot_mode = False
+ elif not mem.is_public and not prot_mode:
+ self.code('protected:')
+ prot_mode = True
+ self.comment(mem.description)
+ self.code(mem.code_declare())
+ if mem.func:
+ self.comment(mem.description)
+ self.code("%s %s() const;"%(mem.ctype, mem.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
+ lastuserver2 = None
+ lastcond = None
+ lastvercond = None
+ # stream name
+ if action == ACTION_READ:
+ stream = "in"
+ else:
+ stream = "out"
+
+ # preparation
+ 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
+ if block.has_arr():
+ self.code("unsigned int array_output_count = 0;")
+
+ 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 TYPES_BASIC:
+ subblock = TYPES_BASIC[y.type]
+ elif y.type in TYPES_COMPOUND:
+ subblock = TYPES_COMPOUND[y.type]
+ elif y.type in TYPES_ENUM:
+ subblock = TYPES_ENUM[y.type]
+ elif y.type in TYPES_FLAG:
+ subblock = TYPES_FLAG[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 lastuserver2 != y.userver2 or lastvercond != y_vercond:
+ # we must switch to a new version block
+ # close old version block
+ if lastver1 or lastver2 or lastuserver or lastuserver2 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.userver2 != None:
+ verexpr = "%s%s( info.userVersion2 == %s )"%(verexpr, concat, y.userver2)
+ 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 not y.arr1.lhs.isdigit():
+ 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 not y.arr2.lhs.isdigit():
+ 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 y.type in TYPES_NATIVE:
+ # 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] and y.is_abstract is False:
+ # 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:
+ cast = ""
+ if y.is_duplicate:
+ cast = "(%s&)" % y.ctype
+ self.code("NifStream( %s%s, %s, info );"%(cast, 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("WriteRef( StaticCast(%s), %s, info, link_map, missing_link_stack );"%(z, stream))
+ 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 = TYPES_COMPOUND[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
+ lastuserver2 = y.userver2
+ 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 not(lastuserver2 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;")
+
+ def getset_declare(self, block, prefix=""):
+ """
+ Declare getter and setter
+ 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()
+
#
# 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' )
+def extract_custom_code(name):
+ custom = {}
+ custom['MISC'] = []
+ custom['FILE HEAD'] = []
+ custom['FILE FOOT'] = []
+ custom['PRE-READ'] = []
+ custom['POST-READ'] = []
+ custom['PRE-WRITE'] = []
+ custom['POST-WRITE'] = []
+ custom['PRE-STRING'] = []
+ custom['POST-STRING'] = []
+ custom['PRE-FIXLINKS'] = []
+ custom['POST-FIXLINKS'] = []
+ custom['CONSTRUCTOR'] = []
+ custom['DESTRUCTOR'] = []
+
+ if os.path.isfile(name) is False:
+ custom['MISC'].append('\n')
+ custom['FILE HEAD'].append('\n')
+ custom['FILE FOOT'].append('\n')
+ custom['PRE-READ'].append('\n')
+ custom['POST-READ'].append('\n')
+ custom['PRE-WRITE'].append('\n')
+ custom['POST-WRITE'].append('\n')
+ custom['PRE-STRING'].append('\n')
+ custom['POST-STRING'].append('\n')
+ custom['PRE-FIXLINKS'].append('\n')
+ custom['POST-FIXLINKS'].append('\n')
+ custom['CONSTRUCTOR'].append('\n')
+ custom['DESTRUCTOR'].append('\n')
+ return custom
+
+ f = io.open(name, 'rt', 1, 'utf-8')
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:
+
+ for cln in lines:
+ if custom_flag is True:
+ if cln.find( END_CUSTOM ) != -1:
custom_flag = False
else:
- if not custom_lines[custom_name]:
- custom_lines[custom_name] = [l]
+ if not custom[custom_name]:
+ custom[custom_name] = [cln]
else:
- custom_lines[custom_name].append(l)
- if l.find( '//--BEGIN MISC CUSTOM CODE--//' ) != -1:
+ custom[custom_name].append(cln)
+ if cln.find( BEG_MISC ) != -1:
custom_flag = True
custom_name = 'MISC'
- elif l.find( '//--BEGIN FILE HEAD CUSTOM CODE--//' ) != -1:
+ elif cln.find( BEG_HEAD ) != -1:
custom_flag = True
custom_name = 'FILE HEAD'
- elif l.find( '//--BEGIN FILE FOOT CUSTOM CODE--//' ) != -1:
+ elif cln.find( BEG_FOOT ) != -1:
custom_flag = True
custom_name = 'FILE FOOT'
- elif l.find( '//--BEGIN PRE-READ CUSTOM CODE--//' ) != -1:
+ elif cln.find( BEG_PRE_READ ) != -1:
custom_flag = True
custom_name = 'PRE-READ'
- elif l.find( '//--BEGIN POST-READ CUSTOM CODE--//' ) != -1:
+ elif cln.find( BEG_POST_READ ) != -1:
custom_flag = True
custom_name = 'POST-READ'
- elif l.find( '//--BEGIN PRE-WRITE CUSTOM CODE--//' ) != -1:
+ elif cln.find( BEG_PRE_WRITE ) != -1:
custom_flag = True
custom_name = 'PRE-WRITE'
- elif l.find( '//--BEGIN POST-WRITE CUSTOM CODE--//' ) != -1:
+ elif cln.find( BEG_POST_WRITE ) != -1:
custom_flag = True
custom_name = 'POST-WRITE'
- elif l.find( '//--BEGIN PRE-STRING CUSTOM CODE--//' ) != -1:
+ elif cln.find( BEG_PRE_STRING ) != -1:
custom_flag = True
custom_name = 'PRE-STRING'
- elif l.find( '//--BEGIN POST-STRING CUSTOM CODE--//' ) != -1:
+ elif cln.find( BEG_POST_STRING ) != -1:
custom_flag = True
custom_name = 'POST-STRING'
- elif l.find( '//--BEGIN PRE-FIXLINKS CUSTOM CODE--//' ) != -1:
+ elif cln.find( BEG_PRE_FIXLINK ) != -1:
custom_flag = True
custom_name = 'PRE-FIXLINKS'
- elif l.find( '//--BEGIN POST-FIXLINKS CUSTOM CODE--//' ) != -1:
+ elif cln.find( BEG_POST_FIXLINK ) != -1:
custom_flag = True
custom_name = 'POST-FIXLINKS'
- elif l.find( '//--BEGIN CONSTRUCTOR CUSTOM CODE--//' ) != -1:
+ elif cln.find( BEG_CTOR ) != -1:
custom_flag = True
custom_name = 'CONSTRUCTOR'
- elif l.find( '//--BEGIN DESTRUCTOR CUSTOM CODE--//' ) != -1:
+ elif cln.find( BEG_DTOR ) != -1:
custom_flag = True
custom_name = 'DESTRUCTOR'
- elif l.find( '//--BEGIN INCLUDE CUSTOM CODE--//' ) != -1:
+ elif cln.find( BEG_INCL ) != -1:
custom_flag = True
custom_name = 'INCLUDE'
-
- return custom_lines
+ return custom
#
# Function to compare two files
#
-def OverwriteIfChanged( original_file, candidate_file ):
+def overwrite_if_changed( original_file, candidate_file ):
files_differ = False
if os.path.isfile( original_file ):
- f1 = file( original_file, 'r' )
- f2 = file( candidate_file, 'r' )
+ file1 = io.open( original_file, 'r' )
+ file2 = io.open( candidate_file, 'r' )
- s1 = f1.read()
- s2 = f2.read()
+ str1 = file1.read()
+ str2 = file2.read()
- f1.close()
- f2.close()
-
- if s1 != s2:
+ file1.close()
+ file2.close()
+
+ if str1 != str2:
files_differ = True
#remove original file
os.unlink( original_file )
@@ -217,7 +1226,7 @@ def OverwriteIfChanged( original_file, candidate_file ):
if files_differ:
#Files differ, so overwrite original with candidate
os.rename( candidate_file, original_file )
-
+
#
# generate compound code
#
@@ -228,479 +1237,354 @@ def OverwriteIfChanged( original_file, candidate_file ):
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]
-
+for n in NAMES_COMPOUND:
+ x = TYPES_COMPOUND[n]
# skip natively implemented types
- if x.niflibtype: continue
- if n[:3] == 'ns ': continue
-
+ if x.name in NATIVETYPES.keys():
+ continue
if not GENALLFILES and not x.cname in GENBLOCKS:
- continue
-
+ 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"' )
+ custom_lines = extract_custom_code( file_name )
+
+ HDR = CFile(io.open(file_name, 'wb'))
+ HDR.code( FULLGEN_NOTICE )
+ HDR.guard( x.cname.upper() )
+ HDR.code()
+ HDR.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()
+ HDR.include( '../obj/NiObject.h' )
+ HDR.code( x.code_include_h() )
+ HDR.namespace( 'Niflib' )
+ HDR.code( x.code_fwd_decl() )
+ HDR.code()
# header
- h.comment(x.description)
+ HDR.comment(x.description)
hdr = "struct %s"%x.cname
- if x.template: hdr = "template \n%s"%hdr
+ if x.template:
+ hdr = "template \n%s"%hdr
hdr += " {"
- h.code(hdr)
-
+ HDR.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) )
-
-
+ HDR.code( COMPOUND_DECL.format(x.cname) )
# declaration
- h.declare(x)
+ HDR.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;' )
-
+ HDR.code( 'NIFLIB_HIDDEN NifInfo Read( istream& in );' )
+ HDR.code( 'NIFLIB_HIDDEN void Write( ostream& out, const NifInfo & info = NifInfo() ) const;' )
+ HDR.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;' )
+ HDR.code( 'NIFLIB_HIDDEN void Read( istream& in, list & link_stack, const NifInfo & info );' )
+ HDR.code( 'NIFLIB_HIDDEN void Write( ostream& out, const map & link_map, list & missing_link_stack, const NifInfo & info ) const;' )
+ HDR.code( 'NIFLIB_HIDDEN string asString( bool verbose = false ) const;' )
- h.code( '//--BEGIN MISC CUSTOM CODE--//' )
+ HDR.code( BEG_MISC )
#Preserve Custom code from before
- for l in custom_lines['MISC']:
- h.write(l);
-
- h.code( '//--END CUSTOM CODE--//' )
+ for line in custom_lines['MISC']:
+ HDR.write(line)
+
+ HDR.code( END_CUSTOM )
# done
- h.code("};")
- h.code()
- h.write( "}\n" )
- h.code( '#endif' )
- h.close()
+ HDR.code("};")
+ HDR.code()
+ HDR.end()
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' )
-
+ custom_lines = extract_custom_code( file_name )
+
+ CPP = CFile(io.open(file_name, 'wb'))
+ CPP.code( PARTGEN_NOTICE )
+ 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("%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 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) )
+ 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('this->%s = src.%s;'%(m.cname, m.cname) )
+ CPP.code('return *this;')
+ CPP.code('};')
+ CPP.code()
+
+ CPP.code( '//Destructor' )
- cpp.code( '//Destructor' )
-
# destructor
- cpp.code("%s::~%s()"%(x.cname,x.cname) + " {};")
+ 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( '}' )
-
+ 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--//' )
+ 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( BEG_MISC )
#Preserve Custom code from before
- for l in custom_lines['MISC']:
- cpp.write(l);
-
- cpp.code( '//--END CUSTOM CODE--//' )
+ for line in custom_lines['MISC']:
+ CPP.write(line)
+
+ CPP.code( END_CUSTOM )
- cpp.close()
+ CPP.end()
# 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()
+ HDR = CFile(io.open(ROOT_DIR + '/include/gen/enums.h', 'wb'))
+ HDR.code( FULLGEN_NOTICE )
+ HDR.guard( 'NIF_ENUMS' )
+ HDR.code()
+ HDR.include( '' )
+ HDR.code( 'using namespace std;' )
+ HDR.code()
+ HDR.namespace( 'Niflib' )
+ HDR.code()
+ for n, x in itertools.chain(TYPES_ENUM.items(), TYPES_FLAG.items()):
+ if x.options:
+ if x.description:
+ HDR.comment(x.description)
+ HDR.code('enum %s {'%(x.cname))
+ for o in x.options:
+ HDR.code('%s = %s, /*!< %s */'%(o.cname, o.value, o.description))
+ HDR.code('};')
+ HDR.code()
+ HDR.code('ostream & operator<<( ostream & out, %s const & val );'%x.cname)
+ HDR.code()
+ HDR.end()
# 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()
+ HDR = CFile(io.open(ROOT_DIR + '/include/gen/enums_intl.h', 'wb'))
+ HDR.code( FULLGEN_NOTICE )
+ HDR.guard( 'NIF_ENUMS_INTL' )
+ HDR.code()
+ HDR.include( '' )
+ HDR.code( 'using namespace std;' )
+ HDR.code()
+ HDR.include('../nif_basic_types.h')
+ HDR.code()
+ HDR.namespace( 'Niflib' )
+ HDR.code()
+ for n, x in itertools.chain(TYPES_ENUM.items(), TYPES_FLAG.items()):
+ if x.options:
+ if x.description:
+ HDR.code()
+ HDR.code( '//---' + x.cname + '---//')
+ HDR.code()
+ HDR.code('void NifStream( %s & val, istream& in, const NifInfo & info = NifInfo() );'%x.cname)
+ HDR.code('void NifStream( %s const & val, ostream& out, const NifInfo & info = NifInfo() );'%x.cname)
+ HDR.code()
+ HDR.end()
#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()
+ CPP = CFile(io.open(ROOT_DIR + '/src/gen/enums.cpp', 'wb'))
+ CPP.code( FULLGEN_NOTICE )
+ CPP.code()
+ CPP.include('')
+ CPP.include('')
+ CPP.include('../../include/NIF_IO.h')
+ CPP.include('../../include/gen/enums.h')
+ CPP.include('../../include/gen/enums_intl.h')
+ CPP.code()
+ CPP.code('using namespace std;')
+ CPP.code()
+ CPP.namespace( 'Niflib' )
+ CPP.code()
+ CPP.code()
+ for n, x in itertools.chain(TYPES_ENUM.items(), TYPES_FLAG.items()):
+ if x.options:
+ CPP.code( ENUM_IMPL.format(x.cname, x.storage, r''.join((ENUM_IMPL_CASE.format(o.cname, o.name) for o in x.options))) )
+ CPP.code()
+ CPP.end()
#
# 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()
-
+ CPP = CFile(io.open(ROOT_DIR + '/src/gen/register.cpp', 'wb'))
+ CPP.code( FULLGEN_NOTICE )
+ CPP.code()
+ CPP.include( '../../include/ObjectRegistry.h' )
+ for n in NAMES_BLOCK:
+ x = TYPES_BLOCK[n]
+ CPP.include( '../../include/obj/' + x.cname + '.h' )
+ CPP.code()
+ CPP.namespace( 'Niflib' )
+ CPP.code( 'void RegisterObjects() {' )
+ CPP.code()
+ for n in NAMES_BLOCK:
+ x = TYPES_BLOCK[n]
+ CPP.code( 'ObjectRegistry::RegisterObject( "' + x.name + '", ' + x.cname + '::Create );' )
+ CPP.code()
+ CPP.code( '}' )
+ CPP.end()
#
# NiObject Files
#
-for n in block_names:
- x = block_types[n]
+for n in NAMES_BLOCK:
+ x = TYPES_BLOCK[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 );
+ custom_lines = extract_custom_code( 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--//' )
+ HDR = CFile(io.open(file_name, 'wb'))
+ HDR.code( PARTGEN_NOTICE )
+ HDR.guard( x.cname.upper() )
+ HDR.code()
+ HDR.code( BEG_HEAD )
#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" )
+ for line in custom_lines['FILE HEAD']:
+ HDR.write(line)
+
+ HDR.code( END_CUSTOM )
+ HDR.code()
+ HDR.code( x.code_include_h() )
+ HDR.namespace( 'Niflib' )
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 )
+ HDR.code( 'using namespace std;' )
+ HDR.code( x.code_fwd_decl() )
+ HDR.code( 'class ' + x.cname + ';' )
+ HDR.code( 'typedef Ref<' + x.cname + '> ' + x.cname + 'Ref;' )
+ HDR.code()
+ HDR.comment( x.description )
if x.inherit:
- out.code( 'class ' + x.cname + ' : public ' + x.inherit.cname + ' {' )
+ HDR.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()
+ HDR.code( 'class ' + x.cname + ' : public RefObject {' )
+ HDR.code( 'public:' )
+ HDR.code( CLASS_DECL.format(x.cname) )
+ HDR.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***/' )
+ for bmem in x.members:
+ if not bmem.arr1_ref and not bmem.arr2_ref and bmem.cname.lower().find("unk") == -1:
+ func_members.append(bmem)
+
+ if func_members:
+ HDR.code( '/***Begin Example Naive Implementation****' )
+ HDR.code()
+ for fmem in func_members:
+ HDR.comment( fmem.description + "\n\\return The current value.", False )
+ HDR.code( fmem.getter_declare("", ";") )
+ HDR.code()
+ HDR.comment( fmem.description + "\n\\param[in] value The new value.", False )
+ HDR.code( fmem.setter_declare("", ";") )
+ HDR.code()
+ HDR.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--//' )
+ HDR.code ( '//--This object has no eligible attributes. No example implementation generated--//' )
+ HDR.code()
+
+ HDR.code( BEG_MISC )
#Preserve Custom code from before
- for l in custom_lines['MISC']:
- out.write(l);
-
- out.code( '//--END CUSTOM CODE--//' )
+ for line in custom_lines['MISC']:
+ HDR.write(line)
+
+ HDR.code( END_CUSTOM )
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--//' )
+ HDR.code( 'protected:' )
+ HDR.declare(x)
+ HDR.code( 'public:' )
+ HDR.code( CLASS_INTL )
+ HDR.code( '};' )
+ HDR.code()
+ HDR.code( BEG_FOOT )
#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()
+ for line in custom_lines['FILE FOOT']:
+ HDR.write(line)
+
+ HDR.code( END_CUSTOM )
+ HDR.code()
+ HDR.end()
##Check if the temp file is identical to the target file
- #OverwriteIfChanged( file_name, 'temp' )
+ #overwrite_if_changed( file_name, 'temp' )
#
# NiObject Implementation File
@@ -708,200 +1592,192 @@ def OverwriteIfChanged( original_file, candidate_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--//' )
+ custom_lines = extract_custom_code( file_name )
+
+ CPP = CFile(io.open(file_name, 'wb'))
+ CPP.code( PARTGEN_NOTICE )
+ CPP.code()
+ CPP.code( BEG_HEAD )
#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' )
+ for line in custom_lines['FILE HEAD']:
+ CPP.write(line)
+
+ CPP.code( END_CUSTOM )
+ CPP.code()
+ CPP.include( '../../include/FixLink.h' )
+ CPP.include( '../../include/ObjectRegistry.h' )
+ CPP.include( '../../include/NIF_IO.h' )
+ CPP.code( x.code_include_cpp( True, "../../include/gen/", "../../include/obj/" ) )
+ CPP.code( "using namespace Niflib;" )
+ CPP.code()
+ CPP.code( '//Definition of TYPE constant' )
if x.inherit:
- out.code ( 'const Type ' + x.cname + '::TYPE(\"' + x.cname + '\", &' + x.inherit.cname + '::TYPE );' )
+ CPP.code ( 'const Type ' + x.cname + '::TYPE(\"' + x.name + '\", &' + x.inherit.cname + '::TYPE );' )
else:
- out.code ( 'const Type ' + x.cname + '::TYPE(\"' + x.cname + '\", &RefObject::TYPE );' )
- out.code()
+ CPP.code ( 'const Type ' + x.cname + '::TYPE(\"' + x.name + '\", &RefObject::TYPE );' )
+ CPP.code()
x_code_construct = x.code_construct()
if x_code_construct:
- out.code( x.cname + '::' + x.cname + '()' + x_code_construct + ' {' )
+ CPP.code( x.cname + '::' + x.cname + '()' + x_code_construct + ' {' )
else:
- out.code( x.cname + '::' + x.cname + '() {' )
- out.code ( '//--BEGIN CONSTRUCTOR CUSTOM CODE--//' )
+ CPP.code( x.cname + '::' + x.cname + '() {' )
+ CPP.code ( BEG_CTOR )
#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--//' )
+ for line in custom_lines['CONSTRUCTOR']:
+ CPP.write(line)
+
+ CPP.code ( END_CUSTOM )
+ CPP.code ( '}' )
+
+ CPP.code()
+ CPP.code( x.cname + '::' + '~' + x.cname + '() {' )
+ CPP.code ( BEG_DTOR )
#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--//' )
+ for line in custom_lines['DESTRUCTOR']:
+ CPP.write(line)
+
+ CPP.code ( END_CUSTOM )
+ CPP.code ( '}' )
+ CPP.code()
+ CPP.code( 'const Type & %s::GetType() const {'%x.cname )
+ CPP.code( 'return TYPE;' )
+ CPP.code( '}' )
+ CPP.code()
+ CPP.code( 'NiObject * ' + x.cname + '::Create() {' )
+ CPP.code( 'return new ' + x.cname + ';' )
+ CPP.code( '}' )
+ CPP.code()
+
+ CPP.code("void %s::Read( istream& in, list & link_stack, const NifInfo & info ) {"%x.cname)
+ CPP.code( BEG_PRE_READ )
#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--//' )
+ for line in custom_lines['PRE-READ']:
+ CPP.write(line)
+
+ CPP.code( END_CUSTOM )
+ CPP.code()
+ CPP.stream(x, ACTION_READ)
+ CPP.code()
+ CPP.code( BEG_POST_READ )
#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--//' )
+ for line in custom_lines['POST-READ']:
+ CPP.write(line)
+
+ CPP.code( END_CUSTOM )
+ CPP.code("}")
+ CPP.code()
+
+ CPP.code("void %s::Write( ostream& out, const map & link_map, list & missing_link_stack, const NifInfo & info ) const {"%x.cname)
+ CPP.code( BEG_PRE_WRITE )
#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--//' )
+ for line in custom_lines['PRE-WRITE']:
+ CPP.write(line)
+
+ CPP.code( END_CUSTOM )
+ CPP.code()
+ CPP.stream(x, ACTION_WRITE)
+ CPP.code()
+ CPP.code( BEG_POST_WRITE )
#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--//' )
+ for line in custom_lines['POST-WRITE']:
+ CPP.write(line)
+
+ CPP.code( END_CUSTOM )
+ CPP.code("}")
+ CPP.code()
+
+ CPP.code("std::string %s::asString( bool verbose ) const {"%x.cname)
+ CPP.code( BEG_PRE_STRING )
#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--//' )
+ for line in custom_lines['PRE-STRING']:
+ CPP.write(line)
+
+ CPP.code( END_CUSTOM )
+ CPP.code()
+ CPP.stream(x, ACTION_OUT)
+ CPP.code()
+ CPP.code( BEG_POST_STRING )
#Preserve Custom code from before
- for l in custom_lines['POST-STRING']:
- out.write(l);
-
- out.code( '//--END CUSTOM CODE--//' )
- out.code("}")
- out.code()
+ for line in custom_lines['POST-STRING']:
+ CPP.write(line)
+
+ CPP.code( END_CUSTOM )
+ CPP.code("}")
+ CPP.code()
- out.code("void %s::FixLinks( const map & objects, list & link_stack, list & missing_link_stack, const NifInfo & info ) {"%x.cname)
+ CPP.code("void %s::FixLinks( const map & objects, list & link_stack, list & missing_link_stack, const NifInfo & info ) {"%x.cname)
+
+ CPP.code( BEG_PRE_FIXLINK )
- 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--//' )
+ for line in custom_lines['PRE-FIXLINKS']:
+ CPP.write(line)
+
+ CPP.code( END_CUSTOM )
+ CPP.code()
+ CPP.stream(x, ACTION_FIXLINKS)
+ CPP.code()
+ CPP.code( BEG_POST_FIXLINK )
#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()
+ for line in custom_lines['POST-FIXLINKS']:
+ CPP.write(line)
+
+ CPP.code( END_CUSTOM )
+ CPP.code("}")
+ CPP.code()
+
+ CPP.code("std::list %s::GetRefs() const {"%x.cname)
+ CPP.stream(x, ACTION_GETREFS)
+ CPP.code("}")
+ CPP.code()
+
+ CPP.code("std::list %s::GetPtrs() const {"%x.cname)
+ CPP.stream(x, ACTION_GETPTRS)
+ CPP.code("}")
+ CPP.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***/' )
+ for bmem in x.members:
+ if not bmem.arr1_ref and not bmem.arr2_ref and bmem.cname.lower().find("unk") == -1:
+ func_members.append(bmem)
+
+ if func_members:
+ CPP.code( '/***Begin Example Naive Implementation****' )
+ CPP.code()
+ for fmem in func_members:
+ CPP.code( fmem.getter_declare(x.name + "::", " {") )
+ CPP.code( "return %s;"%fmem.cname )
+ CPP.code( "}" )
+ CPP.code()
+ CPP.code( fmem.setter_declare(x.name + "::", " {") )
+ CPP.code( "%s = value;"%fmem.cname )
+ CPP.code( "}" )
+ CPP.code()
+ CPP.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--//' )
+ CPP.code ( '//--This object has no eligible attributes. No example implementation generated--//' )
+ CPP.code()
+
+ CPP.code( BEG_MISC )
#Preserve Custom code from before
- for l in custom_lines['MISC']:
- out.write(l);
-
- out.code( '//--END CUSTOM CODE--//' )
+ for line in custom_lines['MISC']:
+ CPP.write(line)
+
+ CPP.code( END_CUSTOM )
##Check if the temp file is identical to the target file
- #OverwriteIfChanged( file_name, 'temp' )
+ #overwrite_if_changed( file_name, 'temp' )
- out.close()
+ CPP.end()
diff --git a/kfmxml b/kfmxml
index 9d12e2f..91eff92 160000
--- a/kfmxml
+++ b/kfmxml
@@ -1 +1 @@
-Subproject commit 9d12e2faf0984b469e1bd4ddaaf3ea4bdcf60101
+Subproject commit 91eff92daf197f0c5376740ed4b000d064138ec2
diff --git a/nifdoc.py b/nifdoc.py
new file mode 100644
index 0000000..4018355
--- /dev/null
+++ b/nifdoc.py
@@ -0,0 +1,292 @@
+#!/usr/bin/python
+"""
+nifdoc.py
+
+Generates HTML documentation for the XML file.
+
+To list command line options run:
+ nifdoc.py -h
+
+This file is part of nifxml
+Copyright (c) 2017 NifTools
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 3.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+"""
+
+from __future__ import unicode_literals
+
+import re
+import os
+import io
+import argparse
+from shutil import copy2
+
+from nifxml import Compound, Block, Enum, parse_xml, version2number
+from nifxml import TYPES_BLOCK, TYPES_BASIC, TYPES_COMPOUND, TYPES_ENUM, TYPES_FLAG, TYPES_VERSION
+from nifxml import NAMES_BLOCK, NAMES_BASIC, NAMES_COMPOUND, NAMES_ENUM, NAMES_FLAG, NAMES_VERSION
+
+from doc import nifdoc_tmpl as tmpl
+
+#
+# Globals
+#
+
+SCRIPT_PATH = os.path.dirname(os.path.realpath(__file__))
+DOC_PATH = '/doc/'
+DOC_FILE = '{}.html'
+CSS_PATH = os.path.join(SCRIPT_PATH, 'doc/nifdoc.css')
+ICO_PATH = os.path.join(SCRIPT_PATH, 'doc/favicon.ico')
+
+def clean(string):
+ """Removes everything but letters from a string"""
+ pattern = re.compile(r'[\W_]+')
+ return pattern.sub('', string)
+
+def main():
+ """Parses the XML and generates all doc pages"""
+ # Parse the XML and sort names
+ parse_xml()
+ NAMES_BASIC.sort()
+ NAMES_COMPOUND.sort()
+ NAMES_BLOCK.sort()
+ NAMES_ENUM.sort()
+ NAMES_FLAG.sort()
+
+ # Default to calling directory
+ root_dir = '.'
+ heading = True
+ metadata = True
+ minver = ''
+
+ # Command line args
+ parser = argparse.ArgumentParser(description="NIF Format XML Docs Generation")
+ parser.add_argument('-p', '--path', help="The path where the doc folder will be generated.")
+ parser.add_argument('-no-h1', '--no-heading', action='store_true',
+ help="Whether to not generate the main heading. Used by NifSkope for built-in help.")
+ parser.add_argument('-no-meta', '--no-metadata-columns', action='store_true',
+ help="Whether to not generate the metadata attribute columns (arg, arr1, arr2, etc.)")
+ parser.add_argument('-min-ver', '--minimum-version',
+ help="Hides attributes below this version. Format 'XX.X.X.XX'")
+ args = parser.parse_args()
+ if args.path:
+ root_dir = args.path
+ if args.no_heading:
+ heading = False
+ if args.no_metadata_columns:
+ metadata = False
+ if args.minimum_version:
+ minver = args.minimum_version
+
+ # Create the document generator
+ doc = DocGenerator(root_dir + DOC_PATH, heading, metadata, version2number(minver))
+ # Generate NiObject Pages
+ doc.gen_pages(NAMES_BLOCK, TYPES_BLOCK, tmpl.NIOBJECT if metadata else tmpl.NIOBJECT_NO_META)
+ # Generate Compound Pages
+ doc.gen_pages(NAMES_COMPOUND, TYPES_COMPOUND, tmpl.COMPOUND if metadata else tmpl.COMPOUND_NO_META)
+ # Generate Basic Pages
+ doc.gen_pages(NAMES_BASIC, TYPES_BASIC, tmpl.BASIC)
+ # Generate Enum Pages
+ enums = dict(TYPES_ENUM, **TYPES_FLAG)
+ doc.gen_pages(sorted(enums), enums, tmpl.ENUM)
+
+ # Generate Basic List Page
+ doc.gen_list_page('Basic Data Types', NAMES_BASIC, TYPES_BASIC, 'basic_list')
+ # Generate NiObject List Page
+ doc.gen_list_page('NIF Object List', NAMES_BLOCK, TYPES_BLOCK, 'niobject_list')
+ # Generate Compound List Page
+ doc.gen_list_page('Compound Data Types', NAMES_COMPOUND, TYPES_COMPOUND, 'compound_list')
+ # Generate Enum List Page
+ doc.gen_list_page('Enum Data Types', sorted(enums), enums, 'enum_list')
+ # Generate Version List Page
+ doc.gen_list_page('NIF File Format Versions', NAMES_VERSION, TYPES_VERSION, 'version_list', tmpl.VERSION_ROW, 'Versions')
+ # Generate Index Page
+ doc.gen_index()
+
+
+class DocGenerator():
+ """Methods for formatting and outputting the template strings with data from the XML"""
+
+ def __init__(self, path, heading=True, metadata=True, minver=0):
+ """Initialize generator"""
+ self.doc_file = path + DOC_FILE
+ self.main = tmpl.MAIN_H1 if heading else tmpl.MAIN_NO_H1
+ self.attr_row = tmpl.ATTR if metadata else tmpl.ATTR_NO_META
+ self.inherit = tmpl.INHERIT_ROW if metadata else tmpl.INHERIT_NO_META
+ self.minver = minver
+ self.blocks = dict(TYPES_BLOCK, **TYPES_COMPOUND)
+ install_dir = os.path.abspath(path)
+ if not os.path.exists(install_dir):
+ os.makedirs(install_dir)
+ # Install CSS and ICO
+ if os.path.dirname(CSS_PATH) != install_dir:
+ copy2(CSS_PATH, install_dir)
+ copy2(ICO_PATH, install_dir)
+
+ #
+ # Template Helper functions
+ #
+
+ def list_attributes(self, compound):
+ """Create Attribute List"""
+ attrs = ''
+ count = 0
+ for mem in compound.members:
+ if self.minver and mem.ver2 and mem.ver2 < self.minver:
+ continue
+ attr_type = tmpl.TYPE_LINK.format(clean(mem.type), mem.type)
+ if mem.template:
+ attr_type += tmpl.TMPL_LINK.format(clean(mem.template), mem.template)
+ content = {
+ 'attr_name': mem.name,
+ 'attr_type': attr_type,
+ 'attr_arg': mem.arg,
+ 'attr_arr1': mem.arr1.lhs,
+ 'attr_arr2': mem.arr2.lhs,
+ 'attr_cond': mem.cond,
+ 'attr_desc': mem.description.replace('\n', ' '),
+ 'attr_from': mem.orig_ver1,
+ 'attr_to': mem.orig_ver2,
+ 'row': 'even' if count % 2 == 0 else 'odd'
+ }
+ count += 1 # Manually increment because of 'continue' on skipped versioned rows
+ attrs += self.attr_row.format(**content)
+ return attrs
+
+ @staticmethod
+ def list_tags(names, types, template):
+ """List each tag with a description"""
+ tag_list = ''
+ for count, tname in enumerate(names):
+ tag = types[tname]
+ content = {
+ 'list_name': tag.name,
+ 'list_cname': clean(tag.name),
+ 'list_desc': tag.description.replace('\n', ' '),
+ 'row': 'even' if count % 2 == 0 else 'odd'
+ }
+ tag_list += template.format(**content)
+ return tag_list
+
+ @staticmethod
+ def list_choices(tag):
+ """Create Choice List"""
+ choice_list = ''
+ for count, opt in enumerate(tag.options):
+ content = {
+ # Display bitflags as hex
+ 'enum_number': opt.value if not hasattr(opt, 'bit') else '{0:#0{1}x}'.format(int(opt.value), 10),
+ 'enum_name': opt.name,
+ 'enum_desc': opt.description.replace('\n', ' '),
+ 'row': 'even' if count % 2 == 0 else 'odd'
+ }
+ choice_list += tmpl.ENUM_ROW.format(**content)
+ return choice_list
+
+ @staticmethod
+ def list_child_blocks(block):
+ """Create Child Block list"""
+ return ''.join(tmpl.LI_LINK.format(clean(n), n) for n in NAMES_BLOCK if TYPES_BLOCK[n].inherit == block)
+
+ def member_of(self, name):
+ """Create Member Of list"""
+ found = ''
+ for b_name in NAMES_BLOCK + NAMES_COMPOUND:
+ for bmem in self.blocks[b_name].members:
+ if bmem.type == name:
+ found += tmpl.LI_LINK.format(clean(b_name), b_name)
+ break
+ return found
+
+ def list_ancestor_attributes(self, block):
+ """Create list of attributes for all ancestors"""
+ attr_list = ''
+ for ancestor in reversed(block.ancestors()):
+ content = {'inherit': ancestor.name, 'cinherit': clean(ancestor.name)}
+ attr_list += self.inherit.format(**content) + self.list_attributes(ancestor)
+ return attr_list
+
+ def list_object_tree(self, root):
+ """Builds a hierarchical unordered list for a specified ancestor root."""
+ tree = ''
+ # Get truncated description
+ lines = root.description.splitlines(False)
+ # Add a new list for this ancestor
+ tree += tmpl.LI_LINK_DESC.format(clean(root.name), root.name, '' if not lines else lines[0])
+ # Create Child List
+ children = [TYPES_BLOCK[n] for n in NAMES_BLOCK if TYPES_BLOCK[n].inherit == root]
+ if children:
+ tree += tmpl.UL_ITEM.format(''.join(self.list_object_tree(c) for c in children))
+ return tree
+
+ #
+ # Generation Functions
+ #
+
+ def gen_pages(self, names, types, template):
+ """Generate Pages for XML Tag"""
+ for count, name in enumerate(names):
+ tag = types[name]
+ contents = {
+ 'name': tag.name,
+ 'description': tag.description.replace('\n', ' '),
+ 'storage': '' if not isinstance(tag, Enum) else tag.storage,
+ 'count': bool(tag.count == '1'),
+ 'member_of': self.member_of(name) if not isinstance(tag, Block) else '',
+ 'row': 'even' if count % 2 == 0 else 'odd'
+ }
+ if isinstance(tag, Block):
+ contents['attributes'] = self.list_ancestor_attributes(tag)
+ contents['parent_of'] = self.list_child_blocks(tag)
+ elif isinstance(tag, Compound):
+ contents['attributes'] = self.list_attributes(tag)
+ elif isinstance(tag, Enum):
+ contents['choices'] = self.list_choices(tag)
+
+ page = {'title': tag.name, 'contents': template.format(**contents)}
+
+ html = io.open(self.doc_file.format(clean(tag.name)), 'wt', 1, 'utf-8')
+ html.write( self.main.format(**page) )
+ html.close()
+
+ def gen_list_page(self, title, names, types, pagename, rowtmpl=tmpl.LIST_ROW, header='Name'):
+ """Generate List Page for XML Tag"""
+ page = {'title': title}
+ contents = {
+ 'title': title,
+ 'list_header': header,
+ 'list': self.list_tags(names, types, rowtmpl)
+ }
+ if isinstance(types[names[0]], Block):
+ page['contents'] = tmpl.NAV_LIST.format(**contents)
+ else:
+ page['contents'] = tmpl.LIST.format(**contents)
+
+ html = io.open(self.doc_file.format(pagename), 'wt', 1, 'utf-8')
+ html.write( self.main.format(**page) )
+ html.close()
+
+ def gen_index(self):
+ """Generate index.html"""
+ page = {'title': 'NIF Object Hierarchy'}
+ contents = {
+ 'title': page['title'],
+ 'object_tree': self.list_object_tree( TYPES_BLOCK['NiObject'] )
+ }
+ page['contents'] = tmpl.NAV_HIER.format(**contents)
+
+ html = io.open(self.doc_file.format('index'), 'wt', 1, 'utf-8')
+ html.write( self.main.format(**page) )
+ html.close()
+
+if __name__ == "__main__":
+ main()
diff --git a/nifxml b/nifxml
index 959cb9c..7e3677e 160000
--- a/nifxml
+++ b/nifxml
@@ -1 +1 @@
-Subproject commit 959cb9c3dd59a319e60e819fc8a1402f821f3684
+Subproject commit 7e3677e97b8c7a41516ad3f03286d8737da96261
diff --git a/nifxml.py b/nifxml.py
index 9559ddb..800a12a 100644
--- a/nifxml.py
+++ b/nifxml.py
@@ -1,755 +1,167 @@
-# TODO: split in multiple files
-
+#!/usr/bin/python
"""
-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}
+nifxml.py
+
+Parses nif.xml into dictionaries of classes grouped by XML tag type.
+
+This file is part of nifxml
+Copyright (c) 2017 NifTools
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 3.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+
+This file incorporates work covered by the following copyright and permission notice:
+ 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.
"""
-from xml.dom.minidom import *
-from textwrap import fill
+from __future__ import unicode_literals
+
+from xml.dom.minidom import Node, parse
-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
+# Globals
#
-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' )
+TYPES_NATIVE = {}
+TYPES_NATIVE['TEMPLATE'] = 'T'
+TYPES_BASIC = {}
+TYPES_ENUM = {}
+TYPES_FLAG = {}
+TYPES_COMPOUND = {}
+TYPES_BLOCK = {}
+TYPES_VERSION = {}
- txt = ""
- for l in lines:
- txt = txt + fill(l, 80) + "\n"
+NAMES_BASIC = []
+NAMES_COMPOUND = []
+NAMES_ENUM = []
+NAMES_FLAG = []
+NAMES_BLOCK = []
+NAMES_VERSION = []
- 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):
+def class_name(name_in): # type: (str) -> str
"""
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
+ if name_in is None:
+ return None
try:
- return native_types[n]
+ return TYPES_NATIVE[name_in]
except KeyError:
- return n.replace(' ', '_')
+ return name_in.replace(' ', '_').replace(":", "_")
- if n == None: return None
+ if name_in is None:
+ return None
try:
- return native_types[n]
+ return TYPES_NATIVE[name_in]
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
+ if name_in == 'TEMPLATE':
+ return 'T'
+ name_out = ''
+ for i, char in enumerate(name_in):
+ if char.isupper():
+ if i > 0:
+ name_out += '_'
+ name_out += char.lower()
+ elif char.islower() or char.isdigit():
+ name_out += char
else:
- n2 += '_'
- return n2
+ name_out += '_'
+ return name_out
-def define_name(n):
+def define_name(name_in): # type: (str) -> str
"""
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'):
+ name_out = ''
+ for i, char in enumerate(name_in):
+ if char.isupper():
if i > 0:
- n2 += '_'
- n2 += c
+ name_out += '_'
+ name_out += char
else:
- n2 += c
- elif (('a' <= c) and (c <= 'z')) or (('0' <= c) and (c <= '9')):
- n2 += c.upper()
+ name_out += char
+ elif char.islower() or char.isdigit():
+ name_out += char.upper()
else:
- n2 += '_'
- return n2
+ name_out += '_'
+ return name_out
-def member_name(n):
+def member_name(name_in): # type: (str) -> str
"""
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 = ''
+ if name_in is None or name_in == 'ARG':
+ return name_in
+ name_out = ''
lower = True
- for i, c in enumerate(n):
- if c == ' ':
+ for char in name_in:
+ if char == ' ':
lower = False
- elif (('A' <= c) and (c <= 'Z')) or (('a' <= c) and (c <= 'z')) or (('0' <= c) and (c <= '9')):
+ elif char.isalnum():
if lower:
- n2 += c.lower()
+ name_out += char.lower()
else:
- n2 += c.upper()
+ name_out += char.upper()
lower = True
+ elif char == '\\': # arg member access operator
+ name_out += '.'
else:
- n2 += '_'
+ name_out += '_'
lower = True
- return n2
-
-def version2number(s):
+ return name_out
+
+def version2number(s): # type: (str) -> int
"""
- 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
+ Translates a legible NIF version number to the packed-byte numeric representation.
+ For example, "10.0.1.0" is translated to 0x0A000100.
"""
- if not s: return None
+ if not s:
+ return None
l = s.split('.')
if len(l) > 4:
- assert(False)
+ assert False
return int(s)
-
if len(l) == 2:
version = 0
version += int(l[0]) << (3 * 8)
@@ -762,26 +174,11 @@ def version2number(s):
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])
+ for i, ver in enumerate(l):
+ version += int(ver) << ((3-i) * 8)
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):
+def scanBrackets(expr_str, fromIndex=0): # type: (str, int) -> Tuple[int, int]
"""Looks for matching brackets.
>>> scanBrackets('abcde')
@@ -798,7 +195,7 @@ def scanBrackets(expr_str, fromIndex = 0):
startpos = -1
endpos = -1
scandepth = 0
- for scanpos in xrange(fromIndex, len(expr_str)):
+ for scanpos in range(fromIndex, len(expr_str)):
scanchar = expr_str[scanpos]
if scanchar == "(":
if startpos == -1:
@@ -813,7 +210,7 @@ def scanBrackets(expr_str, fromIndex = 0):
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.
@@ -842,8 +239,8 @@ class Expression(object):
>>> bool(Expression('1 != 1').eval())
False
"""
- operators = [ '==', '!=', '>=', '<=', '&&', '||', '&', '|', '-', '+', '>', '<' ]
- def __init__(self, expr_str, name_filter = None):
+ 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)
@@ -852,15 +249,15 @@ def __init__(self, expr_str, name_filter = None):
else:
self._right = ''
- def eval(self, data = None):
+ 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):
+ elif isinstance(self._left, str):
left = getattr(data, self._left) if self._left != '""' else ""
else:
- assert(isinstance(self._left, (int, long))) # debug
+ assert isinstance(self._left, int) # debug
left = self._left
if not self._op:
@@ -868,10 +265,10 @@ def eval(self, data = None):
if isinstance(self._right, Expression):
right = self._right.eval(data)
- elif isinstance(self._right, basestring):
+ elif isinstance(self._right, str):
right = getattr(data, self._right) if self._right != '""' else ""
else:
- assert(isinstance(self._right, (int, long))) # debug
+ assert isinstance(self._right, int) # debug
right = self._right
if self._op == '==':
@@ -894,21 +291,33 @@ def eval(self, data = None):
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")
+ raise NotImplementedError("expression syntax error: operator '" + self._op + "' not implemented")
- def __str__(self):
+ def __str__(self): # type: () -> str
"""Reconstruct the expression to a string."""
left = str(self._left)
- if not self._op: return left
+ if not self._op:
+ return left
right = str(self._right)
return left + ' ' + self._op + ' ' + right
+ def encode(self, encoding): # type: (str) -> str
+ """
+ To allow encode() to be called on an Expression directly as if it were a string
+ (For Python 2/3 cross-compatibility.)
+ """
+ return self.__str__().encode(encoding)
+
@classmethod
- def _parse(cls, expr_str, name_filter = None):
+ def _parse(cls, expr_str, name_filter=None): # type: (str, Callable[[str], str]) -> str
"""Returns an Expression, string, or int, depending on the
contents of ."""
# brackets or operators => expression
@@ -917,7 +326,7 @@ def _parse(cls, expr_str, name_filter = None):
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
@@ -950,7 +359,7 @@ def _partition(cls, expr_str):
>>> Expression._partition('(a== b) &&(( b!=c)||d )')
('a== b', '&&', '( b!=c)||d')
"""
- # check for unary operators
+ # check for unary operators
if expr_str.strip().startswith('!'):
return expr_str.lstrip(' !'), '!', None
lenstr = len(expr_str)
@@ -963,7 +372,7 @@ def _partition(cls, expr_str):
# 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
@@ -973,7 +382,7 @@ def _partition(cls, expr_str):
# 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):
+ for op_endpos in range(op_startpos+1, op_startpos-1, -1):
op_str = expr_str[op_startpos:op_endpos+1]
if op_str in cls.operators:
break
@@ -984,12 +393,13 @@ def _partition(cls, expr_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 == ' ':
+ 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):
+ for op_endpos in range(op_startpos+1, op_startpos-1, -1):
op_str = expr_str[op_startpos:op_endpos+1]
if op_str in cls.operators:
break
@@ -1004,11 +414,11 @@ def _partition(cls, expr_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):
+ def _scanBrackets(expr_str, fromIndex=0):
"""Looks for matching brackets.
>>> Expression._scanBrackets('abcde')
@@ -1025,7 +435,7 @@ def _scanBrackets(expr_str, fromIndex = 0):
startpos = -1
endpos = -1
scandepth = 0
- for scanpos in xrange(fromIndex, len(expr_str)):
+ for scanpos in range(fromIndex, len(expr_str)):
scanchar = expr_str[scanpos]
if scanchar == "(":
if startpos == -1:
@@ -1040,23 +450,17 @@ def _scanBrackets(expr_str, fromIndex = 0):
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
- """
+
+ def code(self, prefix='', brackets=True, name_filter=None): # type: (str, bool, Callable[[str], str]) -> str
+ """Format an expression as a 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:
+ if not self.lhs:
+ return ''
+ if isinstance(self.lhs, int):
+ return self.lhs
+ elif self.lhs in TYPES_BLOCK:
return 'IsDerivedType(%s::TYPE)' % self.lhs
else:
return prefix + (name_filter(self.lhs) if name_filter else self.lhs)
@@ -1064,7 +468,7 @@ def code(self, prefix = '', brackets = True, name_filter = None):
lhs = self.lhs
if isinstance(lhs, Expression):
lhs = lhs.code(prefix, True, name_filter)
- elif lhs in block_types:
+ elif lhs in TYPES_BLOCK:
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)
@@ -1074,13 +478,13 @@ def code(self, prefix = '', brackets = True, name_filter = None):
rhs = self.rhs
if isinstance(lhs, Expression):
lhs = lhs.code(prefix, True, name_filter)
- elif lhs in block_types:
+ elif lhs in TYPES_BLOCK:
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:
+ elif rhs in TYPES_BLOCK:
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)
@@ -1098,20 +502,20 @@ def get_terminals(self):
yield terminal
elif self.rhs:
yield self.rhs
-
+
def __getattr__(self, name):
- if (name == 'lhs'):
+ if name == 'lhs':
return getattr(self, '_left')
- if (name == 'rhs'):
+ if name == 'rhs':
return getattr(self, '_right')
- if (name == 'op'):
+ 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):
+ """ducktyping: pretend we're also a string with isdigit() method"""
return False
-
+
class Expr(Expression):
"""
Represents a mathmatical expression?
@@ -1124,30 +528,27 @@ class Expr(Expression):
@ivar rhs: The right hand side of the expression?
@type rhs: string
"""
- def __init__(self, n, name_filter = None):
+ 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.
+ 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):
+
+ 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):
"""
@@ -1156,89 +557,58 @@ def __init__(self, element):
assert element.tagName == 'option'
parent = element.parentNode
#sisters = parent.getElementsByTagName('option')
-
+
# member attributes
- self.value = element.getAttribute('value')
- self.name = element.getAttribute('name')
+ self.value = element.getAttribute('value') # type: str
+ self.name = element.getAttribute('name') # type: str
+ self.description = self.name # type: str
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("=", "_")
+ self.cname = self.name.upper().replace(" ", "_").replace("-", "_").replace("/", "_").replace("=", "_").replace(":", "_") # type: str
class Member:
"""
- This class represents a member variable?
+ This class represents the nif.xml tag.
@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 userver2: The user version 2 where this member exists. Comes from the "userver2" attribute of the tag.
@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 is_abstract: Whether this member is abstract. This means that it does not factor into read/write.
@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.
+ 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?
@@ -1247,35 +617,40 @@ def __init__(self, element):
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
+ self.name = element.getAttribute('name') # type: str
+ self.suffix = element.getAttribute('suffix') # type: str
+ self.type = element.getAttribute('type') # type: str
+ self.arg = element.getAttribute('arg') # type: str
+ self.template = element.getAttribute('template') # type: str
+ self.arr1 = Expr(element.getAttribute('arr1')) # type: Expr
+ self.arr2 = Expr(element.getAttribute('arr2')) # type: Expr
+ self.cond = Expr(element.getAttribute('cond')) # type: Expr
+ self.func = element.getAttribute('function') # type: str
+ self.default = element.getAttribute('default') # type: str
+ self.orig_ver1 = element.getAttribute('ver1') # type: str
+ self.orig_ver2 = element.getAttribute('ver2') # type: str
+ self.ver1 = version2number(element.getAttribute('ver1')) # type: int
+ self.ver2 = version2number(element.getAttribute('ver2')) # type: int
+ xint = lambda s: int(s) if s else None
+ self.userver = xint(element.getAttribute('userver')) # type: Optional[int]
+ self.userver2 = xint(element.getAttribute('userver2')) # type: Optional[int]
+ self.vercond = Expr(element.getAttribute('vercond')) # type: Expr
+ self.is_public = (element.getAttribute('public') == "1") # type: bool
+ self.is_abstract = (element.getAttribute('abstract') == "1") # type: bool
+ self.next_dup = None # type: Optional[Member]
+ self.is_manual_update = False # type: bool
+ self.is_calculated = (element.getAttribute('calculated') == "1") # type: bool
+
+ # Get description from text between start and end tags
+ self.description = "" # type: str
if element.firstChild:
assert element.firstChild.nodeType == Node.TEXT_NODE
self.description = element.firstChild.nodeValue.strip()
- else:
+ elif self.name.lower().find("unk") == 0:
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"]:
@@ -1291,10 +666,10 @@ def __init__(self, element):
elif self.type == "Char8String":
pass
elif self.type == "StringOffset":
- self.default = "-1";
- elif self.type in basic_names:
+ self.default = "-1"
+ elif self.type in NAMES_BASIC:
self.default = "0"
- elif self.type in flag_names or self.type in enum_names:
+ elif self.type in NAMES_FLAG or self.type in NAMES_ENUM:
self.default = "0"
if self.default:
if self.default[0] == '(' and self.default[-1] == ')':
@@ -1306,27 +681,30 @@ def __init__(self, element):
elif self.type == "string" or self.type == "IndexString":
self.default = "\"" + self.default + "\""
elif self.type == "float":
- self.default += "f"
+ # Cast to float then back to string to add any missing ".0"
+ self.default = str(float(self.default)) + "f"
elif self.type in ["Ref", "Ptr", "bool", "Vector3"]:
pass
elif self.default.find(',') != -1:
- pass
+ 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
+ self.uses_argument = (self.cond.lhs == '(ARG)' or self.arr1.lhs == '(ARG)' or self.arr2.lhs == '(ARG)') # type: bool
+ # true if the type is implemented natively
+ self.type_is_native = self.name in TYPES_NATIVE # type: bool
# 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
+ self.is_duplicate = False # type: bool
+ # true if arr2 refers to an array
+ self.arr2_dynamic = False # type: bool
sis = element.previousSibling
while sis:
if sis.nodeType == Node.ELEMENT_NODE:
sis_name = sis.getAttribute('name')
- if sis_name == self.name:
+ if sis_name == self.name and not self.suffix:
self.is_duplicate = True
sis_arr1 = Expr(sis.getAttribute('arr1'))
sis_arr2 = Expr(sis.getAttribute('arr2'))
@@ -1334,206 +712,113 @@ def __init__(self, element):
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
+ # Calculate stuff from reference to next members
+ # Names of the attributes it is a (unmasked) size of
+ self.arr1_ref = [] # type: List[str]
+ # Names of the attributes it is a (unmasked) size of
+ self.arr2_ref = [] # type: List[str]
+ # Names of the attributes it is a condition of
+ self.cond_ref = [] # type: List[str]
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'))
+ 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)
+ 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)
+ 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
+ self.cname = member_name(self.name if not self.suffix else self.name + "_" + self.suffix) # type: str
+ self.ctype = class_name(self.type) # type: str
+ self.carg = member_name(self.arg) # type: str
+ self.ctemplate = class_name(self.template) # type: str
+ self.carr1_ref = [member_name(n) for n in self.arr1_ref] # type: List[str]
+ self.carr2_ref = [member_name(n) for n in self.arr2_ref] # type: List[str]
+ self.ccond_ref = [member_name(n) for n in self.cond_ref] # type: List[str]
class Version:
+ """This class represents the nif.xml tag."""
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.num = element.getAttribute('num') # type: str
+ # Treat the version as a name to match other tags
+ self.name = self.num # type: str
+ self.description = element.firstChild.nodeValue.strip() # type: str
- self.name = element.getAttribute('name')
- assert(self.name) # debug
- self.cname = class_name(self.name)
- self.niflibtype = element.getAttribute('niflibtype')
+class Basic:
+ """This class represents the nif.xml tag."""
+ def __init__(self, element, ntypes):
+ self.name = element.getAttribute('name') # type: str
+ assert self.name # debug
+ self.cname = class_name(self.name) # type: str
+ self.description = "" # type: str
if element.firstChild and element.firstChild.nodeType == Node.TEXT_NODE:
self.description = element.firstChild.nodeValue.strip()
- else:
+ elif self.name.lower().find("unk") == 0:
self.description = "Unknown."
- self.count = element.getAttribute('count')
+ self.count = element.getAttribute('count') # type: str
+ self.template = (element.getAttribute('istemplate') == "1") # type: bool
+ self.options = [] # type: List[Option]
+
+ self.is_link = False # type: bool
+ self.is_crossref = False # type: bool
+ self.has_links = False # type: bool
+ self.has_crossrefs = False # type: bool
+
+ self.nativetype = None # type: Optional[str]
+ if ntypes:
+ self.nativetype = ntypes.get(self.name)
+ if self.nativetype:
+ TYPES_NATIVE[self.name] = self.nativetype
+ if self.nativetype == "Ref":
+ self.is_link = True
+ self.has_links = True
+ if self.nativetype == "*":
+ self.is_crossref = True
+ self.has_crossrefs = True
- self.is_link = False
- self.is_crossref = False
- self.has_links = False
- self.has_crossrefs = False
+class Enum(Basic):
+ """This class represents the nif.xml tag."""
+ def __init__(self, element, ntypes):
+ Basic.__init__(self, element, ntypes)
+
+ self.storage = element.getAttribute('storage')
+ self.prefix = element.getAttribute('prefix')
+ # Find the native storage type
+ self.storage = TYPES_BASIC[self.storage].nativetype if TYPES_BASIC[self.storage].nativetype else TYPES_BASIC[self.storage].name
+ self.description = element.firstChild.nodeValue.strip()
- 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.nativetype = self.cname
+ TYPES_NATIVE[self.name] = self.nativetype
- 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)
+ # Locate all special enumeration options
+ for option in element.getElementsByTagName('option'):
+ if self.prefix and option.hasAttribute('name'):
+ option.setAttribute('name', self.prefix + "_" + option.getAttribute('name'))
+ self.options.append(Option(option))
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)
+ """This class represents the nif.xml tag."""
+ def __init__(self, element, ntypes):
+ Enum.__init__(self, element, ntypes)
+ for option in self.options:
+ option.bit = option.value
+ option.value = 1 << int(option.value)
- #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 = "../"
+class Compound(Basic):
+ """This class represents the nif.xml tag."""
+ def __init__(self, element, ntypes):
+ Basic.__init__(self, element, ntypes)
- self.members = [] # list of all members (list of Member)
- self.argument = False # does it use an argument?
+ self.members = [] # type: List[Member]
+ self.argument = False # type: bool
# store all attribute data & calculate stuff
for member in element.getElementsByTagName('add'):
@@ -1546,12 +831,12 @@ def __init__(self, element):
# recursively defined structures... so we remove
# this one to avoid the problem
# as a result a minority of nifs won't load
- continue
+ continue
#*********************
#** NIFLIB HACK END **
#*********************
self.members.append(x)
-
+
# detect templates
#if x.type == 'TEMPLATE':
# self.template = True
@@ -1559,243 +844,150 @@ def __init__(self, element):
# self.template = True
# detect argument
- if x.uses_argument:
- self.argument = True
- else:
- self.argument = False
+ self.argument = bool(x.uses_argument)
# detect links & crossrefs
- y = None
+ mem = None
try:
- y = basic_types[x.type]
+ mem = TYPES_BASIC[x.type]
except KeyError:
try:
- y = compound_types[x.type]
+ mem = TYPES_COMPOUND[x.type]
except KeyError:
pass
- if y:
- if y.has_links:
+ if mem:
+ if mem.has_links:
self.has_links = True
- if y.has_crossrefs:
+ if mem.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
+ for outer in self.members:
+ atx = False
+ for inner in self.members:
+ if atx:
+ if outer.name == inner.name: # duplicate
+ outer.next_dup = inner
+ break
+ elif outer == inner:
+ atx = True
+
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
+ """Find member by name"""
+ for mem in self.members:
+ if mem.name == name:
+ return mem
+ return None
+
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
-
+ """Find first reference of name in class."""
+ for mem in self.members:
+ if mem.arr1 and mem.arr1.lhs == name:
+ return mem
+ elif mem.arr2 and mem.arr2.lhs == name:
+ return mem
+ return None
+
+ def has_arr(self):
+ """Tests recursively for members with an array size."""
+ for mem in self.members:
+ if mem.arr1.lhs or (mem.type in TYPES_COMPOUND and TYPES_COMPOUND[mem.type].has_arr()):
+ return True
+ return False
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 = ""
-
+ """This class represents the nif.xml tag."""
+ def __init__(self, element, ntypes):
+ Compound.__init__(self, element, ntypes)
self.is_ancestor = (element.getAttribute('abstract') == "1")
inherit = element.getAttribute('inherit')
- if inherit:
- self.inherit = block_types[inherit]
- else:
- self.inherit = None
+ self.inherit = TYPES_BLOCK[inherit] if inherit else 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 member by name"""
+ 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)
+ """Find first reference of name in class"""
+ ret = None
+ if self.inherit:
+ ret = self.inherit.find_first_ref(name)
+ if not ret:
+ ret = Compound.find_first_ref(self, name)
+ return ret
+
+ def ancestors(self):
+ """List all ancestors of this block"""
+ ancestors = []
+ parent = self
+ while parent:
+ ancestors.append(parent)
+ parent = parent.inherit
+ return ancestors
+
+def parse_xml(ntypes=None):
+ """Import elements into our classes"""
+ if os.path.exists("nif.xml"):
+ xml = parse("nif.xml")
+ elif os.path.exists("nifxml/nif.xml"):
+ xml = parse("nifxml/nif.xml")
+ else:
+ raise ImportError("nif.xml not found")
+
+ for element in xml.getElementsByTagName('version'):
+ instance = Version(element)
+ TYPES_VERSION[instance.num] = instance
+ NAMES_VERSION.append(instance.num)
+
+ for element in xml.getElementsByTagName('basic'):
+ instance = Basic(element, ntypes)
+ assert not instance.name in TYPES_BASIC
+ TYPES_BASIC[instance.name] = instance
+ NAMES_BASIC.append(instance.name)
+
+ for element in xml.getElementsByTagName('enum'):
+ instance = Enum(element, ntypes)
+ assert not instance.name in TYPES_ENUM
+ TYPES_ENUM[instance.name] = instance
+ NAMES_ENUM.append(instance.name)
+
+ for element in xml.getElementsByTagName('bitflags'):
+ instance = Flag(element, ntypes)
+ assert not instance.name in TYPES_FLAG
+ TYPES_FLAG[instance.name] = instance
+ NAMES_FLAG.append(instance.name)
+
+ for element in xml.getElementsByTagName('compound'):
+ instance = Compound(element, ntypes)
+ assert not instance.name in TYPES_COMPOUND
+ TYPES_COMPOUND[instance.name] = instance
+ NAMES_COMPOUND.append(instance.name)
+
+ for element in xml.getElementsByTagName('niobject'):
+ instance = Block(element, ntypes)
+ assert not instance.name in TYPES_BLOCK
+ TYPES_BLOCK[instance.name] = instance
+ NAMES_BLOCK.append(instance.name)
+
+ validate_xml()
+
+def validate_xml():
+ """Perform some basic validation on the data retrieved from the XML"""
+ assert TYPES_VERSION and NAMES_VERSION and len(TYPES_VERSION) == len(NAMES_VERSION)
+ assert TYPES_BASIC and NAMES_BASIC and len(TYPES_BASIC) == len(NAMES_BASIC)
+ assert TYPES_COMPOUND and NAMES_COMPOUND and len(TYPES_COMPOUND) == len(NAMES_COMPOUND)
+ assert TYPES_BLOCK and NAMES_BLOCK and len(TYPES_BLOCK) == len(NAMES_BLOCK)
+ assert TYPES_ENUM and NAMES_ENUM and len(TYPES_ENUM) == len(NAMES_ENUM)
+ assert TYPES_FLAG and NAMES_FLAG and len(TYPES_FLAG) == len(NAMES_FLAG)
+
+ assert all(name for name in NAMES_VERSION)
+ assert all(name for name in NAMES_BASIC)
+ assert all(name for name in NAMES_COMPOUND)
+ assert all(name for name in NAMES_BLOCK)
+ assert all(name for name in NAMES_ENUM)
+ assert all(name for name in NAMES_FLAG)
diff --git a/nifxml_doc.py b/nifxml_doc.py
deleted file mode 100644
index 46822a0..0000000
--- a/nifxml_doc.py
+++ /dev/null
@@ -1,554 +0,0 @@
-# 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/templates/attr_row.html b/templates/attr_row.html
deleted file mode 100644
index e72a4da..0000000
--- a/templates/attr_row.html
+++ /dev/null
@@ -1,11 +0,0 @@
-
- {attr-name}
- {attr-type}
- {attr-arg}
- {attr-arr1}
- {attr-arr2}
- {attr-cond}
- {attr-desc}
- {attr-from}
- {attr-to}
-
diff --git a/templates/basic.html b/templates/basic.html
deleted file mode 100644
index d0aa9be..0000000
--- a/templates/basic.html
+++ /dev/null
@@ -1,10 +0,0 @@
-{name}
-{description}
-
-Can Be Used As Array Size
-{count}
-
-Found In
-
\ No newline at end of file
diff --git a/templates/class.html b/templates/class.html
deleted file mode 100644
index 4714324..0000000
--- a/templates/class.html
+++ /dev/null
@@ -1,58 +0,0 @@
-{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/templates/compound.html b/templates/compound.html
deleted file mode 100644
index b0a4be3..0000000
--- a/templates/compound.html
+++ /dev/null
@@ -1,22 +0,0 @@
-
\ No newline at end of file
diff --git a/templates/enum.html b/templates/enum.html
deleted file mode 100644
index 47575ad..0000000
--- a/templates/enum.html
+++ /dev/null
@@ -1,20 +0,0 @@
-
\ No newline at end of file
diff --git a/templates/enum_row.html b/templates/enum_row.html
deleted file mode 100644
index ff6ec55..0000000
--- a/templates/enum_row.html
+++ /dev/null
@@ -1,5 +0,0 @@
-
diff --git a/templates/hierarchy.html b/templates/hierarchy.html
deleted file mode 100644
index 5948453..0000000
--- a/templates/hierarchy.html
+++ /dev/null
@@ -1,2 +0,0 @@
-
\ No newline at end of file
diff --git a/templates/inherit_row.html b/templates/inherit_row.html
deleted file mode 100644
index f19d216..0000000
--- a/templates/inherit_row.html
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/templates/list.html b/templates/list.html
deleted file mode 100644
index 74ec048..0000000
--- a/templates/list.html
+++ /dev/null
@@ -1,8 +0,0 @@
-
\ No newline at end of file
diff --git a/templates/list_row.html b/templates/list_row.html
deleted file mode 100644
index 88fb91a..0000000
--- a/templates/list_row.html
+++ /dev/null
@@ -1,4 +0,0 @@
-
diff --git a/templates/main.html b/templates/main.html
deleted file mode 100644
index 93bfa21..0000000
--- a/templates/main.html
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-