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{0}
\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 = """ +\t

    NIF 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

    +
      +{member_of} +
    +""" + +# List for Parent Of +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\t +\t\t +\t\t +\t\t +\t\t +\t\t +\t\t +\t\t +\t +\t{attributes} +
    NameTypeArgArr1Arr2CondDescriptionFromTo
    +""" + +# Block without metadata columns +BLOCK_NM = """ +

    {name}

    +{description} +

    Attributes

    + +\t +\t\t +\t\t +\t\t +\t +\t{attributes} +
    NameTypeDescription
    +""" + +# 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_tree}
    """ + +# Object list for *_list.html +LIST = """ + +\t +\t\t +\t\t +\t +{list} +
    {list_header}Description
    """ + +# Navbar +NAV = """

    Hierarchy | List

    +

    {title}

    +""" + +# Nav plus contents +NAV_LIST = NAV + LIST +NAV_HIER = NAV + HIERARCHY + +# Enum layout +ENUM = """ +

    {name}

    +{description} + +

    Choices

    + +\t +\t\t +\t\t +\t\t +\t +\t{choices} +
    NumberNameDescription
    + +

    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 . - @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

        -
          -{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

        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        NameTypeArgArr1Arr2CondDescriptionFromTo
        Num KeysuintNumber of keys in the array.AnyAny
        InterpolationKeyTypeNum Keys != 0The key type.AnyAny
        KeysKeyInterpolationNum KeysThe keys.AnyAny
        - \ 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 @@ -

        {name}

        -{description} -

        Attributes

        - - - - - - - - - - - - -{attributes} -
        NameTypeArgArr1Arr2CondDescriptionFromTo
        - -

        Found In

        -
          -{found-in} -
        \ 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 @@ -

        {name}

        -{description} - -

        Choices

        - - - - - - -{choices} -
        NumberNameDescription
        - -

        Storage Type

        -{storage} - -

        Found In

        -
          -{found-in} -
        \ 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 @@ - - {enum-number} - {enum-name} - {enum-desc} - 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 @@ -

        {title}

        -
          {object-tree}
        \ 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 @@ - - From {inherit} - 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 @@ -

        {title}

        - - - - - -{list} -
        NameDescription
        \ 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 @@ - - {list-name} - {list-desc} - 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 @@ - - - - NIF File Format Documentation - {title} - - - - -

        NIF File Format Documentation

        -

        NIF Objects | Compound Types | Enum Types | Basic Types | File Versions

        - {contents} - - diff --git a/templates/niobject.html b/templates/niobject.html deleted file mode 100644 index 9ff1aff..0000000 --- a/templates/niobject.html +++ /dev/null @@ -1,22 +0,0 @@ -

        {name}

        -{description} -

        Attributes

        - - - - - - - - - - - - -{attributes} -
        NameTypeArgArr1Arr2CondDescriptionFromTo
        - -

        Parent Of

        -
          -{parent-of} -
        \ No newline at end of file diff --git a/templates/niobject_nav.html b/templates/niobject_nav.html deleted file mode 100644 index 531afa5..0000000 --- a/templates/niobject_nav.html +++ /dev/null @@ -1,2 +0,0 @@ -

        Hierarchy | List

        -{niobject-contents} diff --git a/templates/version_list.html b/templates/version_list.html deleted file mode 100644 index e264263..0000000 --- a/templates/version_list.html +++ /dev/null @@ -1,8 +0,0 @@ -

        {title}

        - - - - - -{list} -
        NumberDescription
        \ No newline at end of file diff --git a/templates/version_row.html b/templates/version_row.html deleted file mode 100644 index e407dfa..0000000 --- a/templates/version_row.html +++ /dev/null @@ -1,4 +0,0 @@ - - {list-name} - {list-desc} -