pax_global_header00006660000000000000000000000064147551666110014526gustar00rootroot0000000000000052 comment=870ca04ec16f39b474a7bebe900faf53d36a8d6d deutex-5.2.3/000077500000000000000000000000001475516661100130335ustar00rootroot00000000000000deutex-5.2.3/.gitignore000066400000000000000000000003261475516661100150240ustar00rootroot00000000000000Makefile Makefile.in /autom4te.cache /aclocal.m4 /compile /config.guess /config.h /config.h.in /config.log /config.status /configure /depcomp /install-sh /missing /stamp-h1 *.html *~ \#*\# .\#* .DS_Store .vscode deutex-5.2.3/.travis.yml000066400000000000000000000001411475516661100151400ustar00rootroot00000000000000language: c arch: - amd64 - ppc64le compiler: gcc script: ./bootstrap && ./configure && make deutex-5.2.3/AUTHORS000066400000000000000000000003471475516661100141070ustar00rootroot00000000000000André Majorel Ayub Ahmed Jon Dowland Mike Swanson Nick Zatkovich Olivier Montanuy deutex-5.2.3/COPYING000066400000000000000000000432541475516661100140760ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) 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 this service 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 make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. 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. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), 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 distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the 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 a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE 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. 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 convey 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision 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, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This 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. deutex-5.2.3/COPYING.LIB000066400000000000000000000636421475516661100145060ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, 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 this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), 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 distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser 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 Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "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 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY 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 LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey 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 library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! deutex-5.2.3/LICENSE000066400000000000000000000061321475516661100140420ustar00rootroot00000000000000 License terms for DeuTex ------------------------------------------------------------------------ DeuTex is Copyright © 1994-1995 Olivier Montanuy, Copyright © 1999-2005 André Majorel, Copyright © 2006-2025 contributors to the DeuTex project. The parts of DeuTex that have been written by Olivier Montanuy, Kevin McGrail, André Majorel and other contributors are distributed under the terms of version 2 of the GNU general public license (GPL). 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 2 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 Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ------------------------------------------------------------------------ However, the DeuTex distribution includes material that was written by other people or distributed under different licenses : 1. DeuTex incorporates code derived from DEU 5.21. DEU 5.21 was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. No part of DeuTex is public-domain, however. 2. The file lzw.c contains the following notes : > Copyright 1993, David Koblas (koblas=netcom+com) > > Permission to use, copy, modify, and to distribute this software > and its documentation for any purpose is hereby granted without > fee, provided that the above copyright notice appear in all > copies and that both that copyright notice and this permission > notice appear in supporting documentation. There is no > representations about the suitability of this software for > any purpose. this software is provided "as is" without express > or implied warranty. > This file is derived from ppmtogif.c and giftoppm.c > only the compression/decompression routines were kept. > > Based on GIFENCOD by David Rowley . > A Lempel-Zim compression based on "compress". > > Copyright (C) 1989 by Jef Poskanzer. > > Permission to use, copy, modify, and distribute this software and > its documentation for any purpose and without fee is hereby > granted, provided that the above copyright notice appear in all > copies and that both that copyright notice and this permission > notice appear in supporting documentation. This software is > provided "as is" without express or implied warranty. > > The Graphics Interchange Format(c) is the Copyright property of > CompuServe Incorporated. GIF(sm) is a Service Mark property of > CompuServe Incorporated. 3. The files endianio.c and endianm.c are Copyright © André Majorel 1999 and available under the terms of the LGPL. deutex-5.2.3/Makefile.am000066400000000000000000000004051475516661100150660ustar00rootroot00000000000000SUBDIRS = man src EXTRA_DIST = LICENSE NEWS.adoc README.adoc \ pkg.w32/config.make.in pkg.w32/cp-with-libs pkg.w32/Makefile if ASCIIDOC CLEANFILES = *.html %.html: %.adoc TZ=UTC asciidoc $< html: README.html NEWS.html $(MAKE) -C man html endif deutex-5.2.3/NEWS.adoc000066400000000000000000001007301475516661100144400ustar00rootroot00000000000000DeuTex release notes ==================== 5.2.3 (2025-02-18) ------------------ Bug fix ~~~~~~~ * DeuTex now checks the position of the directory offset in a WAD file instead of an arbitrary value, which may get exceeded for reasonably large files. This fixes the extraction of extras.wad in modern Doom releases. (Thanks, Fabian Greffrath) 5.2.2 (2020-12-30) ------------------ Build systems ~~~~~~~~~~~~~ * `configure.ac` is now compatible with autoconf 2.70. C99 is assumed as a baseline default and should not be explicitly called for. * `./configure --enable-man` is now the only way an error occurs if AsciiDoc (`a2x`) is not installed. Graphics ~~~~~~~~ * Hexen graphics are now treated a bit more specially, the last entry in the palette no longer counting as a candidate to produce transparency in the output file. 5.2.1 (2019-08-10) ------------------ Build systems ------------- * `./configure` was broken in 5.2.0 for systems without AsciiDoc installed. It now properly checks for a2x conditionally along with the possible `--disable-man` parameter. 5.2.0 (2019-06-23) ------------------ Graphics ~~~~~~~~ * DeuTex supports textures in TX_START and TX_END markers. These are used by certain editors/engines for textures, with support for storing PNG and JPEG files directly in the WAD. Extraction is likewise handled for all formats. Build systems and code standards ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Instances of `== true` and `!= false` were simplified to more idiomatic C, such as `if (cond)` and `if (!cond)`. * Manual page generation can be disabled, even if `a2x` is available, by using `./configure --disable-man`. This feature may be useful for penguin distributions. 5.1.2 (2018-08-12) ------------------ General ~~~~~~~ * `--help` no longer causes a segfault (thanks andwj). 5.1.1 (2018-01-08) ------------------ General ~~~~~~~ * The texture name array was fixed so the maximum possible string size is now supported. * Some warnings and errors with old versions of pkg-config and gcc have been fixed. * Aliasing errors have been resolved, which caused crashes on some architectures, such as sparc64. * DeuTex can now build WADs with an arbitrary number of lumps, but prints a warning when more than 4046 are included (the Vanilla Doom limit). 5.1.0 (2017-08-11) ------------------ General ~~~~~~~ * The `-overwrite` option now works. * Levels are extracted/inserted in a way to preserve GL nodes. * Inserting pictures with a height of 1 pixel no longer causes a malloc error, and allows the operation of rebuilding a _Doom_ 1 or 2 IWAD. * texture lump file names can now be overridden. Graphics ~~~~~~~~ * DeuTex supports reading and writing sprite offsets based on PNG +grAb+ chunks in a manner compatible with SLADE and ZDoom. +wadinfo.txt+ overrides these offsets unless `-pngoffsets` is used. 5.0.0 (2017-07-31) ------------------ Removed features ~~~~~~~~~~~~~~~~ * DeuSF. * `-man` troff format generation. * WinTex options. * `-fullsnd`: now the only mode. * MS-DOS and OS/2 compatibility. * Incomplete (and conditioned out) Rise of the Triad support. File format support ~~~~~~~~~~~~~~~~~~~ * PNG support added, creating an optional dependency on libpng 1.6. If compiled in, it is the default extraction format, PPM otherwise. * Au and VOC sound formats removed. WAV is the only supported format for extraction and creation. * Full sound lumps from the WAD are always extracted. * MIDI files can be included just by being named *.mid, and are extracted to the same file name extension. General ~~~~~~~ * Log file support has been removed, in favor of the user doing a shell redirection (eg, with `>` or `2>`) instead. * Arch-vile sprites are now extracted and inserted using literal names for sprites with the `[` and `]` characters in names (illegal file name characters on DOS, but not current Windows systems), and sprite names with `\` are now altered to use `^` on-disk, matching the ZDoom PK3 standard. * Graphics with a height > 128 are now inserted into Doom WAD files correctly. * UDMF (Universal Doom Map Format) support. Build systems, code standards ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Real Autoconf+Automake build system to replace the barely-functioning imitation one. `./configure`, `make`, and related environment variables work as should be expected. * MS-DOS and OS/2 batch files removed. * A `malloc.h` include was removed to allow compilation on Mac OS X, and is not needed by current Unix systems in general. * Thousands of lines of code deleted and the entire tree reformated (primarily by using `indent -kr`, followed up by manual touch-ups). Several unused files, functions, and commented out code removed. * C99-style cleanups to use (`u`)`intN_t` types, `bool`, `true`, `false` throughout the code, replacing old defines. * AsciiDoc now used for documentation, and building the manpage requires it to be installed. 4.4.902 (2005-08-31) -------------------- Build ~~~~~ * Vanilla `make` compatibility: removed all occurrences of `$<` not in inference rules in the makefile. * Vanilla `make` compatibility: removed all `\` followed by empty lines. On NCR MP-RAS, `make` quotes all empty lines that follow! * There is now a `./configure` script (GNU autoconf work-alike). + + Installing somewhere else than `/usr/local` is now done by giving `./configure` the `--prefix` option. + + `CC`, `CFLAGS` and `LDFLAGS` are now auto-detected. The defaults can be overridden by passing `./configure` the `--cc`, `--cflags` and `--ldflags` options. + + The fixed-size types (`Int16` etc.) are now correctly set on all LP64 platforms, not just Alpha. * The OPTIONS section is now updated automatically. This impacts no-one but hackers. Code ~~~~ * two `char[8]` were used as `char[9]` (caught by NCR/MetaWare `cc` on NCR MP-RAS). * C89 compatibility: removed a few `//` comments that had slipped into the source. * A few functions were declared as `static` but defined as `extern` (caught by NCR/MetaWare `cc` on NCR MP-RAS). * `static`- and `const`-correctness fixes. * Removed the little-used `Legal()` function. Doc ~~~ * Man page: in DESCRIPTION, added a short list of examples to help new users getting started. Added a FILES section. Made the SEE ALSO section a bit more specific. * In `--help` and the man page, divided the options into groups and sorted them alphabetically within each group. It seems to be clearer that way. * Added a FAQ. * Obfuscated all email addresses (`tr .@ +=`). _Strife_ ~~~~~~~~ The `SCRIPTnn` lumps are now extracted in human-readable form into the `scripts/` directory. The reverse operation is not implemented. Since we understand the Strife script format only partially, the output format is still in flux. New option `-scripts` to extract only the scripts. The reverse engineering was done in collaboration with Matthew W. Miller. Graphics ~~~~~~~~ * It’s now possible to build wads that use a custom `PLAYPAL`. Previously, DeuTex always used the PLAYPAL from the iwad when building. It now uses the one in `lumps/` if it exists. Thanks to Ras2 for reporting the problem. * Rewrote `COLdiff()` as a macro. On my system, the time to build a wad made of a single 1M-pixel patch is down from 1.32s to 0.85s (35% faster), which is not surprising since DeuTex used to spend about 40% of its time in `COLdiff()`. The time to rebuild the _Strife_ iwad is down from 8.6s to 7.2s (16% faster). * Removed the “quantisation is slow” warnings that DeuTex used to spew when composing from PPMs and 24-bit BMPs. Those warnings were relevant to an old quantisation algorithm that has not been used since at least version 3.8 (`#ifdef QUANTSLOW`). * Lifted the arbitrary limit of 256 patches per texture. DeuTex will now accept as many patches as the wad format allows (32,767). If a texture definition has more than that many patches, DeuTex will discard the excess patches with a warning, instead of dying with a cryptic message like `Line n: Illegal char '*'`. * Lifted the arbitrary limit of 4096 on patch width. DeuTex now handles patches as wide as the wad format allows (32,767). * Textures wider than 4096 are now accepted with a warning. PrBoom 2.2.3 is known to take textures as wide as 16,384 pixels. 8192×128 textures make XDoom 20001001 crash in `Z_Malloc`. 1024×128 textures make Doom freeze in `R_Init`. The maximum acceptable widths might be higher if the height of the texture is lesser than 128 but I haven’t looked into it. Thanks to David Damerell for testing. Misc ~~~~ * Removed the annoying startup banner. * If no command is given, emit a meaningful error message instead of suggesting to switch to WinTex. And exit with code 1 instead of 255. * All messages now look like this: + + _c code string_ + + _c_ is the class of the message: *i* for information, *w* for warning, *E* for error, *B* for bug. + + _code_ is a unique 4-character alphanumeric code which unambiguously identifies the message. + + Some of the messages have been rewritten to be more informative (mention the filename, the nature of the error, etc.). * At the request of Kim “Sparky” Parrott, all messages are now copied to a file named `deutex.log` (`deusf.log` for DeuSF). The default log file name can be overridden through the `-log` option. If you don’t want a log file, try `-log /dev/null` (Unix) or `-log nul` (DOS). + + The log is only written if a command that works with wads is given. `--help`, `--version` etc. do not create a log. * Removed the 30 command-line arguments limit. * Made exit status a little bit more normal (2 for errors and 3 for bugs instead of -5 and -10). Sound ~~~~~ * New option `-rate` to specify what DeuTex should do when including sound files whose sample rate is not exactly 11025 Hz. The choices are: + + `reject`: consider it a fatal error and exit immediately with a non-zero exit code. + `force`: emit a warning and force it to 11025 Hz by resampling up or down. + `warn`: emit a warning but include it as is anyway. + `accept`: silently include it as is. + + Previously, the default (and only option) used to be `force`. It’s now `warn`. + + Thanks to Matthew W. Miller for telling me about this issue (which he did in 1999; the six-year delay is mine, all mine). * Write errors while extracting PC speaker sounds are now actually detected and reported. 4.4.0 (2000-01-05) ------------------ Game ~~~~ * _Hexen_: musics are now identified and extracted properly. + + The old music identification code assumed that any lump whose name does not begin with either `D_` or `MUS_` can’t be a music. It worked for _Doom_, _Heretic_ and _Strife_ but, for _Hexen_, it caused all musics to be seen as plain lumps and extracted accordingly into the `lumps/` directory. DeuTex even tried to interprete `STALKR` and `WINNOWR` as pictures and said silly things about them having a “width greater than 4096”. + + The new code really checks whether the lump begins with `MUS\x1a` instead of just looking at its name. A lump is now deemed to be a music if and only if it begins with `MUS\x1a`. + + As a side-effect, certain operations (appending sprites and flats and merging) must have become slower. Furthermore, since these used to blindly assume that any lump whose name begins with either `D_` or `MUS_` is a music, their semantics might have changed. If you find they don’t do what you want, try again using the `-musid` option and let me know whether it improves your condition. * _Hexen_, _Doom_ alpha 0.4/0.5: levels are now properly extracted and included. + + There have been changes in the undocumented details of DeuTex’s behaviour with respect to levels. The one that is most likely to be noticed is that, when including a level, DeuTex now copies the entire contents of the `levels/` pwad, starting from the level label. Previously, it included at most the 11 following lumps, and only if they had the expected names (`THINGS`, `VERTEXES` and so on). + + But, basically, if the `levels/` pwads contain, as they should, all the needed lumps and nothing else, there shouldn’t be any trouble. * _Heretic_ and _Hexen_: does not abort anymore with `Bug: *** idinx (12) ***` when trying to include the graphic lumps (resp. `CREDIT`, `E2END`, `FINAL1`, `FINAL2`, `HELP1`, `HELP2`, `TITLE` and `CREDIT`, `FINALE1`, `FINALE2`, `FINALE3`, `HELP1`, `HELP2`, `INTERPIC`, `TITLE`). More generally, DeuTex now accepts to compose wads even when there are graphic files in `lumps/`. * _Hexen_: does not abort anymore with `Height of FLAT ./flats/x_001.ppm is not 64 or 65` when trying to include flats `X_001` through `X_011`. In addition, DeuTex now just emits a warning instead of aborting for other oddball heights (i.e. not 64, 65 or 128). Have fun. ;-) This is true for all iwads, not only _Hexen_. Graphics ~~~~~~~~ The annoying “quantisation is slow” warnings now appear at most once. Misc ~~~~ * To disambiguate the ` warnings omitted` message, added optional scope prefix and changed the picture extraction function to use it. * Got rid of the “don’t bother Olivier” banner. People must have got the message by now. 4.3.0 (1999-12-24) ------------------ Graphics ~~~~~~~~ Fixed ancient bug where DeuTex sometimes failed to include custom patches if they were not explicitly listed in the `[patches]` section. If the first patch used in `texture1.txt` was a custom patch, it had to be listed in `[patches]` or DeuTex would forget to include it. This is the same bug Olivier mentioned in the home page: [quote] The support for wall patches in DeuTex has been modified. You must now explicitely declare all your patches in a [PATCHE] section. + + If you don’t do this, DeuTex will attempt to work as usual, but there seems to ba a bug in this part of the code, so sometime some needed patches are not loaded. After some summary testing, looks like it’s fixed. Misc ~~~~ * More error handling improvements. * Bumped version number and cleaned things up for public release. * Decreased maximum number of warnings per picture from 10 to 5. Platform ~~~~~~~~ Fixed `ftruncate()` being undeclared when compiling with DJGPP and updated the building-on-DOS section of the doc. 4.2.2 (1999-11-20) ------------------ Misc ~~~~ Made certain failure messages more informative. Platform ~~~~~~~~ Fixed several bugs that showed in the DOS precompiled executables for 4.1.0 and 4.2.0 (most common symptom: DeuTex aborting with a `Can't read WAD` error message). Lengthy technical explanation: in 4.1.0, I removed the `huge` pointer qualifiers that were scattered throughout the source not unlike nitrates in groundwater. The reasoning was that, since DeuTex is always compiled in the `huge` memory model anyway, those qualifiers were redundant. As I found out at the end of a long and painful debugging session, they weren’t. Had I read the doc of the compiler, I would have known that, even when in the huge memory model, pointers are `far` by default, not `huge`. Far pointers wrap around at 64 kB; this is not what you want when you’re trying to work with lumps larger than that. And, apparently, there is no way to specify that pointers should be huge by default. On top of that, there was a genuine bug in `WADRreadBytes2()` that would have prevented the DOS port from working, even if all pointers had been huge. But this one was fixed in 4.2.1. I switched to DJGPP, with which you can get working executables without having to contaminate your code with carcinogenic keywords. The bad news: firstly, the executables are somewhat larger. Secondly, since DJGPP executables use protected mode, they tend to be more fussy. Thanks to Kim Parrott for reporting the bug and alpha testing my fixes. All the above applies only to the DOS precompiled executables. Other platforms did not have these problems. 4.2.1 (1999-11-16) ------------------ Command line ~~~~~~~~~~~~ Fixed segfault on `deutex --vers`. Graphics ~~~~~~~~ New option `-usedidx`. When called with this option, DeuTex scans all the graphics in the wad and prints statistics about which palette indices they use. (By “graphics” is meant “any data that is converted into an RGB triplet by looking up `PLAYPAL` or `TITLEPAL` ”. That includes flats, graphics, patches, sneaps, sneats and sprites.) I’ve added this command for my own use, to help my decide which index should be used to store the transparent colour for _Hexen_. Misc ~~~~ * Made certain failure messages more informative. * Made printing of lump names garbage-proof. Platform ~~~~~~~~ * Fixed a huge DOS bug that made DeuTex fail with `Can't read WAD` error whenever it had to read more than 65535 bytes from a wad at once. * Flushing `stdout` before writing to `stderr` so that messages come out in the right order when both outputs are redirected. Sound ~~~~~ All conditions that used to be fatal errors when extracting sound lumps now just elicit a warning message, indicating which lump it was and what action was taken. 4.2.0 (1999-11-14) ------------------ Doc ~~~ Fixed error in documentation of `-pkgfx`, `-pknormal` and `-usedtex`. _Strife_ ~~~~~~~~ Fixed DeuTex aborting when extracting textures for versions of _Strife_ ≥ 1.1. The problem was that _Strife_ 1.1 and above use a different format for the `TEXTURE1` and `TEXTURE2` lumps (_Strife_ 1.0 uses the same format as _Doom_). New options `-tf strife11`, `-itf strife11` and `-otf strife11` to support that format. Option `-strife` has been changed to imply `-tf strife11`. New option `-strife10` that is identical to `-strife` except that it does not imply `-tf strife11`. Summary: * If you have the Strife 1.0 iwad, use `-strife10` (or `-tf normal`). * If you have Strife 1.1 or above, use `-strife` (or `-tf strife11`). Thanks to Kim Parrott for reporting the bug and Len Pitre for pointing me in the right direction. Sound ~~~~~ Fixed two bugs in reading Sun audio (`.au`) files. Fixes error `WAV: can't read data of./sounds/foo.au` [sic] when trying to build a wad. One of these bugs prevented from reading Sun audio files on little-endian machines. It had been there for a long time; v3.8 has it and the v3.6 binary behaves like it had it too. I doubt that anyone had ever been able to use `.au` files on little-endian machines before. 4.1.0 (1999-11-01) ------------------ Command line ~~~~~~~~~~~~ New options `-sneas`, `-sneaps` and `-sneats`. Code ~~~~ * Replaced certain occurrences of `Int32` by `iolen_t`. * Replaced certain occurrences of `256` by `NCOLOURS`. _Doom_ alpha 0.4 ~~~~~~~~~~~~~~~~ `AMENA0` and `MSKUL*` are now correctly recognized as graphics and not as lumps anymore. The 21 graphic lumps that ended up in `lumps/` are now properly extracted (into `sneaps/` and `sneats/`). (The first item involved propagating to `IDENTgraphic()` the changes made to `PICtoRAW()` in v. 4.0.2. The second item needed heavy hacking, creating a new image type (christened “snea”) and managing an alternate palette for `TITLEPAL`.) Still extracted as lumps: `GNUM[0-9]` and `HUFONT`. _Doom_ alpha 0.5 ~~~~~~~~~~~~~~~~ The 86 graphic lumps that ended up in `lumps/` are now properly extracted (into `sneaps/` and `sneats/`). Still extracted as lump: `HUFONT`. Graphics ~~~~~~~~ Errors that used to cause DeuTex to give up on extracting a picture now just make it skip the rest of the column. It also prints detailed messages about what it didn’t like and in which picture it occurred instead of bailing out silently. Misc ~~~~ * New option `-di` to debug entry identification. Useful mainly to hackers. * Cosmetic changes in the generated `wadinfo.txt` and in the phase messages. * No more messages `Creating PWAD` and `WAD is complete...` during level extraction. * Set a limit of 10 warnings per picture, to prevent invalid pictures from uselessly flooding the output. 4.0.3 (1999-10-02) ------------------ Command line ~~~~~~~~~~~~ New option `-doom2` as suggested by Matthew Miller. Graphics ~~~~~~~~ Now accepts to extract pictures as large as 4096×4096 (previously the limit was 320×200). This fixes `Failed to write sprite` errors when trying to extract `PSYBA0` and `PSYBB0` from `strain.wad`. Thanks to Matthew miller for reporting the bug. Misc ~~~~ Added a useful URL to the GIF warning. Platform ~~~~~~~~ Now builds without errors on FAT filesystems (replaced `.deutex` and `.deusf` by `tmp/_deutex` and `tmp/_deusf`). Sound ~~~~~ Fixed a bug that caused DeuTex to extract sounds with unlikely sample rates like 4 GHz whenever the sample rate in the lump was higher than 32767 Hz (for example `DSVILACT` and `DSVILSIT` from `ncc1701.wad`, with a sample rate of 44.1 kHz). Thanks to Matthew Miller for reporting the bug. 4.0.2 (1999-09-19) ------------------ Command line ~~~~~~~~~~~~ New options * `-doom02` (implies `-ipf alpha`, `-itf none`, and `-itl none`) * `-doom04` (implies `-ipf alpha`, `-itf nameless`, and `-itl textures`) * `-doom05` (implies `-ipf alpha` and `-itl textures`) * `-doompr` (implies `ipf pr`) Code ~~~~ Replaced certain unjustified uses of `Int32` by `long`. Doc ~~~ Removed `old/readme.txt`. It’s so out of date that it’s more confusing than useful. Game ~~~~ _Doom_ alpha and _Doom_ PR: it’s now possible to extract graphics, patches, sprites, and textures from those iwads. Three new options: `-ipf {normal|pr|alpha}`:: Use `alpha` for _Doom_ alpha 0.2, 0.4, and 0.5. + Use `pr` for _Doom_ PR (press release and beta). + Use `normal` for everything else. `-itf {normal|nameless|none}`:: Use `none` for _Doom_ alpha 0.2. + Use `nameless` for _Doom_ alpha 0.4. + Use `normal` for everything else, including _Doom_ alpha 0.5. `-itl {normal|textures|none}`:: Use `none` for _Doom_ alpha 0.2. + Use `textures` for _Doom_ alpha 0.4 and 0.5. + Use `normal` for everything else, including _Doom_ alpha 0.5. You shouldn’t ever have to use those options directly. It’s better to just use `-doom02`, `-doom04`, `-doom05`, and `-doompr`, which take care of setting ipf, itf, and itl properly for you. Note that extracting levels and some other lumps from the _Doom_ alpha iwads does not work yet. Platform ~~~~~~~~ New target in the makefile to generate a binary DOS distribution with the executables and the user documentation in DOS format, with DOS-ish names. 4.0.1 (1999-09-10) ------------------ Command line ~~~~~~~~~~~~ Reworked the command line arguments parsing, with the following consequences. * Options can now be abbreviated freely, as long as the abbreviation is not ambiguous. For example, you can use `-heretic`, `-hereti`, `-heret`, `-here` or `-her` but not `-he` because that could also be the abbreviation for `-help` (or `-hexen`, for that matter). On the other hand, `-h` is allowed because it’s not an abbreviation (there’s really a `-h` option). * `-heretic` and `-hexen` now work (they were “hidden” by `-h[elp]`). * `-v@` has been split in `-v0`, `-v1` ... `-v5` because the new code does not allow excess characters after an option. `-vstring` where string is anything else than `0` through `5` now triggers an error (it used to be accepted silently). I hope no one relied on the old undocumented behaviour. * Certain silly command line arguments that would have worked before would now trigger an error. For example, it used to be possible to type `-extramarital` or `-extermination` for `-extract` but not anymore. The old code defined relatively short options (-ext) and accepted command line arguments as long as the defined option was an initial substring of the command line argument. The new code does the reverse; it defines relatively long options (`-extract`) and accepts command line argument as long as they’re an initial substring of the defined option. Code ~~~~ * Replaced direct testing of `__MSDOS__`, `__OS2__`, `__GNUC__`, `__BORLANDC__` by `DT_CC` and `DT_OS`. This is hopefully going to make Udo’s job a bit easier. * Now uses the same `fopen()` modes for all platforms: `{rw}b` for binary mode and `{rw}` for text mode, as per the ANSI/ISO C standard. This will fix the problem Udo Munk reported with the Cygwin build opening binary files in text mode and thus failing miserably. Note that certain DOS C compilers can be confused so that `{rw}` opens files in _binary_ mode. Don’t do that! If you have problems with text files on DOS, make sure your C compiler is configured so that `{rw}` opens files in _text_ mode. * Added to the distribution archive `gifcodec.c` that I had forgotten to include (it’s not used anyway). * Added to the distribution archive `src/{deusf,deusfos,deutex,deutexos}.def` that I had forgotten to include. I guess that’s Windows/OS/2-only stuff. Doc ~~~ * Updated `making.txt` and renamed it as `INSTALL` for homogeneity. Removed obsolete reference to `alpha.sh` and the file itself. * Made more legal updates. * Documented `DOOMWADDIR` in the man page. Misc ~~~~ * Changed the default graphics format for Unix from GIF to PPM, so that fewer user sites are broken if and when GIF support is removed. For the same reason, added a warning message when `-gif` is used or the first time an image is read from a GIF file. * Changed the lookup order for images to PPM, BMP, GIF (was BMP, GIF, PPM). Platform ~~~~~~~~ * Fixed a couple of things that didn’t work on 16-bit platforms (real-mode DOS). * Now compiles on DOS with Borland C++ 4.0. * Now compiles on DOS with MSC 6.0. The MSC 6.0 build is functional but limited because it can’t allocate blocks larger than 64 kB, which is insufficient for certain images. I can’t use `halloc()` instead of `malloc()` because it does not supporting resizing (i.e. there’s no `hrealloc()` function). * In response to Udo’s remarks, DJGPP and Cygwin are now properly identified (`__DJGPP__` and `__CYGWIN__`). * Added sanity checks on specified-size types Int32 and friends. 4.0.0a3 (1999-09-05) -------------------- Code ~~~~ * Removed incongruous `#define`-ing of `O_BINARY` and `SEEK_SET`. * After Udo Munk’s report, fixed warnings in ** `src/color.c(74)` ** `src/ident.c(583)` ** `src/ident.c(658)` ** `src/mkwad.c(78)` ** `src/mkwad.c(79)` ** `src/mkwad.c(80)` ** `src/mkwad.c(81)` ** `src/picture.c(903)` ** `src/picture.c(912)` Legal ~~~~~ As agreed to by Olivier Montanuy, DeuTex is now entirely GPL’d. Well, _almost_ entirely, since it includes code written by different authors in `lzw.c` and elsewhere. Changed the notices in the source files and added new file `LICENSE` to clarify things. Makefile ~~~~~~~~ * Should now work with all C compilers (removed `-Wall` from `CFLAGS`). * `clean` now removes the DOS executables if they exist. * Does not compile with debug information in by default anymore. * New targets `dall`, `ddt`, `dds`, `ddeutex` and `ddeusf` for compiling with debug information and all warnings. * New target `help`. * New target `distdos`. Platform ~~~~~~~~ * Replaced `unlink()` by `remove()` for portability. Thanks to Udo for reporting this. * On 8.3 filesystems, `make` should not choke on `docsrc/changes.html` anymore. Thanks to Udo for reporting this. Sound ~~~~~ Corrected some misleading endianness comments in `sound.c`. 4.0.0a2 (1999-08-14) -------------------- Game ~~~~ * Easier to use with _Strife_ (now looks for `strife1.wad`, new option `-strife` ). * Easier to use with _Hexen_ (new option `-hexen`). Command line ~~~~~~~~~~~~ * New options `-hexen` and `-strife`. * New option `--version` (prints version number and returns 0). Doc ~~~ Various changes in the man page, in the output of `-help` and `-man` and elsewhere. Makefile ~~~~~~~~ Various improvements. Distribution ~~~~~~~~~~~~ Set modes straight. 4.0.0a1 (1999-08-12) -------------------- General ~~~~~~~ * Fixed many segfaults that were caused by attempts to `fclose (NULL)`. Wad ~~~ * New options `-be`, `-le`, `-ibe`, `-ile`, `-obe`, and `-ole` to control the endianness of the wads. *Caution*: don’t use them if you don’t know what you’re doing! As far as I know, wads are always little-endian regardless of the architecture of the host. Therefore, I see no reason for someone in his/her right mind to create a big-endian wad. Those options are here more for the sake of completeness than anything else. * Made `%` legal in names, to deal with _Strife_’s `INVFONG%` and `INVFONY%`. * _(Also graphics)_ End-of-flats marker is now `F_END` by default instead of `FF_END`. The reason for this change is that, with `F_END`, you don’t need DeuSF to get _Doom_ to see your new flats. Should you need to, it’s still possible to get `FF_END` by using `-fend`. Graphics ~~~~~~~~ * The default transparent colour is now a dark blue-green (rgb:00/2f/2f). It used to be cyan (rgb:00/ff/ff) which was blindingly bright, especially compared to the usually dark colours used in _Doom_ textures. It’s no fun to tweak shades of dark brown on a cyan background. + + To reuse images done with/for a previous version of DeuTex, you need to either invoke DeuTex with `-rgb 0 255 255` or convert your images by replacing all occurrences of colour (0, 255, 255) by colour (0, 47, 47). To preserve compatibility with WinTex, I didn’t change the default transparent colour in WinTex mode; it’s still (0, 255, 255). * Fixed segfaults due to bug in conversion of bitmap images to _Doom_ pictures. Occured in certain 2-pixel high images such as `STBFN045` in the _Strife_ iwad. * Now supports pictures and textures up to 509 high (was limited to 128). * Now supports pictures and textures up to 1024 wide (was limited to 512). * New option `-pf` to deal with the different picture format in the _Doom_ alpha iwad (the underlying functionality is not implemented yet!) * Graphics: using `-ppm` does not cause anymore DeuTex to abort with `Bug: *** psvit ***`. * Graphics: fixed `-ppm` message. Sound ~~~~~ * A bug that must have prevented reading `.wav` files on big endian machines has been squashed. Command line ~~~~~~~~~~~~ * Options can’t start with a slash (`/`) anymore. I don’t think anyone used it and was a silly feature for a Unix program. * Not case insensitive anymore. * Changed the wording of error messages to use “option” instead of “command”. * Added options `-?` and `--help` as synonyms for `-help`. Doc ~~~ * New option `-man` to print help in `troff -man` source format for inclusion in the man page. * The version number is now a free-form string. * Made on-line help more compact. * Updated `making.txt`. * Made a proper `README` file. Makefile ~~~~~~~~ * Renamed `unix` target as `strip`. * New target `install`. * New target `dist`. Platform ~~~~~~~~ Reworked the handling of endianness. DeuTex used to deal with that through a set of macros that swapped bytes whenever the required endianness was not the same as the native endianness. To known the native endianness, DeuTex relied on a macro defined via `-D`. There were two problems with this scheme. Firstly, Olivier got the meaning of “little endian” and “big endian” backwards and defining `LITTLE_ENDIAN` in fact caused DeuTex to believe it was being compiled for a big endian machine. As the glibc headers happen to define `LITTLE_ENDIAN` if the machine is little endian, compiling DeuTex on a glibc little endian Linux system was impossible unless you made changes to the source. The other, more fundamental, objection against the old approach is that, as it needed the user to tell it about the native endianness by modifying the makefile, it prevented unattended builds and made things difficult for naive users. The new method eliminates this problem by using a different algorithm that does not need to know the native endianness. The end result is that you don’t have to worry about endianness anymore. Internal ~~~~~~~~ * In `TXTinit()`, removed useless `% 0xFF` in index of `TXTval`. deutex-5.2.3/README.adoc000066400000000000000000000066551475516661100146340ustar00rootroot00000000000000DeuTex ====== DeuTex can do many things with _Doom_, _Freedoom_, _Heretic_, _Hexen_, and _Strife_ https://doomwiki.org/wiki/WAD[“WAD”] files, such as extracting and inserting graphics, sounds, levels, and other resources. It can be used for creating and modifying IWAD (“Internal WAD”) and PWAD (“Patch WAD”) files both. It is a command-line driven program and as such, is suitable for scripting such as from a shell script or Makefile. File formats ------------ For insertion and extraction, DeuTex supports a few common formats. For music, DeuTex supports both the DMX “mus” file format native to _Doom_, and also MIDI files supported in _Doom_ 1.666 and newer. There is no conversion between these formats, they are inserted and extracted as-is. For audio samples, they are saved and read using simple WAV files containing only uncompressed PCM audio streams. For the various forms of graphics, the BMP (uncompressed only), GIF, PPM, and PNG footnote:[Requires libpng 1.6 or newer.] formats are supported. Unrecognized-format lumps are saved with +.lmp+ extension in the +lumps+ directory and are re-inserted as-is from there. Levels are saved in an encapsulated PWAD file; breaking them apart would not be useful for editing and all the lumps that make up a level are co-dependent on each other. Building and installing ----------------------- DeuTex uses an Autoconf+Automake build system, so its compilation and installation process is identical to most other Unix packages: ./configure make make install When building directly from the version control repository, you will need autoconf and automake installed and run the `./bootstrap` command first to generate the `./configure` file. To build and install the manpage, AsciiDoc must be installed, in particular the DocBook-based `a2x` command for transforming it into manpage format. DeuTex will still build without it, however, and the AsciiDoc source in +man/deutex.txt+ is fairly readable on its own. To build and install on ReactOS or Microsoft Windows will require an environment compatible with Unix shells and programs, such as https://cygwin.com/[Cygwin] or http://www.msys2.org/[MSYS2]. History ------- DeuTex began life as a fork of Doom Editing Utilities (known as DEU for short) by Olivier Montanuy in 1994, expunging the graphical user interface in favor of command line and scriptable usage scenarios. Originally written for DOS, its primary home is now modern Unix and Windows systems, and is a fundamental piece to building _https://freedoom.github.io/[Freedoom’s]_ IWAD files. The name comes from a play on LaTeX and in turn TeX, a popular typesetting system in academia but no technical connection to DeuTex. It is pronounced as two syllables, the first like “due” or “dew,” and the second like “tech” (not “tex”!), owing from its namesake. Copyright --------- Copyright © Olivier Montanuy, André Majorel, contributors to the DeuTex project. 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 2 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 ANY PARTICULAR PURPOSE. See the GNU General Public License for more details, provided under the name +COPYING+. deutex-5.2.3/bootstrap000077500000000000000000000000311475516661100147700ustar00rootroot00000000000000#!/bin/sh autoreconf -i deutex-5.2.3/configure.ac000066400000000000000000000023121475516661100153170ustar00rootroot00000000000000AC_INIT([DeuTex], [5.2.3], [https://github.com/Doom-Utils/deutex/issues]) AC_PACKAGE_URL([https://github.com/Doom-Utils/deutex]) AM_INIT_AUTOMAKE([foreign]) AC_PROG_CC AM_SILENT_RULES([yes]) AC_CHECK_PROG([ASCIIDOC], [a2x], [yes]) AC_ARG_ENABLE([man], AS_HELP_STRING([--disable-man], [Disable manpage generation]), [], [enable_man=check] ) AS_IF([test "x$enable_man" = "xyes"], AS_IF([test "x$ASCIIDOC" != "xyes"], AC_MSG_ERROR([AsciiDoc a2x program is required in order to build the manual]) ) ) AM_CONDITIONAL([ASCIIDOC], [test "x$enable_man" != "xno" && test "x$ASCIIDOC" = "xyes"]) AC_ARG_WITH([libpng], AS_HELP_STRING([--without-libpng], [Build without libpng @<:@default=check@:>@]), [], [[with_libpng=check]]) AS_IF([test "$with_libpng" != no], [PKG_CHECK_MODULES([PNG], [libpng >= 1.6.0], [AC_DEFINE([HAVE_LIBPNG], [1], [libpng installed])], [AS_IF([test "$with_libpng" != check], [AC_MSG_FAILURE([--with-libpng was given, but test for libpng failed]) ]) ]) ]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([ Makefile man/Makefile man/deutex.txt pkg.w32/config.make src/Makefile ]) AC_OUTPUT deutex-5.2.3/man/000077500000000000000000000000001475516661100136065ustar00rootroot00000000000000deutex-5.2.3/man/.gitignore000066400000000000000000000000171475516661100155740ustar00rootroot00000000000000*.6 deutex.txt deutex-5.2.3/man/Makefile.am000066400000000000000000000003231475516661100156400ustar00rootroot00000000000000if ASCIIDOC man_MANS = @PACKAGE@.6 CLEANFILES = @PACKAGE@.6 @PACKAGE@.html @PACKAGE@.6: deutex.txt a2x -f manpage deutex.txt @PACKAGE@.html: deutex.txt TZ=UTC asciidoc deutex.txt html: @PACKAGE@.html endif deutex-5.2.3/man/deutex.txt.in000066400000000000000000000255031475516661100162570ustar00rootroot00000000000000@PACKAGE@(6) ========= :doctype: manpage NAME ---- @PACKAGE@ - do things with wad files SYNOPSIS -------- *@PACKAGE@* *-?*|*-h*|*-help*|*--help* *@PACKAGE@* *--version* *@PACKAGE@* ['OPTIONS'] *-add* 'incomplete.wad' 'out.wad' *@PACKAGE@* ['OPTIONS'] *-af* 'flats.wad' *@PACKAGE@* ['OPTIONS'] *-append* 'incomplete.wad' *@PACKAGE@* ['OPTIONS'] *-as* 'sprite.wad' *@PACKAGE@* ['OPTIONS'] *-check* 'in.wad' *@PACKAGE@* ['OPTIONS'] *-debug* ['in.gif'] *@PACKAGE@* ['OPTIONS'] *-get* 'entry' ['in.wad'] *@PACKAGE@* ['OPTIONS'] *-join* 'incomplete.wad' 'in.wad' *@PACKAGE@* ['OPTIONS'] *-make* ['dirctivs.txt'] 'out.wad' *@PACKAGE@* ['OPTIONS'] *-merge* 'in.wad' *@PACKAGE@* ['OPTIONS'] *-pkgfx* ['in.wad' ['out.txt']] *@PACKAGE@* ['OPTIONS'] *-pknormal* ['in.wad' ['out.txt']] *@PACKAGE@* ['OPTIONS'] *-restore* *@PACKAGE@* ['OPTIONS'] *-usedidx* ['in.wad'] *@PACKAGE@* ['OPTIONS'] *-usedtex* ['in.wad'] *@PACKAGE@* ['OPTIONS'] *-unused* 'in.wad' *@PACKAGE@* ['OPTIONS'] *-wadir* ['in.wad'] *@PACKAGE@* ['OPTIONS'] *-xtract* 'in.wad' ['dirctivs.txt'] DESCRIPTION ----------- @PACKAGE_NAME@ is a wad composer for Doom, Heretic, Hexen and Strife. It can be used to extract the lumps of a wad and save them as individual files or the reverse, and much more. When extracting a lump to a file, it does not just copy the raw data, it converts it to an appropriate format (such as PPM for graphics, Sun audio for samples, etc.). Conversely, when it reads files for inclusion in pwads, it does the necessary conversions (for example, from PPM to Doom picture format). Decomposing a wad ~~~~~~~~~~~~~~~~~ To decompose a wad (i.e. extract its contents), use the *-extract* (a.k.a. *-xtract*) command. When decomposing a wad, @PACKAGE_NAME@ creates one file for each lump. The files are created in one of the following subdirectories of the working directory: *flats/*, *lumps/*, *musics/*, *patches/*, *sounds/*, *sprites/*, *textures/*. The decomposing process also creates a very important file, *wadinfo.txt*, which will be used later when composing. To extract the contents of the Doom II iwad, @PACKAGE@ -doom2 /path/to/doom2.wad -xtract To extract the contents of a Doom II pwad named mywad.wad, @PACKAGE@ -doom2 /path/to/doom2.wad -xtract mywad.wad To extract only the sprites, @PACKAGE@ -doom2 /path/to/doom2.wad -sprites -xtract To extract only the sounds, @PACKAGE@ -doom2 /path/to/doom2.wad -sounds -xtract Composing (building) a wad ~~~~~~~~~~~~~~~~~~~~~~~~~~ Composing is the symmetrical process. It's done with the three commands *-build*, *-create* and *-make*, that are equivalent. Using *wadinfo.txt* and the files in *flats/*, *lumps/*, *musics/*, *patches/*, *sounds/*, *sprites/* and *textures/*, @PACKAGE_NAME@ creates a new wad. To create a new pwad named mywad.wad, @PACKAGE@ -doom2 /path/to/doom2.wad -make mywad.wad To create a new iwad named mytc.wad, @PACKAGE@ -doom2 /path/to/doom2.wad -iwad -make mytc.wad Other operations ~~~~~~~~~~~~~~~~ @PACKAGE_NAME@ has many (too many?) other commands like *-join*, *-merge*, *-usedtex* etc. OPTIONS ------- Modal options not requiring an iwad ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *-?*, *-h*, *-help*, *--help*:: Print list of options. *-syntax*:: Print the syntax of wad creation directives. *--version*:: Print version number and exit immediately. *-unused* 'in.wad':: Find unused spaces in a wad. Modal options requiring an iwad ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *-add* 'in.wad' 'out.wad':: Copy sp & fl of iwad and 'in.wad' to 'out.wad'. *-af* 'flats.wad':: Append all floors/ceilings to the wad. *-append* 'io.wad':: Add sprites & flats of iwad to io.wad. *-as* 'sprite.wad':: Append all sprites to the wad. *-build*|*-create*|*-make* ['in.txt'] 'out.wad':: Make a pwad. *-check*|*-test* 'in.wad':: Check the textures. *-debug* ['file']:: Debug color conversion. *-extract*|*-xtract* ['in.wad' ['out.txt']]:: Extract some/all entries from a wad. *-get* 'entry' ['in.wad']:: Get a wad entry from main wad or in.wad. *-join* 'incomplete.wad' 'in.wad':: Append sprites & flats of Doom to a pwad. *-merge* 'in.wad':: Merge doom.wad and a pwad. *-pkgfx* ['in.wad' ['out.txt']]:: Detect identical graphics. *-pknormal* ['in.wad' ['out.txt']]:: Detect identical normal. *-restore*:: Restore doom.wad and the pwad. *-usedidx* ['in.wad']:: Color index usage statistics. *-usedtex* ['in.wad']:: List textures used in all levels. *-wadir* ['in.wad']:: List and identify entries in a wad. General options ~~~~~~~~~~~~~~~ *-overwrite*:: Overwrite all. *-dir* 'dir':: Extraction directory (default `.`). Iwad ~~~~ *-doom* 'dir':: Path to Doom iwad. *-doom2* 'dir':: Path to Doom II iwad. *-doom02* 'dir':: Path to Doom alpha 0.2 iwad. *-doom04* 'dir':: Path to Doom alpha 0.4 iwad. *-doom05* 'dir':: Path to Doom alpha 0.5 iwad. *-doompr* 'dir':: Path to Doom PR pre-beta iwad. *-heretic* 'dir':: Path to Heretic iwad. *-hexen* 'dir':: Path to Hexen iwad. *-strife* 'dir':: Path to Strife iwad. *-strife10* 'dir':: Path to Strife 1.0 iwad. Wad options ~~~~~~~~~~~ *-be*:: Assume all wads are big endian (default LE). *-deu*:: Add 64k of junk for DEU 5.21 compatibility. *-george*|*-s_end*:: Use *S_END* for sprites, not *SS_END*. *-ibe*:: Input wads are big endian (default LE). *-ile*:: Input wads are little endian (default). *-ipf* 'code':: Picture format (*alpha*, *normal*, *pr*; default normal). *-itf* 'code':: Input texture format (*nameless*, *none*, *normal*, *strife11*; default normal). *-itl* 'code':: Texture lump (*none*, *normal*, *textures*; default normal). *-iwad*:: Compose iwad, not pwad. *-le*:: Assume all wads are little endian (default). *-obe*:: Create big endian wads (default LE). *-ole*:: Create little endian wads (default). *-otf* 'code':: Output texture format (*nameless*, *none*, *normal*, *strife11*; default normal). *-pngoffsets*:: Override offsets in WADINFO with offsets contained in PNG metadata *-tf* 'code':: Texture format (*nameless*, *none*, *normal*, *strife11*; default normal). Lump selection ~~~~~~~~~~~~~~ *-flats*:: Select flats. *-graphics*:: Select graphics. *-levels*:: Select levels. *-lumps*:: Select lumps. *-musics*:: Select musics. *-patches*:: Select patches. *-scripts*:: Select Strife scripts. *-sneas*:: Select sneas (sneaps and sneats). *-sneaps*:: Select sneaps. *-sneats*:: Select sneats. *-sounds*:: Select sounds. *-sprites*:: Select sprites. *-textures*:: Select textures. Graphics ~~~~~~~~ *-bmp*:: Save pictures as BMP (*.bmp*). *-png*:: Save pictures as PNG (*.png*). Default format. *-gif*:: Save pictures as GIF (*.gif*). *-ppm*:: Save pictures as rawbits PPM (P6, *.ppm*). *-rgb* 'r' 'g' 'b':: Specify the transparent colour (default 0 47 47). Sound ~~~~~ *-rate* 'code':: Policy for != 11025 Hz (*reject*, *force*, *warn*, *accept*; default warn). Reporting ~~~~~~~~~ *-di* 'name':: Debug identification of entry. *-v0*|*-v1*|*-v2*|*-v3*|*-v4*|*-v5*:: Set verbosity level, default 2. DIAGNOSTICS ----------- All messages are identified by a unique code. Some messages are identical; the code is useful to distinguish them. All codes have four characters: two letters and two digits. The letters identify the part of the code where the message comes from, the digits give the message number within that area. In general, numbers are assigned so that messages that come from parts of the code that are executed earlier have lower numbers. FILES ----- 'dir'*/flats/*:: When extracting, flats are saved to this directory. When composing, flats are read from this directory. 'dir'*/graphics/*:: When extracting, graphics are saved to this directory. When composing, graphics are read from this directory. 'dir'*/levels/*:: When extracting, levels are saved to this directory. When composing, levels are read from this directory. 'dir'*/lumps/*:: When extracting, lumps are saved to this directory. When composing, lumps are read from this directory. 'dir'*/musics/*:: When extracting, musics are saved to this directory. When composing, musics are read from this directory. 'dir'*/patches/*:: When extracting, patches are saved to this directory. When composing, patches are read from this directory. 'dir'*/scripts/*:: When extracting, Strife scripts are saved to this directory. When composing, Strife scripts are read from this directory. 'dir'*/sneaps/*:: When extracting, Doom alpha sneaps are saved to this directory. When composing, Doom alpha sneaps are read from this directory. 'dir'*/sneats/*:: When extracting, Doom alpha sneats are saved to this directory. When composing, Doom alpha sneats are read from this directory. 'dir'*/sounds/*:: When extracting, sounds are saved to this directory. When composing, sounds are read from this directory. 'dir'*/sprites/*:: When extracting, sprites are saved to this directory. When composing, sprites are read from this directory. 'dir'*/textures/texture1.txt*:: The *TEXTURE1* lump (all but Doom alpha 0.4 and 0.5). 'dir'*/textures/texture2.txt*:: The *TEXTURE2* lump (all commercial IWADs except Doom 2). 'dir'*/textures/textures.txt*:: The *TEXTURES* lump (Doom alpha 0.4 and 0.5). 'dir'*/tx_start/*:: Special texture directory for certain engines such as ZDoom. Specifying a positive integer after the name in wadinfo.txt causes no format conversion to be performed (eg, PNGs and BMPs remain as PNGs and BMPs in the WAD), otherwise an attempt to convert to Doom’s patch format is done. 'dir'*/wadinfo.txt*:: The default master file. ENVIRONMENT ----------- *DOOMWADDIR*:: The directory where the iwad resides. The value of this environment variable is overridden by *-main*, *-doom* and friends. COPYRIGHT --------- @PACKAGE_NAME@ is copyright © 1994-1995 Olivier Montanuy, copyright © 1999-2005 André Majorel, copyright © 2006-2021 contributors to @PACKAGE_NAME@. Most of this program is under the GNU General Public License version 2, but some of it is available under other licenses. 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 LICENSE for specific information and copyright notices. All trademarks are property of their owners. AUTHORS ------- The original author of @PACKAGE_NAME@ is Olivier Montanuy. From 1994 to 1996, @PACKAGE_NAME@ was maintained by Olivier Montanuy with help from Per Allansson, James Bonfield, Sharon Bowles, Mark Mathews, and Chuck Rossi. The original manual was written by Kevin McGrail. From version 4.0 (1999) through 4.4.902 (2005), the maintainer was André Majorel (http://www.teaser.fr/~amajorel). The project has since been maintained by a loose collaboration of authors primarily as part of the Debian project and Freedoom project. They include Jon Dowland, Simon Howard, Mike Swanson, RjY, Ayub Ahmed, and Nick Zatkovich. REPORTING BUGS -------------- Please report bugs to the issue tracker at https://github.com/Doom-Utils/deutex. deutex-5.2.3/pkg.w32/000077500000000000000000000000001475516661100142265ustar00rootroot00000000000000deutex-5.2.3/pkg.w32/.gitignore000066400000000000000000000000441475516661100162140ustar00rootroot00000000000000!Makefile *.zip config.make staging deutex-5.2.3/pkg.w32/Makefile000066400000000000000000000013641475516661100156720ustar00rootroot00000000000000include config.make DOC_HTML_FILES = ../NEWS.html ../README.html ../man/$(PACKAGE).html DOC_TXT_FILES = ../AUTHORS ../COPYING ZIP=$(PACKAGE)-$(PACKAGE_VERSION)_w32.zip all: $(ZIP) clean: rm -rf staging $(ZIP) $(ZIP): staging zip -Xjr $@ $ 0: filename = to_process.pop() for dll in file_dependencies(filename, objdump): try: dll = find_dll(dll, dll_paths) if dll not in result: result |= {dll} to_process |= {dll} except IOError as e: missing |= {dll} return result, missing def get_dll_path(): """Examine command line arguments and determine the DLL search path. If the --path argument is provided, paths from this are added. Furthermore, if --ldflags is provided, this is interpreted as a list of linker flags and the -L paths are used to find associated paths that are likely to contain DLLs, with the assumption that autotools usually installs DLLs to ${prefix}/bin when installing Unix-style libraries into ${prefix}/lib. Returns: List of filesystem paths to check for DLLs. """ result = set(args.dll_path) if args.ldflags != "": for arg in shlex.split(args.ldflags): if arg.startswith("-L"): prefix, libdir = os.path.split(arg[2:]) if libdir != "lib": continue bindir = os.path.join(prefix, "bin") if os.path.exists(bindir): result |= {bindir} return list(result) args = parser.parse_args() dll_path = get_dll_path() dll_files, missing = all_dependencies(args.source, args.objdump, dll_path) # Exit with failure if DLLs are missing. if missing: sys.stderr.write("Missing DLLs not found in path %s:\n" % (dll_path,)) for filename in missing: sys.stderr.write("\t%s\n" % filename) sys.exit(1) # Destination may be a full path (rename) or may be a directory to copy into: # cp foo.exe bar/baz.exe # cp foo.exe bar/ if os.path.isdir(args.destination): dest_dir = args.destination else: dest_dir = os.path.dirname(args.destination) # Copy .exe and DLLs. shutil.copy(args.source, args.destination) for filename in dll_files: shutil.copy(filename, dest_dir) deutex-5.2.3/src/000077500000000000000000000000001475516661100136225ustar00rootroot00000000000000deutex-5.2.3/src/.gitignore000066400000000000000000000000271475516661100156110ustar00rootroot00000000000000*.o *.exe deutex .deps deutex-5.2.3/src/Makefile.am000066400000000000000000000007131475516661100156570ustar00rootroot00000000000000bin_PROGRAMS = deutex deutex_SOURCES = color.c color.h compose.c deutex.c deutex.h \ endianio.c endianio.h endianm.c endianm.h extract.c extract.h ident.c \ ident.h listdir.c lists.c lists.h lzw.c merge.c merge.h mkwad.c \ mkwad.h png_tools.c png_tools.h picture.c picture.h sound.c sound.h \ sscript.c sscript.h text.c text.h texture.c texture.h tools.c tools.h \ usedidx.c usedidx.h wadio.c wadio.h deutex_LDADD = ${PNG_LIBS} AM_CPPFLAGS = ${PNG_CFLAGS} deutex-5.2.3/src/color.c000066400000000000000000000226211475516661100151070ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ #include "deutex.h" #include "tools.h" #include "color.h" /* ** hash table ** */ uint8_t COLindex(uint8_t R, uint8_t G, uint8_t B, uint8_t index); static uint8_t COLpalMatch(uint8_t R, uint8_t G, uint8_t B); static struct PIXEL *COLpal; /* The game palette (comes from PLAYPAL) */ static struct PIXEL *COLpalAlt = NULL; /* Alternate palette (TITLEPAL) */ static struct PIXEL COLinv; static uint8_t COLinvisib; static bool COLok = false; /* * pixel_cmp * Compare two PIXEL structures à la memcmp() */ static int pixel_cmp(const void *pixel1, const void *pixel2) { const struct PIXEL *const p1 = (const struct PIXEL *) pixel1; const struct PIXEL *const p2 = (const struct PIXEL *) pixel2; if (p1->R != p2->R) return p1->R - p2->R; if (p1->G != p2->G) return p1->G - p2->G; if (p1->B != p2->B) return p1->B - p2->B; return 0; } const int COLsame = 3; /* * COLdiff - compute the distance between two colours * * Return a number between 0 (closest) and 24384 (farthest). */ int16_t COLdiff(uint8_t R, uint8_t G, uint8_t B, uint8_t idx) { register struct PIXEL *pixel = &COLpal[(int16_t) (idx & 0xFF)]; register int16_t d; /*signed */ register int16_t e = 0; d = (((int16_t) R) & 0xFF) - (((int16_t) pixel->R) & 0xFF); d >>= 1; e += d * d; d = (((int16_t) G) & 0xFF) - (((int16_t) pixel->G) & 0xFF); d >>= 1; e += d * d; d = (((int16_t) B) & 0xFF) - (((int16_t) pixel->B) & 0xFF); d >>= 1; e += d * d; if (e < 0) return 0x7FFF; return e; } static uint8_t COLpalMatch(uint8_t R, uint8_t G, uint8_t B) { int16_t i, test, min = 0x7FFF; uint8_t idxmin = '\0'; if (!COLok) Bug("PL24", "COLok"); for (i = 0; i < 256; i++) { if ((uint8_t) i != COLinvisib) { test = COLdiff(R, G, B, (uint8_t) i); if (test < min) { min = test; idxmin = (uint8_t) i; } if (min < COLsame) { break; } } } return idxmin; } /* Choose only 10,11...16 */ #define POWER 12 const int16_t HashP2 = POWER; /* 10=1024 */ const int16_t HashSz = 1 << POWER; /* 1<< HashP2 */ const int16_t HashMask = (1 << POWER) - 1; /* HashSz-1 */ /*const int16_t HashStop = -1;*/ static uint8_t *COLhash; /*hash table */ /*static int16_t *COLnext;*/ int16_t Hash(uint8_t r, uint8_t g, uint8_t b) { int res; uint8_t R = r & 0xFC, G = g & 0xFC, B = b & 0xFC; res = (((R << 3) ^ G) << 2) ^ B; res = (res << 3) + (R & 0xC3) + (G & 0x61) + (~B & 0x98); res = (res << 5) + (R & 0x3B) + (~G & 0x95) + (B & 0x33); res ^= res >> 8; res &= HashMask; return (int16_t) res; } /*original colors*/ static void COLputColHash(int16_t index, uint8_t R, uint8_t G, uint8_t B) { int16_t count, idx, nextidx; idx = Hash(R, G, B); for (count = 0; count < 8; count++) { nextidx = (idx + count) & HashMask; if (COLhash[nextidx] == COLinvisib) { COLhash[nextidx] = (uint8_t) (index & 0xFF); return; } } Bug("PL07", "Can't hash Doom pal"); } /*new colors, with matching*/ static uint8_t COLgetIndexHash(uint8_t R, uint8_t G, uint8_t B) { int16_t idx, nextidx, count; uint8_t res; idx = Hash(R, G, B); for (count = 0; count < 8; count++) { nextidx = (idx + count) & HashMask; res = COLhash[nextidx]; if (res == COLinvisib) { /*free */ COLhash[nextidx] = res = COLpalMatch(R, G, B); return res; } else if (COLdiff(R, G, B, res) < COLsame) { return res; } } /*no good solution. slow match */ return COLpalMatch(R, G, B); } /* * Normal palette (PLAYPAL) */ void COLinit(uint8_t invR, uint8_t invG, uint8_t invB, char *Colors, int16_t Colsz, const char *pathname, const char *lumpname) { int16_t i; const char *name = NULL; /*int16_t R,G,B; */ if (COLok) Bug("PL02", "COLok"); if (Colsz < 256 * sizeof(struct PIXEL)) { if (lumpname == NULL) { ProgError("PL03", "%s: wrong size for PLAYPAL", fname(pathname)); } else { ProgError("PL04", "%s: %s: wrong size for PLAYPAL", fname(pathname), lump_name(lumpname)); } } COLok = true; COLpal = (struct PIXEL *) Malloc(256 * sizeof(struct PIXEL)); for (i = 0; i < NCOLOURS; i++) { COLpal[i].R = Colors[i * 3 + 0]; COLpal[i].G = Colors[i * 3 + 1]; COLpal[i].B = Colors[i * 3 + 2]; } if (COLpal[0].R == 0 && COLpal[0].G == 0 && COLpal[0].B == 0 && COLpal[0xf7].R == 0 && COLpal[0xf7].G == 0 && COLpal[0xf7].B == 0) { i = 0xf7; name = "Doom"; } else if (COLpal[35].R == 255 && COLpal[35].G == 255 && COLpal[35].B == 255 && COLpal[255].R == 255 && COLpal[255].G == 255 && COLpal[255].B == 255) { i = 0xff; name = "Heretic"; } else if (COLpal[33].R == 29 && COLpal[33].G == 32 && COLpal[33].B == 29 && COLpal[255].R == 255 && COLpal[255].G == 255 && COLpal[255].B == 255) { i = 0xff; name = "Hexen"; } else if (COLpal[0].R == 0 && COLpal[0].G == 0 && COLpal[0].B == 0 && COLpal[240].R == 0 && COLpal[240].G == 0 && COLpal[240].B == 0) { i = 0xf0; name = "Strife"; } else { i = 0xff; name = NULL; } /* ** correction to doom palette */ COLinvisib = (uint8_t) (i & 0xFF); Info("PL05", "Palette is %s", name ? name : "(unknown)"); if (name == NULL) { Warning("PL06", "Unknown palette, using colour 0xff as transparent colour"); Warning("PL06", "Some graphics may appear moth-eaten"); } COLinv.R = COLpal[i].R = invR; COLinv.G = COLpal[i].G = invG; COLinv.B = COLpal[i].B = invB; /* Init hash table. We take special care of hashing only unique RGB triplets. This precaution is unnecessary for Doom, Heretic, Hexen and Strife but Doom alpha O.2, 0.4 and 0.5 have a PLAYPAL that contains many duplicates that would fill the hash table with useless data. */ { struct PIXEL *unique = Malloc(NCOLOURS * sizeof *unique); for (i = 0; i < NCOLOURS; i++) unique[i] = COLpal[i]; qsort(unique, NCOLOURS, sizeof *unique, pixel_cmp); COLhash = (uint8_t *) Malloc(HashSz); Memset(COLhash, COLinvisib, HashSz); /*clear hash table */ for (i = 0; i < NCOLOURS; i++) { if ((uint8_t) i != COLinvisib && (i == 0 || pixel_cmp(unique + i, unique + i - 1) != 0)) COLputColHash(i, unique[i].R, unique[i].G, unique[i].B); } free(unique); } } void COLfree(void) { if (!COLok) Bug("PL99", "COLok"); COLok = false; free(COLpal); free(COLhash); if (COLpalAlt != NULL) free(COLpalAlt); } uint8_t COLinvisible(void) { if (!COLok) Bug("PL27", "COLok"); return COLinvisib; } struct PIXEL *COLdoomPalet(void) { if (!COLok) Bug("PL20", "COLok"); return COLpal; } uint8_t COLindex(uint8_t R, uint8_t G, uint8_t B, uint8_t index) { int16_t i; if (!COLok) Bug("PL23", "COLok"); /*check for invisible color */ if (R == COLinv.R && G == COLinv.G && B == COLinv.B) return COLinvisib; /*check for DOOM palette */ i = ((int16_t) index) & 0xFF; if (R == COLpal[i].R) if (G == COLpal[i].G) if (B == COLpal[i].B) return index; /*else, check hash palette */ i = (int16_t) COLgetIndexHash(R, G, B); return (uint8_t) i; } /* * Alternate palette (TITLEPAL) */ static char *titlepal_data = NULL; static size_t titlepal_size = 0; void COLinitAlt(char *_titlepal_data, int32_t _titlepal_size) { titlepal_data = _titlepal_data; titlepal_size = _titlepal_size; } struct PIXEL *COLaltPalet(void) { if (COLpalAlt != NULL) return COLpalAlt; /* What follows is done only once : */ if (titlepal_data == NULL) { int n; Warning("PL11", "TITLEPAL not found, using PLAYPAL instead"); COLpalAlt = (struct PIXEL *) Malloc(NCOLOURS * sizeof *COLpalAlt); for (n = 0; n < NCOLOURS; n++) COLpalAlt[n] = COLpal[n]; } else { struct PIXEL *p; struct PIXEL *pmax; const unsigned char *d = (const unsigned char *) titlepal_data; const unsigned char *dmax = d + titlepal_size; if (titlepal_size < 3 * NCOLOURS) Warning("PL13", "TITLEPAL too short (%ld), filling with black", (long) titlepal_size); COLpalAlt = (struct PIXEL *) Malloc(NCOLOURS * sizeof *COLpalAlt); /* Copy the contents of TITLEPAL into COLpalAlt */ for (p = COLpalAlt, pmax = p + NCOLOURS; p < pmax; p++) { p->R = d < dmax ? *d++ : 0; p->G = d < dmax ? *d++ : 0; p->B = d < dmax ? *d++ : 0; } free(titlepal_data); titlepal_data = NULL; /* Paranoia */ } return COLpalAlt; } deutex-5.2.3/src/color.h000066400000000000000000000015361475516661100151160ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ /*init colors before any operation*/ void COLinit(uint8_t invR, uint8_t invG, uint8_t invB, char *Colors, int16_t Colsz, const char *pathname, const char *lumpname); void COLinitAlt(char *_titlepal_data, int32_t _titlepal_size); void COLfree(void); /*cross reference for picture.c only*/ struct PIXEL { uint8_t R; uint8_t G; uint8_t B; }; uint8_t COLindex(uint8_t R, uint8_t G, uint8_t B, uint8_t idx); uint8_t COLinvisible(void); struct PIXEL *COLdoomPalet(void); struct PIXEL *COLaltPalet(void); /*end of cross reference for picture.c*/ deutex-5.2.3/src/compose.c000066400000000000000000000661051475516661100154430ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ #include "deutex.h" #include #include "tools.h" #include "mkwad.h" #include "texture.h" #include "ident.h" #include "color.h" #include "picture.h" #include "sound.h" #include "text.h" static void AddSomeJunk(const char *file); /* ** Translate TEXTURE1,TEXTURE2 and PNAME in ** a texture list for future modifications */ extern char file[128]; /* ** make a PWAD from creation directives ** load levels,lumps, ** create textures ** load sounds, pics, sprites, patches, flats, */ /* ** Can't handle PATCHES redefined from WAD */ static bool CMPOcopyFromWAD(int32_t * size, struct WADINFO *rwad, const char *DataDir, const char *Dir, const char *nam, const char *filenam) { static struct WADINFO pwad; int16_t entry; if (!MakeFileName(file, DataDir, Dir, "", filenam, "WAD")) return false; WADRopenR(&pwad, file); entry = WADRfindEntry(&pwad, nam); if (entry >= 0) { *size = WADRwriteWADentry(rwad, &pwad, entry); } WADRclose(&pwad); if (entry <= 0) return false; return true; } /* ** find a picture. ** must=true is picture must exist ** returns picture type */ static int16_t CMPOloadPic(int32_t * size, struct WADINFO *rwad, char *file, const char *DataDir, const char *Dir, const char *nam, const char *filenam, int16_t Type, int16_t OfsX, int16_t OfsY) { int res = PICNONE; if (MakeFileName(file, DataDir, Dir, "", filenam, "ppm")) res = PICPPM; else if (MakeFileName(file, DataDir, Dir, "", filenam, "bmp")) res = PICBMP; #ifdef HAVE_LIBPNG else if (MakeFileName(file, DataDir, Dir, "", filenam, "png")) res = PICPNG; #endif else if (MakeFileName(file, DataDir, Dir, "", filenam, "gif")) res = PICGIF; else if (CMPOcopyFromWAD(size, rwad, DataDir, Dir, nam, filenam)) return PICWAD; if (res != PICNONE) *size = PICsaveInWAD(rwad, file, Type, OfsX, OfsY, res); else if (Type != PLUMP) #ifdef HAVE_LIBPNG Warning("PC90", "could not find file %s, .png, .ppm, .bmp or .gif", file); #else Warning("PC90", "could not find file %s, .ppm, .bmp or .gif", file); #endif return res; } /* ** find a raw picture, for TX_START support. */ static bool CMPOloadRawPic(int32_t * size, struct WADINFO *rwad, char *file, const char *DataDir, const char *Dir, const char *nam, const char *filenam) { /* most ports support PNG, ZDoom also supports JPEG */ if (MakeFileName(file, DataDir, Dir, "", filenam, "jpeg") || MakeFileName(file, DataDir, Dir, "", filenam, "jpg") || MakeFileName(file, DataDir, Dir, "", filenam, "png")) { *size = WADRwriteLump(rwad, file); return true; } /* file has the last filename tried, i.e. NAME.png */ Warning("PC91", "could not find file %s, .gif, or .jpeg", file); return false; } struct WADINFO *CMPOrwad; const char *CMPOwadout = NULL; void CMPOerrorAction(void) { if (CMPOwadout == NULL) return; WADRclose(CMPOrwad); /*close file */ remove(CMPOwadout); /*delete file */ } void CMPOmakePWAD(const char *doomwad, WADTYPE type, const char *PWADname, const char *DataDir, const char *texin, NTRYB select, char trnR, char trnG, char trnB, bool George) { /* ** type PWAD as we are generating a real PWAD */ int32_t start = 0, size = 0; static char name[8]; static char filenam[8]; /*PNAMES */ int16_t nbPatchs, p; bool NeedPNAME = false; bool FoundOne = false; bool Repeat; IMGTYPE Picture; /*optional insertion point */ int16_t X, Y; /*text file to read */ static struct TXTFILE *TXT; /*DOOM wad */ static struct WADINFO iwad, pwad; /*result wad file */ static struct WADINFO rwad; /*for Pnames */ int16_t entry; char *EntryP; int32_t EntrySz = 0; /* initialisation */ Info("CM01", "Composing %cWAD %s from %s", (type == IWAD) ? 'I' : 'P', fname(PWADname), texin); /*open iwad,get iwad directory */ iwad.ok = 0; WADRopenR(&iwad, doomwad); TXT = TXTopenR(texin, 0); WADRopenW(&rwad, PWADname, type, 1); /* fake IWAD or real PWAD */ /* ** dirty: set error handler to delete the wad out file, ** if an error occurs. */ CMPOrwad = &rwad; CMPOwadout = PWADname; ProgErrorAction(CMPOerrorAction); /* ** levels! add your own new levels to DOOM! ** read level from a PWAD file */ if (select & BLEVEL) { if (TXTseekSection(TXT, "LEVELS")) { while (TXTentryParse (name, filenam, &X, &Y, &Repeat, TXT, false)) { p = IDENTlevel(name); if (p < 0) ProgError("CM11", "Illegal level name %s", lump_name(name)); if (!MakeFileName (file, DataDir, "LEVELS", "", filenam, "WAD")) ProgError("CM12", "Can't find level WAD %s", fname(file)); Detail("CM13", "Reading level WAD file %s", fname(file)); WADRwriteWADlevel(&rwad, file, name); } } } /* ** prepare palette for graphics */ /*find PLAYPAL */ if (select & (BGRAPHIC | BSPRITE | BPATCH | BFLAT)) { /* If wadinfo.txt mentions a custom PLAYPAL, use that. Otherwise, use the one in the iwad. */ char *paldata = NULL; const char *playpal_pathname = NULL; const char *playpal_lumpname = NULL; if (TXTseekSection(TXT, "LUMPS")) { while (TXTentryParse (name, filenam, &X, &Y, &Repeat, TXT, false)) { if (strcmp(name, "PLAYPAL") == 0) { FILE *playpal_fp; MakeFileName(file, DataDir, "LUMPS", "", filenam, "LMP"); playpal_pathname = file; playpal_lumpname = NULL; EntrySz = 768; paldata = Malloc(EntrySz); playpal_fp = fopen(file, FOPEN_RB); if (playpal_fp == NULL) ProgError("CM21", "%s: %s", fname(file), strerror(errno)); EntrySz = fread(paldata, 1, EntrySz, playpal_fp); if (EntrySz != 768) /* DEBUG was ProgError */ Warning("CM22", "%s: short read", fname(file)); fclose(playpal_fp); break; } } } if (paldata == NULL) { playpal_pathname = iwad.filename; playpal_lumpname = palette_lump; entry = WADRfindEntry(&iwad, palette_lump); if (entry < 0) ProgError("CM23", "Can't find %s in main WAD", lump_name(palette_lump)); paldata = WADRreadEntry(&iwad, entry, &EntrySz); } COLinit(trnR, trnG, trnB, paldata, (int16_t) EntrySz, playpal_pathname, playpal_lumpname); free(paldata); } /* ** ** lumps. non graphic raw data for DOOM */ if (select & BLUMP) { start = size = 0; if (TXTseekSection(TXT, "LUMPS")) { Phase("CM30", "Making lumps"); while (TXTentryParse (name, filenam, &X, &Y, &Repeat, TXT, false)) { if (!Repeat) { WADRalign4(&rwad); /*align entry on int32_t word */ start = WADRposition(&rwad); if (MakeFileName (file, DataDir, "LUMPS", "", filenam, "LMP")) { size = WADRwriteLump(&rwad, file); } else { Picture = CMPOloadPic(&size, &rwad, file, DataDir, "LUMPS", name, filenam, PLUMP, X, Y); if (Picture == PICNONE) if (!CMPOcopyFromWAD (&size, &rwad, DataDir, "LUMPS", name, filenam)) ProgError("CM31", "Can't find lump or picture file %s", file); } } WADRdirAddEntry(&rwad, start, size, name); } } } /* ** initialise list of patch names */ if (select & (BTEXTUR | BPATCH)) { entry = WADRfindEntry(&iwad, "PNAMES"); if (entry < 0) ProgError("CM40", "Can't find PNAMES in main WAD"); EntryP = WADRreadEntry(&iwad, entry, &EntrySz); PNMinit(EntryP, EntrySz); free(EntryP); NeedPNAME = false; } /* ** read texture1 */ if (select & BTEXTUR) { if (TXTseekSection(TXT, "TEXTURE1")) { Phase("CM50", "Making TEXTURE1"); TXUinit(); entry = WADRfindEntry(&iwad, "TEXTURE1"); if (entry >= 0) { EntryP = WADRreadEntry(&iwad, entry, &EntrySz); TXUreadTEXTURE("TEXTURE1", EntryP, EntrySz, NULL, 0, true); free(EntryP); } else Warning("CM51", "Can't find TEXTURE1 in main WAD"); FoundOne = false; /*read TEXTURES composing TEXTURE1 */ while (TXTentryParse (name, filenam, &X, &Y, &Repeat, TXT, false)) { if (MakeFileName (file, DataDir, "TEXTURES", "", filenam, "TXT")) { Detail("CM52", "Reading texture file %s", fname(file)); TXUreadTexFile(file, true); NeedPNAME = true; FoundOne = true; } else if (MakeFileName (file, DataDir, "TEXTURES", "", name, "WAD")) { Detail("CM53", "Reading texture WAD %s", fname(file)); WADRopenR(&pwad, file); entry = WADRfindEntry(&pwad, "TEXTURE1"); if (entry >= 0) { EntryP = WADRreadEntry(&pwad, entry, &EntrySz); TXUreadTEXTURE("TEXTURE1", EntryP, EntrySz, NULL, 0, true); free(EntryP); NeedPNAME = true; FoundOne = true; } WADRclose(&pwad); } else ProgError("CM54", "Can't find texture list %s", file); } /*write texture */ if (FoundOne) { WADRalign4(&rwad); /*align entry on int32_t word */ start = WADRposition(&rwad); size = TXUwriteTEXTUREtoWAD(&rwad); WADRdirAddEntry(&rwad, start, size, "TEXTURE1"); } TXUfree(); } } /* ** read texture2 */ if (select & BTEXTUR) { if (TXTseekSection(TXT, "TEXTURE2")) { Phase("CM55", "Making TEXTURE2"); TXUinit(); entry = WADRfindEntry(&iwad, "TEXTURE2"); if (entry >= 0) { EntryP = WADRreadEntry(&iwad, entry, &EntrySz); TXUreadTEXTURE("TEXTURE2", EntryP, EntrySz, NULL, 0, true); free(EntryP); } else Warning("CM56", "Can't find TEXTURE2 in main WAD"); FoundOne = false; /*read TEXTURES composing TEXTURE2 */ while (TXTentryParse (name, filenam, &X, &Y, &Repeat, TXT, false)) { if (MakeFileName (file, DataDir, "TEXTURES", "", filenam, "TXT")) { Detail("CM57", "Reading texture file %s", fname(file)); TXUreadTexFile(file, true); NeedPNAME = true; FoundOne = true; } else if (MakeFileName (file, DataDir, "TEXTURES", "", name, "WAD")) { Detail("CM58", "Reading texture WAD %s", fname(file)); WADRopenR(&pwad, file); entry = WADRfindEntry(&pwad, "TEXTURE2"); if (entry >= 0) { EntryP = WADRreadEntry(&pwad, entry, &EntrySz); TXUreadTEXTURE("TEXTURE2", EntryP, EntrySz, NULL, 0, true); free(EntryP); NeedPNAME = true; FoundOne = true; } WADRclose(&pwad); } else ProgError("CM59", "Can't find texture list %s", file); } /*write texture */ if (FoundOne) { WADRalign4(&rwad); /*align entry on int32_t word */ start = WADRposition(&rwad); size = TXUwriteTEXTUREtoWAD(&rwad); WADRdirAddEntry(&rwad, start, size, "TEXTURE2"); } TXUfree(); } } /* ** PNAME */ if (select & BTEXTUR) { if (NeedPNAME) { /*write PNAME in PWAD */ Phase("CM41", "Making PNAMES"); WADRalign4(&rwad); /*align entry on int32_t word */ start = WADRposition(&rwad); size = PNMwritePNAMEtoWAD(&rwad); WADRdirAddEntry(&rwad, start, size, "PNAMES"); } } /* ** TX_START ** textures (patch or PNG format) ** TX_END */ if (select & BTEXTUR) { start = size = 0; if (TXTseekSection(TXT, "TX_START")) { Phase("CM59", "Making TX_START textures"); FoundOne = false; while (TXTentryParse (name, filenam, &X, &Y, &Repeat, TXT, true)) { /* only add markers if we have some textures */ if (!FoundOne) { WADRalign4(&rwad); /*align entry on int32_t word */ start = WADRposition(&rwad); WADRdirAddEntry(&rwad, start, 0L, "TX_START"); FoundOne = true; } WADRalign4(&rwad); /*align entry on int32_t word */ start = WADRposition(&rwad); size = 0; /* when X value is > 0, insert as a raw lump (no conversion) */ if (X > 0) { CMPOloadRawPic(&size, &rwad, file, DataDir, "TX_START", name, filenam); } else { CMPOloadPic(&size, &rwad, file, DataDir, "TX_START", name, filenam, PGRAPH, X, Y); } WADRdirAddEntry(&rwad, start, size, name); } if (FoundOne) { WADRalign4(&rwad); start = WADRposition(&rwad); WADRdirAddEntry(&rwad, start, 0L, "TX_END"); } } } /* ** ** sounds. all sounds entries */ if (select & BSOUND) { start = size = 0; if (TXTseekSection(TXT, "SOUNDS")) { Phase("CM60", "Making sounds"); while (TXTentryParse (name, filenam, &X, &Y, &Repeat, TXT, false)) { if (!Repeat) { WADRalign4(&rwad); /*align entry on int32_t word */ start = WADRposition(&rwad); if (MakeFileName (file, DataDir, "SOUNDS", "", filenam, "TXT")) { size = SNDcopyPCSoundInWAD(&rwad, file); Detail("CM62", "Reading PC sound from file %s", fname(file)); } else { if (MakeFileName (file, DataDir, "SOUNDS", "", filenam, "WAV")) { size = SNDcopyInWAD(&rwad, file, SNDWAV); } else { if (!CMPOcopyFromWAD (&size, &rwad, DataDir, "SOUNDS", name, filenam)) { ProgError("CM63", "Can't find sound %s, WAV", file); } } Detail("CM64", "Reading sound file %s", fname(file)); } } WADRdirAddEntry(&rwad, start, size, name); } } } /* ** ** Musics */ if (select & BMUSIC) { start = size = 0; if (TXTseekSection(TXT, "MUSICS")) { Phase("CM65", "Making musics"); while (TXTentryParse (name, filenam, &X, &Y, &Repeat, TXT, false)) { if (!Repeat) { WADRalign4(&rwad); /*align entry on int32_t word */ start = WADRposition(&rwad); /*Music */ if (MakeFileName (file, DataDir, "MUSICS", "", filenam, "MUS")) { size = WADRwriteLump(&rwad, file); Detail("CM66", "Reading music file %s", fname(file)); } else if (MakeFileName (file, DataDir, "MUSICS", "", filenam, "MID")) { size = WADRwriteLump(&rwad, file); Detail("CM68", "Reading music file %s", fname(file)); } else if (!CMPOcopyFromWAD (&size, &rwad, DataDir, "MUSICS", name, filenam)) ProgError("CM67", "Can't find music %s", file); } WADRdirAddEntry(&rwad, start, size, name); } } } /* ** ordinary graphics */ if (select & BGRAPHIC) { start = size = 0; if (TXTseekSection(TXT, "GRAPHICS")) { Phase("CM70", "Making graphics"); while (TXTentryParse (name, filenam, &X, &Y, &Repeat, TXT, true)) { if (!Repeat) { WADRalign4(&rwad); /*align entry on int32_t word */ start = WADRposition(&rwad); Picture = CMPOloadPic(&size, &rwad, file, DataDir, "GRAPHICS", name, filenam, PGRAPH, X, Y); } WADRdirAddEntry(&rwad, start, size, name); } } } /* ** SS_START ** sprites ** SS_END */ if (select & BSPRITE) { start = size = 0; if (TXTseekSection(TXT, "SPRITES")) { Phase("CM75", "Making sprites"); FoundOne = false; while (TXTentryParse (name, filenam, &X, &Y, &Repeat, TXT, true)) { /* first sprite seen? */ if (!Repeat || !FoundOne) { WADRalign4(&rwad); /*align entry on int32_t word */ start = WADRposition(&rwad); if (!FoundOne) { if (type == IWAD) WADRdirAddEntry(&rwad, start, 0L, "S_START"); else WADRdirAddEntry(&rwad, start, 0L, "SS_START"); } FoundOne = true; CMPOloadPic(&size, &rwad, file, DataDir, "SPRITES", name, filenam, PSPRIT, X, Y); } WADRdirAddEntry(&rwad, start, size, name); } if (FoundOne) { WADRalign4(&rwad); start = WADRposition(&rwad); if ((type == IWAD) || George) WADRdirAddEntry(&rwad, start, 0L, "S_END"); else WADRdirAddEntry(&rwad, start, 0L, "SS_END"); } } } /* ** Try to load patches ** even if no new textures (old patches could be redefined) */ /* write new patches in PWAD */ /* read the name of the new textures and insert them */ /* between P_START and P_END for future completion */ if (select & BPATCH) { FoundOne = false; /* ** First look for patches in [PATCHES] */ start = size = 0; if (TXTseekSection(TXT, "PATCHES")) { Phase("CM80", "Making patches"); while (TXTentryParse (name, filenam, &X, &Y, &Repeat, TXT, true)) { if (!Repeat || !FoundOne) { WADRalign4(&rwad); /*align entry on int32_t word */ start = WADRposition(&rwad); if (!FoundOne) { if (type == IWAD) { WADRdirAddEntry(&rwad, start, 0L, "P_START"); WADRdirAddEntry(&rwad, start, 0L, "P1_START"); } else { WADRdirAddEntry(&rwad, start, 0L, "PP_START"); } } FoundOne = true; CMPOloadPic(&size, &rwad, file, DataDir, "PATCHES", name, filenam, PPATCH, X, Y); } WADRdirAddEntry(&rwad, start, size, name); } } /* ** Check if all the needed patches are defined. */ nbPatchs = PNMgetNbOfPatch(); for (p = 0; p < nbPatchs; p++) { if (!PNMisNew(p)) { continue; /*if old patch, forget it */ } PNMgetPatchName(name, p); Normalise(filenam, name); /*search in main IWAD directory */ if (WADRfindEntry(&iwad, name) >= 0) { Output("Reusing DOOM entry %s as patch\n", lump_name(name)); } /*search in current PWAD */ else if (WADRfindEntry(&rwad, name) < 0) { /*PATCH not found in current WAD, load automatically **from the PATCH directory */ WADRalign4(&rwad); /*align entry on int32_t word */ start = WADRposition(&rwad); Picture = CMPOloadPic(&size, &rwad, file, DataDir, "PATCHES", name, filenam, PPATCH, INVALIDINT, INVALIDINT); if (Picture != PICNONE) { if (!FoundOne) { Phase("CM82", "Making patches"); if (type == IWAD) { WADRdirAddEntry(&rwad, start, 0L, "P_START"); WADRdirAddEntry(&rwad, start, 0L, "P1_START"); } else WADRdirAddEntry(&rwad, start, 0L, "PP_START"); } FoundOne = true; WADRdirAddEntry(&rwad, start, size, name); } } } if (FoundOne) { WADRalign4(&rwad); /*align entry on int32_t word */ start = WADRposition(&rwad); if (type == IWAD) { WADRdirAddEntry(&rwad, start, 0L, "P1_END"); WADRdirAddEntry(&rwad, start, 0L, "P2_START"); WADRdirAddEntry(&rwad, start, 0L, "P2_END"); WADRdirAddEntry(&rwad, start, 0L, "P3_START"); WADRdirAddEntry(&rwad, start, 0L, "P3_END"); WADRdirAddEntry(&rwad, start, 0L, "P_END"); } else WADRdirAddEntry(&rwad, start, 0L, "PP_END"); } } /* ** clear off Pnames */ if (select & (BTEXTUR | BPATCH)) { PNMfree(); } /* FF_START ** Flats ** F_END */ if (select & BFLAT) { if (TXTseekSection(TXT, "FLATS")) { Phase("CM85", "Making flats"); FoundOne = false; while (TXTentryParse (name, filenam, &X, &Y, &Repeat, TXT, false)) { if (!Repeat || !FoundOne) { /*align entry on int32_t word */ WADRalign4(&rwad); start = WADRposition(&rwad); if (!FoundOne) { if (type == IWAD) { WADRdirAddEntry(&rwad, start, 0L, "F_START"); WADRdirAddEntry(&rwad, start, 0L, "F1_START"); } else WADRdirAddEntry(&rwad, start, 0L, "FF_START"); } FoundOne = true; CMPOloadPic(&size, &rwad, file, DataDir, "FLATS", name, filenam, PFLAT, INVALIDINT, INVALIDINT); } WADRdirAddEntry(&rwad, start, size, name); } if (FoundOne) { start = WADRposition(&rwad); if (type == IWAD) { WADRdirAddEntry(&rwad, start, 0L, "F1_END"); WADRdirAddEntry(&rwad, start, 0L, "F2_START"); WADRdirAddEntry(&rwad, start, 0L, "F2_END"); WADRdirAddEntry(&rwad, start, 0L, "F3_START"); WADRdirAddEntry(&rwad, start, 0L, "F3_END"); WADRdirAddEntry(&rwad, start, 0L, "F_END"); } else WADRdirAddEntry(&rwad, start, 0L, "F_END"); } } } /* ** exit from graphic */ if (select & (BGRAPHIC | BSPRITE | BPATCH | BFLAT)) COLfree(); /* ** iwad not needed anymore */ WADRclose(&iwad); /* ** the end */ TXTcloseR(TXT); WADRwriteDir(&rwad, 1); /* write the WAD directory */ ProgErrorCancel(); WADRclose(&rwad); /*add some junk at end of wad file, for DEU 5.21 */ if (type == PWAD) AddSomeJunk(PWADname); } extern int16_t HowMuchJunk; static char Junk[] = "This junk is here for DEU 5.21. I am repeating myself anyway... "; static void AddSomeJunk(const char *file) { FILE *out; int16_t n; out = fopen(file, FOPEN_AB); /*open R/W at the end */ if (out == NULL) ProgError("CM97", "%s: %s", fname(file), strerror(errno)); for (n = 0; n < HowMuchJunk; n++) if (fwrite(Junk, 1, 64, out) < 64) Warning("CM98", "Can't insert my junk!"); fclose(out); } deutex-5.2.3/src/deutex.c000066400000000000000000001136421475516661100152730ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ /* DeuTex is a sequel to the DOOM editor DEU 5.21 (hence the name) Originaly it used lots of code from DEU, and even some DEU modules Now it doesn't use anymore DEU modules, but large parts of this code are still inspired by the great DEU code, though the style is quite different. I wrote this code with the intent to limit bug generation and propagation to a minimum. That means modules, no global variables, a lot of mess in the parameters. I deliberately coded very basic functions, not optimised at all. Optimisation will come later. That means: No hash table, No fast color quantisation... If you find that code verbose, you are damn right. But I'm quite proud of my coding time/testing time ratio of 5, considering that code already works on so many PWADs. I would strongly advise *not* to reuse this code, unless you like my bugs and take the engagement to treat them well. I'm so fond of them now... -- Olivier Montanuy */ #include "deutex.h" #include #include #include #include "tools.h" #include "mkwad.h" #include "merge.h" #include "extract.h" #include "wadio.h" #include "picture.h" #include "usedidx.h" /* ** global variables for commands */ char file[128]; /* general use file name */ static WADTYPE Type; /*IWAD type */ static NTRYB Select; static const char *DataDir = NULL; static const char *DoomDir = NULL; static char MainWAD[128]; /* name of the main wad file */ static char WadInf[128]; /* name of the wadinfo file */ static bool WadInfOk; int16_t HowMuchJunk; /* junk to add */ static IMGTYPE Picture; /* save as PPM, BMP or GIF ? */ static SNDTYPE Sound = SNDWAV; /* save as WAV? Yes. */ static bool WSafe; static bool George; char trnR, trnG, trnB; picture_format_t picture_format = PF_NORMAL; texture_format_t input_texture_format = TF_NORMAL; texture_format_t output_texture_format = TF_NORMAL; texture_lump_t texture_lump = TL_NORMAL; rate_policy_t rate_policy = RP_WARN; clobber_t clobber = CLOBBER_NO; bool use_png_offsets = false; const char *debug_ident = NULL; const char *palette_lump = "PLAYPAL"; static char anon[1] = { '\0' }; typedef void (*comfun_t) (int argc, const char *argv[]); static void opt_widths(void); static int is_prefix(const char *s1, const char *s2); static void call_opt(comfun_t func, ...); /* ** commands */ void COMhelp(int argc, const char *argv[]); void COMvers(int argc, const char *argv[]); void COMformat(int argc, const char *argv[]); void COMipf(int argc, const char *argv[]); void COMtf(int argc, const char *argv[]); void COMitl(int argc, const char *argv[]); void COMverbose(int argc, const char *argv[]) { PrintVerbosity(argv[0][2] - '0'); Info("AA10", "Verbosity level is %c", argv[0][2]); (void) argc; } void COMdoom(int argc, const char *argv[]) { DoomDir = argv[1]; Info("AA15", "Main directory: %s", DoomDir); (void) argc; } void COMdoom02(int argc, const char *argv[]) { call_opt(COMdoom, anon, argv[1], NULL); call_opt(COMipf, anon, "alpha", NULL); call_opt(COMtf, "tf", "none", NULL); call_opt(COMitl, anon, "none", NULL); (void) argc; } void COMdoom04(int argc, const char *argv[]) { call_opt(COMdoom, anon, argv[1], NULL); call_opt(COMipf, anon, "alpha", NULL); call_opt(COMtf, "tf", "nameless", NULL); call_opt(COMitl, anon, "textures", NULL); (void) argc; } void COMdoom05(int argc, const char *argv[]) { call_opt(COMdoom, anon, argv[1], NULL); call_opt(COMipf, anon, "alpha", NULL); call_opt(COMitl, anon, "textures", NULL); (void) argc; } void COMdoompr(int argc, const char *argv[]) { call_opt(COMdoom, anon, argv[1], NULL); call_opt(COMipf, anon, "pr", NULL); (void) argc; } void COMstrife(int argc, const char *argv[]) { call_opt(COMdoom, anon, argv[1], NULL); call_opt(COMtf, "tf", "strife11", NULL); (void) argc; } void COMmain(int argc, const char *argv[]) { DoomDir = NULL; strncpy(MainWAD, argv[1], 128); Info("AA16", "Main IWAD file: %s", MainWAD); (void) argc; } void COMwadir(int argc, const char *argv[]) { XTRlistDir(MainWAD, ((argc < 2) ? NULL : argv[1]), Select); } void COMadd(int argc, const char *argv[]) { ADDallSpriteFloor(argv[2], MainWAD, argv[1], Select); (void) argc; } void COMapp(int argc, const char *argv[]) { ADDappendSpriteFloor(MainWAD, argv[1], Select); (void) argc; } void COMapps(int argc, const char *argv[]) { Select = (BALL) & (~BFLAT); /*no flats */ ADDappendSpriteFloor(MainWAD, argv[1], Select); (void) argc; } void COMappf(int argc, const char *argv[]) { Select = (BALL) & (~BSPRITE); /*no sprites */ ADDappendSpriteFloor(MainWAD, argv[1], Select); (void) argc; } void COMjoin(int argc, const char *argv[]) { ADDjoinWads(MainWAD, argv[1], argv[2], Select); (void) argc; } void COMmerge(int argc, const char *argv[]) { Select = BALL; PSTmergeWAD(MainWAD, argv[1], Select); (void) argc; } void COMrestor(int argc, const char *argv[]) { HDRrestoreWAD((argc >= 2) ? argv[1] : MainWAD); } /* ** Selections */ void COMsprit(int argc, const char *argv[]) { Select |= BSPRITE; Info("AA62", "Select sprites"); (void) argc; (void) argv; } void COMflat(int argc, const char *argv[]) { Select |= BFLAT; Info("AA63", "Select flats"); (void) argc; (void) argv; } void COMlevel(int argc, const char *argv[]) { Select |= BLEVEL; Info("AA64", "Select levels"); (void) argc; (void) argv; } void COMlump(int argc, const char *argv[]) { Select |= BLUMP; Info("AA65", "Select lumps"); (void) argc; (void) argv; } void COMtextur(int argc, const char *argv[]) { Select |= BTEXTUR; Info("AA66", "Select textures"); (void) argc; (void) argv; } void COMpatch(int argc, const char *argv[]) { Select |= BPATCH; Info("AA67", "Select patches"); (void) argc; (void) argv; } void COMsound(int argc, const char *argv[]) { Select |= BSOUND; Info("AA68", "Select sounds"); (void) argc; (void) argv; } void COMmusic(int argc, const char *argv[]) { Select |= BMUSIC; Info("AA69", "Select musics"); (void) argc; (void) argv; } void COMgraphic(int argc, const char *argv[]) { Select |= BGRAPHIC; Info("AA70", "Select graphics"); (void) argc; (void) argv; } void COMsneas(int argc, const char *argv[]) { Select |= BSNEA; Info("AA71", "Select sneas"); (void) argc; (void) argv; } void COMsneaps(int argc, const char *argv[]) { Select |= BSNEAP; Info("AA72", "Select sneaps"); (void) argc; (void) argv; } void COMsneats(int argc, const char *argv[]) { Select |= BSNEAT; Info("AA73", "Select sneats"); (void) argc; (void) argv; } void COMscripts(int argc, const char *argv[]) { Select |= BSCRIPT; Info("AA74", "Select scripts"); (void) argc; (void) argv; } void COMgeorge(int argc, const char *argv[]) { George = true; Info("AA32", "Using S_END for sprites"); (void) argc; (void) argv; } void PicDebug(char *file, const char *DataDir, const char *name); void COMdebug(int argc, const char *argv[]) { #include "color.h" static struct WADINFO iwad; int16_t pnm; char *Colors; int32_t Pnamsz = 0; iwad.ok = 0; WADRopenR(&iwad, MainWAD); pnm = WADRfindEntry(&iwad, palette_lump); if (pnm < 0) ProgError("GD04", "Can't find %s in Main WAD", lump_name(palette_lump)); Colors = WADRreadEntry(&iwad, pnm, &Pnamsz); COLinit(trnR, trnG, trnB, Colors, (int16_t) Pnamsz, iwad.filename, palette_lump); free(Colors); WADRclose(&iwad); PicDebug(file, DataDir, (argc < 2) ? "test" : argv[1]); COLfree(); (void) argc; (void) argv; } void COMdi(int argc, const char *argv[]) { Info("ID01", "Debugging identification of entry %s", lump_name(argv[1])); debug_ident = argv[1]; (void) argc; } void COMdeu(int argc, const char *argv[]) { HowMuchJunk = MAXJUNK64; Info("AA33", "Will add 64 kB of junk at end of wad for DEU 5.21 compatibility"); (void) argc; (void) argv; } void COMdir(int argc, const char *argv[]) { DataDir = argv[1]; Info("AA22", "Files will be saved in directory %s", DataDir); (void) argc; } void COMrate(int argc, const char *argv[]) { if (argc >= 2 && !strcmp(argv[1], "reject")) rate_policy = RP_REJECT; else if (argc >= 2 && !strcmp(argv[1], "force")) rate_policy = RP_FORCE; else if (argc >= 2 && !strcmp(argv[1], "warn")) rate_policy = RP_WARN; else if (argc >= 2 && !strcmp(argv[1], "accept")) rate_policy = RP_ACCEPT; else ProgError("AA41", "Usage is \"-rate {reject|force|warn|accept}\""); Info("AA42", "Sample rate policy is \"%s\"", argv[1]); (void) argc; } void COMstroy(int argc, const char *argv[]) { WSafe = false; clobber = CLOBBER_YES; Info("AA28", "Overwrite existing files"); (void) argc; (void) argv; } void COMgif(int argc, const char *argv[]) { Picture = PICGIF; Info("AA50", "Saving pictures as GIF (.gif)"); (void) argc; (void) argv; } #ifdef HAVE_LIBPNG void COMpng(int argc, const char *argv[]) { Picture = PICPNG; Info("AA99", "Saving pictures as PNG (.png)"); (void) argc; (void) argv; } #endif void COMbmp(int argc, const char *argv[]) { Picture = PICBMP; Info("AA51", "Saving pictures as BMP (.bmp)"); (void) argc; (void) argv; } void COMppm(int argc, const char *argv[]) { Picture = PICPPM; Info("AA52", "Saving pictures as rawbits PPM (P6, .ppm)"); (void) argc; (void) argv; } void COMrgb(int argc, const char *argv[]) { trnR = (char) (atoi(argv[1]) & 0xFF); trnG = (char) (atoi(argv[2]) & 0xFF); trnB = (char) (atoi(argv[3]) & 0xFF); Info("AA21", "Transparent colour is R=%d G=%d B=%d", ((int) trnR & 0xFF), ((int) trnG & 0xFF), ((int) trnB & 0xFF)); (void) argc; } void COMle(int argc, const char *argv[]) { set_input_wad_endianness(0); set_output_wad_endianness(0); (void) argc; (void) argv; } #ifdef HAVE_LIBPNG void COMpngoffset(int argc, const char *argv[]) { use_png_offsets = true; (void) argc; (void) argv; } #endif void COMbe(int argc, const char *argv[]) { set_input_wad_endianness(1); set_output_wad_endianness(1); (void) argc; (void) argv; } void COMile(int argc, const char *argv[]) { set_input_wad_endianness(0); (void) argc; (void) argv; } void COMibe(int argc, const char *argv[]) { set_input_wad_endianness(1); (void) argc; (void) argv; } void COMole(int argc, const char *argv[]) { set_output_wad_endianness(0); (void) argc; (void) argv; } void COMobe(int argc, const char *argv[]) { set_output_wad_endianness(1); (void) argc; (void) argv; } void COMipf(int argc, const char *argv[]) { if (argc >= 2 && !strcmp(argv[1], "alpha")) picture_format = PF_ALPHA; else if (argc >= 2 && !strcmp(argv[1], "normal")) picture_format = PF_NORMAL; else if (argc >= 2 && !strcmp(argv[1], "pr")) picture_format = PF_PR; else ProgError("PI01", "Usage is \"-ipf {alpha|normal|pr}\""); Info("PI02", "Input picture format is \"%s\"", argv[1]); } void COMtf(int argc, const char *argv[]) { int set_in = 0; int set_out = 0; if (!strcmp(argv[0], "itf")) set_in = 1; else if (!strcmp(argv[0], "otf")) set_out = 1; else if (!strcmp(argv[0], "tf")) { set_in = 1; set_out = 1; } else Bug("AA90", "Invalid argv[0] \"%.32s\"", argv[0]); if (argc >= 2 && !strcmp(argv[1], "nameless")) { if (set_in) input_texture_format = TF_NAMELESS; if (set_out) output_texture_format = TF_NAMELESS; } else if (argc >= 2 && !strcmp(argv[1], "none")) { if (set_in) input_texture_format = TF_NONE; if (set_out) output_texture_format = TF_NONE; } else if (argc >= 2 && !strcmp(argv[1], "normal")) { if (set_in) input_texture_format = TF_NORMAL; if (set_out) output_texture_format = TF_NORMAL; } else if (argc >= 2 && !strcmp(argv[1], "strife11")) { if (set_in) input_texture_format = TF_STRIFE11; if (set_out) output_texture_format = TF_STRIFE11; } else ProgError("TX04", "Usage is \"-%.32s {nameless|none|normal|strife11}\"", argv[0]); if (set_in) Info("TX05", "Input texture format is \"%s\"", argv[1]); if (set_out) Info("TX06", "Output texture format is \"%s\"", argv[1]); } void COMitl(int argc, const char *argv[]) { if (argc >= 2 && !strcmp(argv[1], "none")) texture_lump = TL_NONE; else if (argc >= 2 && !strcmp(argv[1], "textures")) texture_lump = TL_TEXTURES; else if (argc >= 2 && !strcmp(argv[1], "normal")) texture_lump = TL_NORMAL; else ProgError("TX01", "Usage is \"-itl {none|textures|normal}\""); Info("TX02", "Input texture lump is \"%s\"", argv[1]); } /* ** Build an IWAD ** */ void COMiwad(int argc, const char *argv[]) { Type = IWAD; Info("AA31", "Building an iwad"); (void) argc; (void) argv; } /* ** Main Commands ** */ void COMmake(int argc, const char *argv[]) { const char *wadinf, *wadout; if (!WadInfOk) { MakeFileName(WadInf, DataDir, "", "", "WADINFO", "TXT"); } if (argc <= 2) { wadinf = WadInf; wadout = argv[1]; } else { wadinf = argv[1]; wadout = argv[2]; } CMPOmakePWAD(MainWAD, Type, wadout, DataDir, wadinf, Select, trnR, trnG, trnB, George); (void) argc; } void COMxtra(int argc, const char *argv[]) { const char *wadinf, *wadin; if (!WadInfOk) { MakeFileName(WadInf, DataDir, "", "", "WADINFO", "TXT"); } if (argc <= 1) { wadin = MainWAD; } else { wadin = argv[1]; } if (argc <= 2) { wadinf = WadInf; } else { wadinf = argv[2]; } XTRextractWAD(MainWAD, DataDir, wadin, wadinf, Picture, Sound, Select, trnR, trnG, trnB, WSafe, NULL); } void COMget(int argc, const char *argv[]) { XTRgetEntry(MainWAD, DataDir, ((argc < 3) ? MainWAD : argv[2]), argv[1], Picture, Sound, trnR, trnG, trnB); } void COMpackNorm(int argc, const char *argv[]) { XTRcompakWAD(DataDir, (argc > 1) ? argv[1] : MainWAD, (argc > 2) ? argv[2] : NULL, false); } void COMpackGfx(int argc, const char *argv[]) { XTRcompakWAD(DataDir, (argc > 1) ? argv[1] : MainWAD, (argc > 2) ? argv[2] : NULL, true); } void COMvoid(int argc, const char *argv[]) { XTRvoidSpacesInWAD(argv[1]); (void) argc; } void COMusedtex(int argc, const char *argv[]) { XTRtextureUsed((argc > 1) ? argv[1] : MainWAD); } void COMusedidx(int argc, const char *argv[]) { const char *wadinf, *wadin; cusage_t *cusage = NULL; if (!WadInfOk) { /* Not used anyway */ MakeFileName(WadInf, DataDir, "", "", "WADINFO", "TXT"); } if (argc <= 1) { wadin = MainWAD; } else { wadin = argv[1]; } if (argc <= 2) { wadinf = WadInf; } else { wadinf = argv[2]; } cusage = Malloc(sizeof *cusage); { int n; for (n = 0; n < NCOLOURS; n++) { cusage->uses[n] = 0; cusage->nlumps[n] = 0; cusage->where_first[n][0] = '\0'; } } XTRextractWAD(MainWAD, DataDir, wadin, wadinf, Picture, Sound, Select, trnR, trnG, trnB, WSafe, cusage); free(cusage); } void COMcheck(int argc, const char *argv[]) { XTRstructureTest(MainWAD, argv[1]); (void) argc; } enum { OC_MASK = 0xc0, OC_SEC = 0x00, OC_OPT = 0x40, OC_MOD = 0x80, OC_END = 0xc0, }; #define PASS(comtype) ((comtype) & 0x1f) typedef unsigned char COMTYPE; #define SEC OC_SEC /* Section (used by --help/-man) */ #define CM0 OC_MOD + 0 /* Modal (no banner, no log, no iwad) */ /* -------- Banner is printed -------- */ #define CM1 OC_MOD + 1 /* Modal (banner, no log, no iwad) */ #define OP1 OC_OPT + 2 /* Opt (-log) */ /* -------- Log file is opened -------- */ #define OP2 OC_OPT + 3 /* Opt (banner, log) */ #define CM3 OC_MOD + 0x04 /* Modal (banner, log, no iwad) */ #define CM4 OC_MOD + 0x24 /* Modal (banner, log, iwad required) */ #define END OC_END typedef struct { COMTYPE type; char argc; char *com; comfun_t exec; char *use; char *help; uint16_t width1; uint16_t width2; } comdef_t; /* FIXME should be at the top of the file but we need comdef_t */ static const comdef_t *parse_argv(int *argc, const char ***argv, int pass); static comdef_t Com[] = { {SEC, 0, NULL, NULL, NULL, "Modal options not requiring an iwad"}, {CM1, 0, "?", COMhelp, NULL, "same as \1--help\3"}, {CM1, 0, "h", COMhelp, NULL, "same as \1--help\3"}, {CM1, 0, "help", COMhelp, NULL, "same as \1--help\3"}, {CM1, 0, "-help", COMhelp, NULL, "print list of options"}, {CM0, 0, "syntax", COMformat, NULL, "print the syntax of wad creation directives"}, {CM3, 1, "unused", COMvoid, "", "find unused spaces in a wad"}, {CM0, 0, "version", COMvers, NULL, "same as \1--version\3"}, {CM0, 0, "-version", COMvers, NULL, "print version number and exit successfully"}, {SEC, 0, NULL, NULL, NULL, "Modal options requiring an iwad"}, {CM4, 2, "add", COMadd, " ", "copy sp & fl of iwad and \2in.wad\3 to \2out.wad\3"}, {CM4, 1, "af", COMappf, "", "append all floors/ceilings to the wad"}, {CM4, 1, "append", COMapp, "", "add sprites & flats of iwad to \2io.wad\3"}, {CM4, 1, "as", COMapps, "", "append all sprites to the wad"}, {CM4, 1, "build", COMmake, "[] ", "make a pwad"}, {CM4, 1, "check", COMcheck, "", "check the textures"}, {CM4, 1, "create", COMmake, "[] ", "same as \1-build\3"}, {CM4, 0, "debug", COMdebug, "[]", "debug colour conversion"}, {CM4, 0, "extract", COMxtra, "[ []]", "same as \1-xtract\3"}, {CM4, 1, "get", COMget, " []", "get a wad entry from main wad or \2in.wad\3"}, {CM4, 2, "join", COMjoin, " ", "append sprites & flats of Doom to a pwad"}, {CM4, 1, "make", COMmake, "[] ", "same as \1-build\3"}, {CM4, 1, "merge", COMmerge, "", "merge doom.wad and a pwad"}, {CM4, 1, "pkgfx", COMpackGfx, "[ []]", "detect identical graphics"}, {CM4, 1, "pknormal", COMpackNorm, "[ []]", "detect identical normal"}, {CM4, 0, "restore", COMrestor, NULL, "restore doom.wad and the pwad"}, {CM4, 1, "test", COMcheck, "", "same as \1-check\3"}, {CM4, 0, "usedidx", COMusedidx, "[]", "colour index usage statistics"}, {CM4, 0, "usedtex", COMusedtex, "[]", "list textures used in all levels"}, {CM4, 0, "wadir", COMwadir, "[]", "list and identify entries in a wad"}, {CM4, 0, "xtract", COMxtra, "[ []]", "extract some/all entries from a wad"}, {SEC, 0, NULL, NULL, NULL, "General options"}, {OP2, 0, "overwrite", COMstroy, NULL, "overwrite all"}, {OP2, 1, "dir", COMdir, "", "extraction directory (default \1.\3)"}, {SEC, 0, NULL, NULL, NULL, "Iwad"}, {OP2, 1, "doom", COMdoom, "", "path to Doom iwad"}, {OP2, 1, "doom2", COMdoom, "", "path to Doom II iwad"}, {OP2, 1, "doom02", COMdoom02, "", "path to Doom alpha 0.2 iwad"}, {OP2, 1, "doom04", COMdoom04, "", "path to Doom alpha 0.4 iwad"}, {OP2, 1, "doom05", COMdoom05, "", "path to Doom alpha 0.5 iwad"}, {OP2, 1, "doompr", COMdoompr, "", "path to Doom PR pre-beta iwad"}, {OP2, 1, "heretic", COMdoom, "", "path to Heretic iwad"}, {OP2, 1, "hexen", COMdoom, "", "path to Hexen iwad"}, {OP2, 1, "strife", COMstrife, "", "path to Strife iwad"}, {OP2, 1, "strife10", COMdoom, "", "path to Strife 1.0 iwad"}, {SEC, 0, NULL, NULL, NULL, "Wad options"}, {OP2, 0, "be", COMbe, NULL, "assume all wads are big endian (default LE)"}, {OP2, 0, "deu", COMdeu, NULL, "add 64k of junk for DEU 5.21 compatibility"}, {OP2, 0, "george", COMgeorge, NULL, "same as \1-s_end\3"}, {OP2, 0, "ibe", COMibe, NULL, "input wads are big endian (default LE)"}, {OP2, 0, "ile", COMile, NULL, "input wads are little endian (default)"}, {OP2, 1, "ipf", COMipf, "", "picture format (\1alpha\3, *\1normal\3, \1pr\3)"}, {OP2, 1, "itf", COMtf, "", "input texture format (\1nameless\3, \1none\3, *\1normal\3, \1strife11\3)"}, {OP2, 1, "itl", COMitl, "", "texture lump (\1none\3, *\1normal\3, \1textures\3)"}, {OP2, 0, "iwad", COMiwad, NULL, "compose iwad, not pwad"}, {OP2, 0, "le", COMle, NULL, "assume all wads are little endian (default)"}, {OP2, 0, "obe", COMobe, NULL, "create big endian wads (default LE)"}, {OP2, 0, "ole", COMole, NULL, "create little endian wads (default)"}, {OP2, 1, "otf", COMtf, "", "output texture format (\1nameless\3, \1none\3, *\1normal\3, \1strife11\3)"}, #ifdef HAVE_LIBPNG {OP2, 0, "pngoffsets", COMpngoffset, NULL, "override offsets in WADINFO with offsets contained in PNG metadata"}, #endif /*by request from George Hamlin */ {OP2, 0, "s_end", COMgeorge, NULL, "use \1S_END\3 for sprites, not \1SS_END\3"}, {OP2, 1, "tf", COMtf, "", "texture format (\1nameless\3, \1none\3, *\1normal\3, \1strife11\3)"}, {SEC, 0, NULL, NULL, NULL, "Lump selection"}, {OP2, 0, "flats", COMflat, NULL, "select flats"}, {OP2, 0, "graphics", COMgraphic, NULL, "select graphics"}, {OP2, 0, "levels", COMlevel, NULL, "select levels"}, {OP2, 0, "lumps", COMlump, NULL, "select lumps"}, {OP2, 0, "musics", COMmusic, NULL, "select musics"}, {OP2, 0, "patches", COMpatch, NULL, "select patches"}, {OP2, 0, "scripts", COMscripts, NULL, "select Strife scripts"}, {OP2, 0, "sneas", COMsneas, NULL, "select sneas (sneaps and sneats)"}, {OP2, 0, "sneaps", COMsneaps, NULL, "select sneaps"}, {OP2, 0, "sneats", COMsneats, NULL, "select sneats"}, {OP2, 0, "sounds", COMsound, NULL, "select sounds"}, {OP2, 0, "sprites", COMsprit, NULL, "select sprites"}, {OP2, 0, "textures", COMtextur, NULL, "select textures"}, {SEC, 0, NULL, NULL, NULL, "Graphics"}, {OP2, 0, "bmp", COMbmp, NULL, "save pictures as BMP (\1.bmp\3)"}, {OP2, 0, "gif", COMgif, NULL, "save pictures as GIF (\1.gif\3)"}, #ifdef HAVE_LIBPNG {OP2, 0, "png", COMpng, NULL, "save pictures as PNG (\1.png\3)"}, #endif {OP2, 0, "ppm", COMppm, NULL, "save pictures as rawbits PPM (P6, \1.ppm\3)"}, {OP2, 3, "rgb", COMrgb, " ", "specify the transparent colour (default 0 47 47)"}, {SEC, 0, NULL, NULL, NULL, "Sound"}, {OP2, 1, "rate", COMrate, "", "policy for != 11025 Hz (\1reject\3, \1force\3, *\1warn\3, \1accept\3)"}, {SEC, 0, NULL, NULL, NULL, "Reporting"}, {OP2, 1, "di", COMdi, "", "debug identification of entry"}, {OP2, 0, "v0", COMverbose, NULL, "set verbosity level to 0"}, {OP2, 0, "v1", COMverbose, NULL, "set verbosity level to 1"}, {OP2, 0, "v2", COMverbose, NULL, "set verbosity level to 2 (default)"}, {OP2, 0, "v3", COMverbose, NULL, "set verbosity level to 3"}, {OP2, 0, "v4", COMverbose, NULL, "set verbosity level to 4"}, {OP2, 0, "v5", COMverbose, NULL, "set verbosity level to 5"}, {END, 0, "", COMhelp, NULL, ""} }; int main(int argc, char *argv_non_const[]) { /* Create argv[] identical to argv_[] but of type (const char **). We do this to avoid the warnings about initialising a (const char **) with a (char **). */ const char **argv = malloc(argc * sizeof *argv); if (argv == NULL) ProgError("MM69", "Out of memory (%d)", argc); { size_t n; for (n = 0; n < argc; n++) argv[n] = argv_non_const[n]; } /* Do a first pass through argv to process the options where you don't want the banners (-version, -man) */ { int c = argc - 1; const char **v = argv + 1; const comdef_t *d = parse_argv(&c, &v, 0); if (d != NULL) { d->exec(c, v); exit(0); } } /* ** default parameters */ WadInfOk = false; George = false; #ifdef HAVE_LIBPNG Picture = PICPNG; #else Picture = PICPPM; #endif Sound = SNDWAV; trnR = 0; trnG = 47; trnB = 47; WSafe = true; HowMuchJunk = 0; Select = 0; Type = PWAD; ProgErrorCancel(); /*no error handler defined */ /*setbuf(stdout,(char *)NULL); */ setvbuf(stdout, NULL, _IOLBF, BUFSIZ); PrintVerbosity(2); /* Do a second pass through argv to catch options like --help that shouldn't cause the creation of a log file. */ { int c = argc - 1; const char **v = argv + 1; const comdef_t *d = parse_argv(&c, &v, 1); if (d != NULL) { d->exec(c, v); exit(0); } } /* At this point, we have either (1) no modal option or (2) a modal option which requires writing to log. Make a third pass through argv to find out. */ { int c = argc - 1; const char **v = argv + 1; const comdef_t *d = parse_argv(&c, &v, 4); if (d == NULL) ProgError("AA96", "No command given"); /* We now know we need to create a log. Do a pass through argv to catch -log. */ { int c = argc - 1; const char **v = argv + 1; parse_argv(&c, &v, 2); } /* Default iwad directory */ DataDir = "."; DoomDir = getenv("DOOMWADDIR"); if (DoomDir == NULL) DoomDir = "."; else Phase("AA17", "Doom directory is %.128s", DoomDir); /* Honour the non-modal options */ { int c = argc - 1; const char **v = argv + 1; parse_argv(&c, &v, 3); } /* If the modal option requires an iwad, find it. */ if (d->type & 0x20) { static const char *wads[] = { "doom", /* Doom, Ultimate Doom, Doom alpha */ "doom2", /* Doom II */ "plutonia", /* Final Doom */ "tnt", /* Final Doom */ "freedoom1", /* Freedoom: Phase 1 */ "freedoom2", /* Freedoom: Phase 2 */ "freedm", /* FreeDM */ "blasphem", /* Blasphemer */ "heretic", /* Heretic */ "hexen", /* Hexen */ "strife1", /* Strife */ "doompres", /* Doom Press Release pre-beta */ "doom1", /* Doom shareware */ "heretic1", /* Heretic demo */ "strife0", /* Strife demo */ "hacx", /* Hacx IWAD */ "chex3", /* Chex Quest 3 IWAD */ NULL }; int gotit = 0; const char **w; for (w = wads; *w != NULL; w++) { if (MakeFileName(MainWAD, ".", "", "", *w, "wad")) { gotit = 1; break; } } if (!gotit) { for (w = wads; *w != NULL; w++) { if (MakeFileName(MainWAD, DoomDir, "", "", *w, "wad")) { gotit = 1; break; } } } #ifndef _WIN32 if (!gotit) { for (w = wads; *w != NULL; w++) { if (MakeFileName (MainWAD, "/usr/share/games/doom", "", "", *w, "wad")) { gotit = 1; break; } } } #endif if (!gotit) ProgError("AA18", "Can't find any of doom.wad, doom2.wad, doompres.wad," " heretic.wad, hexen.wad, strife1.wad"); } if (!(Select & BALL)) Select = BALL; d->exec(c, v); } Info("AA99", "Normal exit"); return 0; } /* * parse_argv - parse the command line * * Only the non-modal options whose group matches the * argument are executed. * * Modal options are never executed. If their group matches , * the function returns a pointer to their definition in Com[]. * Otherwise, the function returns a null pointer. * * Upon return, *argc and *argv point one past the last argument * processed. */ static const comdef_t *parse_argv(int *argc, const char ***argv, int pass) { /* Parse the command line from left to right. Try to match the each argument against the longest possible option defined. */ while (*argc > 0) { const comdef_t *d = Com + sizeof Com / sizeof *Com - 1; const char *arg = **argv; if (*arg != '-') ProgError("AA92", "Argument \"%s\": not an option", arg); arg++; { const comdef_t *w; for (w = Com; w->type != END; w++) { int r; if ((w->type & OC_MASK) == OC_SEC) continue; r = is_prefix(arg, w->com); if (r != 0) { if (r > 1) { /* Exact match. */ d = w; goto got_it; } if (d->type != END) /* Ambiguous partial match. */ ProgError("AA93", "\"-%s\" is ambiguous (-%s, -%s)", arg, d->com, w->com); /* Unambiguous partial match. */ d = w; } } } got_it: if (*argc - 1 < d->argc) ProgError("AA94", "Usage: -%s%s%s", d->com, d->use ? " " : "", d->use ? d->use : ""); { int class = (d->type & 0xc0); int group = (d->type & 0x1f); if (class == OC_SEC) { ; /* Can't happen */ } else if (class == OC_OPT) { if (group == pass) d->exec(*argc, *argv); } else if (class == OC_MOD) { if (group == pass) return d; else /* Parsing ALWAYS stops at the first modal option. */ return NULL; } else if (class == OC_END) { ProgError("AA95", "Invalid option \"%s\"", **argv); } else { Bug("AA97", "Com #%d: invalid class %02Xh", (int) (d - Com), class); } } *argv += d->argc + 1; *argc -= d->argc + 1; } /* Found no modal option for this pass number */ return NULL; } /* ** Print Help */ #define TTYCOL 79 #define OPTINDENT 2 #define COLSPACING 2 void COMhelp(int argc, const char *argv[]) { const comdef_t *d; size_t width1 = 22; size_t width2 = 22; int section = 0; printf("Help for %s:\n", PACKAGE_NAME); opt_widths(); for (d = Com; d->type != END; d++) { /* Do a first pass on all the options for this section. Find out how wide the left and right columns need to be. */ if (d->type == SEC) { if (section++) putchar('\n'); printf("%s:\n", d->help); width1 = d->width1 + OPTINDENT; width2 = d->width2; if (width1 + 1 + width2 > TTYCOL) width1 = TTYCOL - width2 - COLSPACING; } /* Now that we know how wide the left column needs to be, print all the options in this section. */ else { char buf[200]; size_t l; size_t desclen; const char *desc; sprintf(buf, "%*s-%s", OPTINDENT, "", d->com); if (d->use) { strcat(buf, " "); strcat(buf, d->use); } l = strlen(buf); desc = d->help; { const char *p; for (desclen = 0, p = desc; *p != '\0'; p++) if (*p < '\1' || *p > '\3') desclen++; } if (l > width1 || l + COLSPACING + desclen > TTYCOL) printf("%s\n%*s", buf, (int) width1 + COLSPACING, ""); else printf("%-*s%*s", (int) width1, buf, COLSPACING, ""); for (; *desc != '\0'; desc++) { if (*desc == '\1'); else if (*desc == '\2'); else if (*desc == '\3'); else putchar(*desc); } putchar('\n'); } } (void) argc; (void) argv; } /* * Print version and exit successfully. * All --version does. */ void COMvers(int argc, const char *argv[]) { (void) argc; (void) argv; printf("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION); exit(0); } static char *Format[] = { "* Format of PWAD creation directives *", "This format is conform to MS-Windows .INI Files.", "Sections are named [LEVELS] [LUMPS] [SOUNDS]", "[MUSICS] [TEXTURE1] [TEXTURE2] [GRAPHICS]", "[SPRITES] [PATCHES] [FLATS] [SNEAPS] [SNEATS]", "Entries have format:", "{name}= {filename} {offsetX} {offsetY}", "A '*' at the end of the definition means that the", "entry will be exactly the same as the previous one.", NULL }; void COMformat(int argc, const char *argv[]) { int i; for (i = 0; Format[i] != NULL; i++) puts(Format[i]); (void) argc; (void) argv; } /* * opt_widths - make a pass through Com and compute widths per section */ static void opt_widths(void) { comdef_t *d; comdef_t *current_section = NULL; size_t width1t = 0; size_t width1r = 0; size_t width2t = 0; size_t width2r = 0; for (d = Com;; d++) { if (d->type == SEC || d->type == END) { /* Seen all the entries of a section. Set - argc = maximum roff width of the first column, - com = maximum roff width of the second column, - exec = maximum text width of the first column, - use = maximum text width of second column. */ if (current_section != NULL) { uint16_t tmp; current_section->argc = (char) width1r; if (current_section->argc != width1r) current_section->argc = CHAR_MAX; /* Can't happen */ tmp = width2r; if (tmp != width2r) /* Can't happen */ tmp = SHRT_MAX; tmp = width1t; if (tmp != width1t) tmp = SHRT_MAX; current_section->width1 = tmp; tmp = width2t; if (tmp != width2t) tmp = SHRT_MAX; current_section->width2 = tmp; } } if (d->type == END) break; if (d->type == SEC) { current_section = d; width1r = 0; width1t = 0; width2r = 0; width2t = 0; continue; } { /* Width of column 1 (synopsis) */ size_t wr = 1 + strlen(d->com); size_t wt = wr; if (d->use != NULL) { const char *u; wr++; wt++; for (u = d->use; *u != '\0'; u++) { if (*u != '<' && *u != '>') wr++; wt++; } } if (wr > width1r) width1r = wr; if (wt > width1t) width1t = wt; } { /* Width of column 2 (description) */ const char *desc; size_t wr = 0; size_t wt = 0; for (desc = d->help; *desc != '\0'; desc++) { if (*desc < '\1' || *desc > '\3') wr++; if (*desc < '\1' || *desc > '\3') wt++; } if (wr > width2r) width2r = wr; if (wt > width2t) width2t = wt; } } } /* * is_prefix - tell whether a string an initial prefix of another * * Return * 0 if s1 is not a prefix of s2 * 1 if s1 is a prefix of s2 * >1 if s1 is equal to s2 */ static int is_prefix(const char *s1, const char *s2) { for (;; s1++, s2++) { if (*s1 == '\0') return (*s2 == '\0') ? 2 : 1; if (*s2 != *s1) return 0; } } /* * call_opt * Equivalent to having the same option on the command line */ static void call_opt(comfun_t func, ...) { int argc; const char *argv[10]; va_list args; va_start(args, func); for (argc = 0; argc < sizeof argv / sizeof *argv; argc++) { argv[argc] = va_arg(args, const char *); if (argv[argc] == NULL) { argc++; break; } } func(argc, argv); va_end(args); } deutex-5.2.3/src/deutex.h000066400000000000000000000140161475516661100152730ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef DEUTEX_H #define DEUTEX_H #include "config.h" #include #include #include #include #include /* fopen() modes */ #define FOPEN_RB "rb" #define FOPEN_RBP "rb+" #define FOPEN_WB "wb" #define FOPEN_RT "r" #define FOPEN_WT "w" #define FOPEN_AT "a" #define FOPEN_AB "a" /* Number of bytes to read from or write to a file */ typedef unsigned long iolen_t; #define MEMORYCACHE (0x8000L) /* steps to increase size of WAD directory*/ #define MAXPWADDIR (128) /*Add 64000 bytes of pure junk, because of a bug in DEU5.21 */ #define MAXJUNK64 (1000) /*special value, means int is not valid*/ #define INVALIDINT (-6666) /*indicate an extern WAD entry*/ #define EXTERNAL (0x80000000L) /* Wad magic numbers */ #define IWADMAGIC 0x5749 /* little-endian 16-bit int for "IW" */ #define PWADMAGIC 0x5750 /* little-endian 16-bit int for "PW" */ #define WADMAGIC 0x4441 /* little-endian 16-bit int for "AD" */ /*type of WAD files. correspond to 1st half of name*/ typedef int16_t WADTYPE; #define IWAD (IWADMAGIC) #define PWAD (PWADMAGIC) /* Number of entries in the game palette */ #define NCOLOURS 256 /* Entry selection Bits*/ typedef int16_t NTRYB; #define BLEVEL (0x0001) #define BLUMP (0x0002) #define BSOUND (0x0004) #define BTEXTUR (0x0008) #define BGRAPHIC (0x0010) #define BSPRITE (0x0020) #define BPATCH (0x0040) #define BFLAT (0x0080) #define BMUSIC (0x0100) #define BSNEAP (0x0200) #define BSNEAT (0x0400) #define BSCRIPT (0x0800) #define BSNEA (BSNEAP|BSNEAT) #define BALL (0x7FFF) typedef int16_t SNDTYPE; #define SNDNONE (0) #define SNDWAV (2) typedef int16_t IMGTYPE; #define PICNONE (0) #define PICBMP (1) #define PICGIF (2) #define PICPPM (3) #define PICWAD (5) #define PICPNG (6) #define PICJPEG (7) /*wad directory*/ struct WADDIR { /*same as in doom */ int32_t start; /*start of entry */ int32_t size; /*size of entry */ char name[8]; /*name of entry */ }; struct WADINFO { int32_t ntry; /*entries in dir */ int32_t dirpos; /*position of dir */ struct WADDIR *dir; /*directory */ int32_t maxdir; /*max nb of entry in dir */ int32_t wposit; /*position for write */ int32_t maxpos; /*farther referenced byte in WAD */ FILE *fd; /* File pointer */ char *filename; /* Pointer on block malloc'd by WADRopen*() and free'd by WADRclose() and containing the name of the file. */ char ok; /*security ok&1=read ok&2=write */ }; typedef int16_t PICTYPE; #define PGRAPH 0x02 #define PWEAPN 0x04 #define PSPRIT 0x06 #define PPATCH 0x08 #define PFLAT 0x0a #define PLUMP 0x0c #define PSNEAP 0x0d #define PSNEAT 0x0e typedef int16_t ENTRY; #define EMASK 0xFF00 #define EVOID 0x0000 #define ELEVEL 0x0100 #define EMAP 0x0200 /*Levels (doom1) and Maps(Doom2) */ #define ELUMP 0x0300 #define ETEXTUR 0x0400 #define EPNAME 0x0500 /*textures */ #define ESOUND 0x0600 #define ESNDPC 0x0601 #define ESNDWAV 0x0602 #define EGRAPHIC 0x0700 #define ESPRITE 0x0800 #define EPATCH 0x0900 #define EPATCH1 0x0901 #define EPATCH2 0x0902 #define EPATCH3 0x0903 #define EFLAT 0x0A00 #define EFLAT1 0x0A01 #define EFLAT2 0x0A02 #define EFLAT3 0x0A03 #define EMUSIC 0x0B00 #define EMUS 0x0B01 #define EMIDI 0x0B02 #define ETXSTART 0x0C00 #define EDATA 0x1000 #define ESNEA 0x2000 #define ESNEAP 0x2001 /* Snea using PLAYPAL */ #define ESNEAT 0x2002 /* Snea using TITLEPAL */ #define ESSCRIPT 0x3000 /* Strife script (SCRIPTnn) */ #define EZZZZ 0x7F00 /*unidentified entries */ void CMPOmakePWAD(const char *doomwad, WADTYPE type, const char *PWADname, const char *DataDir, const char *texin, NTRYB select, char trnR, char trnG, char trnB, bool George); void EXE2list(FILE * out, char *doomexe, int32_t start, int16_t thres); void EXEsubstit(const char *texin, const char *doomexe, int32_t start, int16_t thres); void XTRlistDir(const char *doomwad, const char *wadin, NTRYB select); void XTRvoidSpacesInWAD(const char *wadin); void XTRcompakWAD(const char *DataDir, const char *wadin, const char *texout, bool ShowIdx); void XTRstructureTest(const char *doomwad, const char *wadin); void XTRtextureUsed(const char *wadin); /* * Types defined elsewhere */ struct cusage_s; typedef struct cusage_s cusage_t; /* * Misc globals, set by command line arguments */ typedef enum { PF_NORMAL, PF_ALPHA, PF_PR } picture_format_t; typedef enum { TF_NORMAL, TF_NAMELESS, TF_NONE, TF_STRIFE11 } texture_format_t; typedef enum { TL_NORMAL, TL_TEXTURES, TL_NONE } texture_lump_t; typedef enum { RP_REJECT, RP_FORCE, RP_WARN, RP_ACCEPT } rate_policy_t; typedef enum { CLOBBER_YES, CLOBBER_NO, CLOBBER_ASK } clobber_t; extern picture_format_t picture_format; extern texture_format_t input_texture_format; extern texture_format_t output_texture_format; extern texture_lump_t texture_lump; extern rate_policy_t rate_policy; extern clobber_t clobber; extern bool use_png_offsets; extern const char *debug_ident; extern const char *palette_lump; #endif //DEUTEX_H deutex-5.2.3/src/endianio.c000066400000000000000000000064611475516661100155630ustar00rootroot00000000000000/* Copyright ® André Majorel, contributors to the DeuTex project. Those functions allow to read little-endian and big-endian integers from a file regardless of the endianness of the CPU. SPDX-License-Identifier: LGPL-2.1-or-later */ #include "deutex.h" #include "endianio.h" /* * fread_i16_be * Read a big-endian 16-bit signed integer from file . * Returns 0 on success, != 0 on failure. */ int fread_i16_be(FILE * fd, int16_t * buf) { *buf = (getc(fd) << 8) | getc(fd); return feof(fd) || ferror(fd); } /* * fread_i16_le * Read a little-endian 16-bit signed integer from file . * Returns 0 on success, != 0 on failure. */ int fread_i16_le(FILE * fd, int16_t * buf) { *buf = getc(fd) | (getc(fd) << 8); return feof(fd) || ferror(fd); } /* * fread_i32_be * Read a big-endian 32-bit signed integer from file . * Returns 0 on success, != 0 on failure. */ int fread_i32_be(FILE * fd, int32_t * buf) { *buf = ((int32_t) getc(fd) << 24) | ((int32_t) getc(fd) << 16) | ((uint16_t) getc(fd) << 8) | getc(fd); return feof(fd) || ferror(fd); } /* * fread_i32_le * Read a little-endian 32-bit signed integer from file . * Returns 0 on success, != 0 on failure. */ int fread_i32_le(FILE * fd, int32_t * buf) { *buf = getc(fd) | ((uint16_t) getc(fd) << 8) | ((int32_t) getc(fd) << 16) | ((int32_t) getc(fd) << 24); return feof(fd) || ferror(fd); } /* * fread_u16_le * Read a little-endian 16-bit unsigned integer from file . * Returns 0 on success, != 0 on failure. */ int fread_u16_le(FILE * fd, uint16_t * buf) { *buf = getc(fd) | (getc(fd) << 8); return feof(fd) || ferror(fd); } /* * fwrite_i16_le * Write a little-endian 16-bit signed integer to file . * Returns 0 on success, != 0 on failure. */ int fwrite_i16_le(FILE * fd, int16_t buf) { putc(buf & 0xff, fd); putc((buf >> 8) & 0xff, fd); return feof(fd) || ferror(fd); } /* * fwrite_i16_be * Write a big-endian 16-bit signed integer to file . * Returns 0 on success, != 0 on failure. */ int fwrite_i16_be(FILE * fd, int16_t buf) { putc((buf >> 8) & 0xff, fd); putc(buf & 0xff, fd); return feof(fd) || ferror(fd); } /* * fwrite_i32_le * Write a little-endian 32-bit signed integer to file . * Returns 0 on success, != 0 on failure. */ int fwrite_i32_le(FILE * fd, int32_t buf) { putc(buf & 0xff, fd); putc((buf >> 8) & 0xff, fd); putc((buf >> 16) & 0xff, fd); putc((buf >> 24) & 0xff, fd); return feof(fd) || ferror(fd); } /* * fwrite_i32_be * Write a big-endian 32-bit signed integer to file . * Returns 0 on success, != 0 on failure. */ int fwrite_i32_be(FILE * fd, int32_t buf) { putc((buf >> 24) & 0xff, fd); putc((buf >> 16) & 0xff, fd); putc((buf >> 8) & 0xff, fd); putc(buf & 0xff, fd); return feof(fd) || ferror(fd); } /* * fwrite_u16_le * Write a little-endian 16-bit unsigned integer to file . * Returns 0 on success, != 0 on failure. */ int fwrite_u16_le(FILE * fd, uint16_t buf) { putc(buf & 0xff, fd); putc((buf >> 8) & 0xff, fd); return feof(fd) || ferror(fd); } deutex-5.2.3/src/endianio.h000066400000000000000000000012711475516661100155620ustar00rootroot00000000000000/* Copyright ® André Majorel, contributors to the DeuTex project. Those functions allow to read little-endian and big-endian integers from a file regardless of the endianness of the CPU. SPDX-License-Identifier: LGPL-2.1-or-later */ int fread_i16_le(FILE * fd, int16_t * buf); int fread_i16_be(FILE * fd, int16_t * buf); int fread_i32_le(FILE * fd, int32_t * buf); int fread_i32_be(FILE * fd, int32_t * buf); int fread_u16_le(FILE * fd, uint16_t * buf); int fwrite_i16_le(FILE * fd, int16_t buf); int fwrite_i16_be(FILE * fd, int16_t buf); int fwrite_i32_le(FILE * fd, int32_t buf); int fwrite_i32_be(FILE * fd, int32_t buf); int fwrite_u16_le(FILE * fd, uint16_t buf); deutex-5.2.3/src/endianm.c000066400000000000000000000067211475516661100154070ustar00rootroot00000000000000/* Copyright ® André Majorel, contributors to the DeuTex project. Those functions allow to read little-endian and big-endian integers from memory regardless of the endianness of the CPU. SPDX-License-Identifier: LGPL-2.1-or-later */ #include "deutex.h" #include "endianm.h" /* * read_i16_le * Read a little-endian 16-bit signed integer from memory area * pointed to by . */ void read_i16_le(const void *ptr, i16 * buf) { *buf = ((const unsigned char *) ptr)[0] | (((const unsigned char *) ptr)[1] << 8); } /* * read_i32_le * Read a little-endian 32-bit signed integer from memory area * pointed to by . */ void read_i32_le(const void *ptr, i32 * buf) { *buf = ((const unsigned char *) ptr)[0] | ((u16) ((const unsigned char *) ptr)[1] << 8) | ((i32) ((const unsigned char *) ptr)[2] << 16) | ((i32) ((const unsigned char *) ptr)[3] << 24); } /* * read_i32_be * Read a big-endian 32-bit signed integer from memory area * pointed to by . */ void read_i32_be(const void *ptr, i32 * buf) { *buf = ((i32) ((const unsigned char *) ptr)[0] << 24) | ((i32) ((const unsigned char *) ptr)[1] << 16) | ((u16) ((const unsigned char *) ptr)[2] << 8) | (((const unsigned char *) ptr)[3]); } /* * peek_i16_le * Read a little-endian 16-bit signed integer from memory area * pointed to by . */ i16 peek_i16_le(const void *ptr) { return ((const unsigned char *) ptr)[0] | (((const unsigned char *) ptr)[1] << 8); } /* * peek_u16_le * Read a little-endian 16-bit unsigned integer from memory area * pointed to by . */ u16 peek_u16_le(const void *ptr) { return ((const unsigned char *) ptr)[0] | (((const unsigned char *) ptr)[1] << 8); } /* * peek_i32_be * Read a big-endian 32-bit signed integer from memory area * pointed to by . */ i32 peek_i32_be(const void *ptr) { return ((i32) (((const unsigned char *) ptr)[0]) << 24) | ((i32) (((const unsigned char *) ptr)[1]) << 16) | ((u16) (((const unsigned char *) ptr)[2]) << 8) | ((const unsigned char *) ptr)[3]; } /* * peek_i32_le * Read a little-endian 32-bit signed integer from memory area * pointed to by . */ i32 peek_i32_le(const void *ptr) { return ((const unsigned char *) ptr)[0] | ((u16) ((const unsigned char *) ptr)[1] << 8) | ((i32) ((const unsigned char *) ptr)[2] << 16) | ((i32) ((const unsigned char *) ptr)[3] << 24); } /* * write_i16_le * Write a little-endian 16-bit signed integer to memory area * pointed to by . */ void write_i16_le(void *ptr, i16 val) { ((unsigned char *) ptr)[0] = val; ((unsigned char *) ptr)[1] = val >> 8; } /* * write_i32_be * Write a big-endian 32-bit signed integer to memory area * pointed to by . */ void write_i32_be(void *ptr, i32 val) { ((unsigned char *) ptr)[0] = val >> 24; ((unsigned char *) ptr)[1] = val >> 16; ((unsigned char *) ptr)[2] = val >> 8; ((unsigned char *) ptr)[3] = val; } /* * write_i32_le * Write a little-endian 32-bit signed integer to memory area * pointed to by . */ void write_i32_le(void *ptr, i32 val) { ((unsigned char *) ptr)[0] = val; ((unsigned char *) ptr)[1] = val >> 8; ((unsigned char *) ptr)[2] = val >> 16; ((unsigned char *) ptr)[3] = val >> 24; } deutex-5.2.3/src/endianm.h000066400000000000000000000015211475516661100154050ustar00rootroot00000000000000/* Copyright ® André Majorel, contributors to the DeuTex project. Those functions allow to read little-endian and big-endian integers from memory regardless of the endianness of the CPU. SPDX-License-Identifier: LGPL-2.1-or-later */ /* Use the names DeuTex provides */ #ifndef i16 #define i16 int16_t #endif #ifndef i32 #define i32 int32_t #endif #ifndef u16 #define u16 uint16_t #endif #ifndef u32 #define u32 uint32_t #endif void read_i16_le(const void *ptr, i16 * buf); void read_i32_le(const void *ptr, i32 * buf); void read_i32_be(const void *ptr, i32 * buf); i16 peek_i16_le(const void *ptr); u16 peek_u16_le(const void *ptr); i32 peek_i32_be(const void *ptr); i32 peek_i32_le(const void *ptr); void write_i16_le(void *ptr, i16 val); void write_i32_be(void *ptr, i32 val); void write_i32_le(void *ptr, i32 val); deutex-5.2.3/src/extract.c000066400000000000000000001152501475516661100154440ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ #include "deutex.h" #include "tools.h" #include "endianm.h" #include "text.h" #include "mkwad.h" #include "texture.h" #include "ident.h" #include "color.h" #include "picture.h" #include "sound.h" #include "sscript.h" #include "usedidx.h" /* ** here we go for some real indecent programming. sorry */ extern char file[128]; /* ** try to save entry */ static bool XTRbmpSave(int16_t * pinsrX, int16_t * pinsrY, struct WADDIR *entry, PICTYPE type, const char *DataDir, const char *dir, struct WADINFO *info, IMGTYPE Picture, bool WSafe, cusage_t * cusage) { bool res; int32_t start = entry->start; int32_t size = entry->size; char *name = entry->name; char *buffer; char *extens = NULL; if (size < 8L) return false; switch (Picture) { #ifdef HAVE_LIBPNG case PICPNG: extens = "PNG"; break; #endif case PICGIF: extens = "GIF"; break; case PICBMP: extens = "BMP"; break; case PICPPM: extens = "PPM"; break; default: Bug("EX47", "Invalid img type %d", (int) Picture); } res = MakeFileName(file, DataDir, dir, "", name, extens); if (res && WSafe) { Warning("EX48", "Will not overwrite file %s", file); return true; } buffer = (char *) Malloc(size); WADRseek(info, start); WADRreadBytes(info, buffer, size); res = PICsaveInFile(file, type, buffer, size, pinsrX, pinsrY, Picture, name, cusage); if (res) Detail("EX49", "Saved picture as %s", fname(file)); free(buffer); return res; } /* ** determine image format of a lump within TX_START..TX_END. ** returns a PICXXX value, defaulting to PICNONE when no ** specific image format matches the lump data. */ static IMGTYPE XTRpicFormat(struct WADDIR *entry, struct WADINFO *info) { int32_t start = entry->start; int32_t size = entry->size; unsigned char buf[16]; memset(buf, 0, sizeof(buf)); if (size > 16) size = 16; WADRseek(info, start); WADRreadBytes(info, buf, size); /* PNG? */ if (buf[0] == 0x89 && buf[1] == 'P' && buf[2] == 'N' && buf[3] == 'G' && buf[4] == 0x0D && buf[5] == 0x0A && buf[6] == 0x1A) { return PICPNG; } /* JPEG? */ if (buf[0] == 0xFF && buf[1] == 0xD8 && buf[2] == 0xFF && buf[3] >= 0xE0 && ((buf[6] == 'J' && buf[7] == 'F') || (buf[6] == 'E' && buf[7] == 'x'))) { return PICJPEG; } return PICNONE; } /* ** extract entries from a WAD ** ** Called with cusage == NULL, (-xtract) this function extracts ** everything in the wad to separate files. ** ** Called with cusage != NULL, (-usedidx) this function creates ** no files but print statistics about which colours are used ** in the wad. */ void XTRextractWAD(const char *doomwad, const char *DataDir, const char *wadin, const char *wadinfo, IMGTYPE Picture, SNDTYPE Sound, NTRYB select, char trnR, char trnG, char trnB, bool WSafe, cusage_t * cusage) { static struct WADINFO pwad; static struct WADINFO iwad; static struct WADINFO lwad; struct WADDIR *pdir; int16_t pnb; ENTRY *piden; int16_t p; int32_t ostart, osize; char *buffer; bool res; int16_t insrX = 0, insrY = 0; bool EntryFound; char *extens = NULL; /*text file to write */ static struct TXTFILE *TXT = NULL; Phase("EX00", "Extracting entries from wad %s", wadin); /*open iwad,get iwad directory */ iwad.ok = 0; WADRopenR(&iwad, doomwad); /* If -usedidx, we're only interested in graphics. */ if (cusage != NULL) select &= (BGRAPHIC | BSPRITE | BPATCH | BFLAT | BSNEAP | BSNEAT); /*read WAD */ pwad.ok = 0; WADRopenR(&pwad, wadin); pdir = pwad.dir; pnb = (int16_t) pwad.ntry; /*find PNAMES */ { int16_t pnm = WADRfindEntry(&iwad, "PNAMES"); char *Pnam = NULL; int32_t Pnamsz = 0; if (pnm < 0) Warning("EX01", "Iwad: no PNAMES lump"); else Pnam = WADRreadEntry(&iwad, pnm, &Pnamsz); piden = IDENTentriesPWAD(&pwad, Pnam, Pnamsz); if (Pnam != NULL) free(Pnam); } /* ** prepare for graphics */ /* Read PLAYPAL */ { const char *lumpname = palette_lump; struct WADINFO *wad; int16_t lumpnum; char *lumpdata = NULL; int32_t lumpsz; wad = &pwad; lumpnum = WADRfindEntry(wad, lumpname); if (lumpnum >= 0) lumpdata = WADRreadEntry(wad, lumpnum, &lumpsz); else { wad = &iwad; lumpnum = WADRfindEntry(wad, lumpname); if (lumpnum >= 0) lumpdata = WADRreadEntry(wad, lumpnum, &lumpsz); else { long n; wad = NULL; lumpdata = Malloc(768); Warning("EX02", "No %s lump found, making up a palette", lumpname); for (n = 0; n < 256; n++) { lumpdata[3 * n] = n; lumpdata[3 * n + 1] = (n & 0x7f) << 1; lumpdata[3 * n + 2] = (n & 0x3f) << 2; } } } COLinit(trnR, trnG, trnB, lumpdata, (int16_t) lumpsz, (wad == NULL) ? "(nofile)" : wad->filename, lumpname); free(lumpdata); } /* If TITLEPAL exists, read the first 768 bytes of it. But don't prepare COLpal2 because we don't know yet whether we need it. Indeed, if there are no sneats to extract, we're not interested in seeing any TITLEPAL-related warnings. */ { int n; char *titlepal_data = NULL; int32_t titlepal_size = 3 * NCOLOURS; n = WADRfindEntry(&pwad, "TITLEPAL"); if (n >= 0) titlepal_data = WADRreadEntry2(&pwad, n, &titlepal_size); else { n = WADRfindEntry(&iwad, "TITLEPAL"); if (n >= 0) titlepal_data = WADRreadEntry2(&iwad, n, &titlepal_size); else { titlepal_data = NULL; titlepal_size = 0; } } COLinitAlt(titlepal_data, titlepal_size); } /* ** read the PNAMES entry in PWAD ** or in DOOM.WAD if it does not exist elsewhere */ do { int16_t pnm = WADRfindEntry(&pwad, "PNAMES"); char *Pnam; int32_t lumpsz; if (pnm >= 0) Pnam = WADRreadEntry(&pwad, pnm, &lumpsz); else { pnm = WADRfindEntry(&iwad, "PNAMES"); if (pnm < 0) { Warning("EX03", "Iwad: no PNAMES lump (2)"); break; } Pnam = WADRreadEntry(&iwad, pnm, &lumpsz); } PNMinit(Pnam, lumpsz); free(Pnam); } while (0); /* ** iwad not needed anymore */ WADRclose(&iwad); /* ** output WAD creation directives ** and save entries depending on their type. ** If -usedidx, do _not_ create a directives file. */ if (cusage != NULL) TXT = &TXTdummy; /* Notional >/dev/null */ else { /*check if file exists */ TXT = TXTopenW(wadinfo); { char comment[81]; sprintf(comment, PACKAGE_NAME " %.32s", PACKAGE_VERSION); TXTaddComment(TXT, comment); } TXTaddComment(TXT, "PWAD creation directives"); } /* ** LEVELS */ if (select & BLEVEL) { Phase("EX10", "Extracting levels..."); for (EntryFound = false, p = 0; p < pnb; p++) { switch (piden[p] & EMASK) { case ELEVEL: case EMAP: if (!EntryFound) { MakeDir(file, DataDir, "LEVELS", ""); TXTaddEmptyLine(TXT); TXTaddComment(TXT, "List of levels"); TXTaddSection(TXT, "levels"); EntryFound = true; } /* entries to save in WAD, named from Level name */ { int pmax; for (pmax = p; pmax < pnb && piden[pmax] == piden[p]; pmax++); res = MakeFileName(file, DataDir, "LEVELS", "", pdir[p].name, "WAD"); if (res && WSafe) Warning("EX11", "Will not overwrite file %s", file); else { WADRopenW(&lwad, file, PWAD, 0); ostart = WADRposition(&lwad); /*BC++ 4.5 bug */ WADRdirAddEntry(&lwad, ostart, 0L, pdir[p].name); WADRwriteWADlevelParts(&lwad, &pwad, p, pmax - p); WADRwriteDir(&lwad, 0); WADRclose(&lwad); } TXTaddEntry(TXT, pdir[p].name, NULL, INVALIDINT, INVALIDINT, false, false); p = pmax - 1; } } } } /* ** LUMPS */ if (select & BLUMP) { Phase("EX15", "Extracting lumps..."); ostart = 0x80000000L; osize = 0; for (EntryFound = false, p = 0; p < pnb; p++) { if ((piden[p] & EMASK) == ELUMP) { if (!EntryFound) { MakeDir(file, DataDir, "LUMPS", ""); TXTaddEmptyLine(TXT); TXTaddComment(TXT, "List of data Lumps"); TXTaddSection(TXT, "lumps"); EntryFound = true; } if ((ostart == pwad.dir[p].start) && (osize == pwad.dir[p].size)) { TXTaddEntry(TXT, pdir[p].name, NULL, INVALIDINT, INVALIDINT, true, false); } else { ostart = pwad.dir[p].start; osize = pwad.dir[p].size; res = false; if (osize == 64000L) { /*lumps that are pics */ res = XTRbmpSave(&insrX, &insrY, &pdir[p], PLUMP, DataDir, "LUMPS", &pwad, Picture, WSafe, cusage); } if (!res) { /*normal lumps */ res = MakeFileName(file, DataDir, "LUMPS", "", pdir[p].name, "LMP"); if (res && WSafe) { Warning("EX16", "Will not overwrite file %s", file); } else { WADRsaveEntry(&pwad, p, file); Detail("EX17", "Saved lump as %s", fname(file)); } } TXTaddEntry(TXT, pdir[p].name, NULL, INVALIDINT, INVALIDINT, false, false); } } } } /* ** TEXTURES */ if (select & BTEXTUR) { EntryFound = false; for (p = 0; p < pnb; p++) { if (piden[p] == ETEXTUR + 1) { if (!EntryFound) { MakeDir(file, DataDir, "TEXTURES", ""); EntryFound = true; } TXTaddEmptyLine(TXT); TXTaddComment(TXT, "List of definitions for TEXTURE1"); TXTaddSection(TXT, "texture1"); { const char *name; /* Always extract TEXTURES as texture1.txt! */ if (texture_lump == TL_TEXTURES) name = "TEXTURE1"; else name = pdir[p].name; TXTaddEntry(TXT, name, NULL, INVALIDINT, INVALIDINT, false, false); res = MakeFileName(file, DataDir, "TEXTURES", "", name, "TXT"); } if (res && WSafe) { Warning("EX21", "Will not overwrite file %s", file); } else { buffer = (char *) Malloc(pdir[p].size); WADRseek(&pwad, pdir[p].start); WADRreadBytes(&pwad, buffer, pdir[p].size); TXUinit(); TXUreadTEXTURE(pdir[p].name, buffer, pdir[p].size, NULL, 0, true); free(buffer); TXUwriteTexFile(file); TXUfree(); } } } for (p = 0; p < pnb; p++) { if (piden[p] == ETEXTUR + 2) { if (!EntryFound) { MakeDir(file, DataDir, "TEXTURES", ""); EntryFound = true; } TXTaddEmptyLine(TXT); TXTaddComment(TXT, "List of definitions for TEXTURE2"); TXTaddSection(TXT, "texture2"); TXTaddEntry(TXT, pdir[p].name, NULL, INVALIDINT, INVALIDINT, false, false); res = MakeFileName(file, DataDir, "TEXTURES", "", pdir[p].name, "TXT"); if (res && WSafe) { Warning("EX22", "Will not overwrite file %s", file); } else { buffer = (char *) Malloc(pdir[p].size); WADRseek(&pwad, pdir[p].start); WADRreadBytes(&pwad, buffer, pdir[p].size); TXUinit(); TXUreadTEXTURE(pdir[p].name, buffer, pdir[p].size, NULL, 0, true); free(buffer); TXUwriteTexFile(file); TXUfree(); } } } } /* ** TX_START textures */ if ((select & BTEXTUR) && cusage == NULL) { Phase("EX70", "Extracting TX_START textures..."); EntryFound = false; for (p = 0; p < pnb; p++) { if (piden[p] == ETXSTART) { IMGTYPE detect_type; if (!EntryFound) { MakeDir(file, DataDir, "TX_START", ""); TXTaddEmptyLine(TXT); TXTaddComment(TXT, "List of TX_START textures"); TXTaddSection(TXT, "tx_start"); EntryFound = true; } ostart = pwad.dir[p].start; osize = pwad.dir[p].size; insrX = insrY = 0; detect_type = XTRpicFormat(&pdir[p], &pwad); if (detect_type == PICNONE) { /* assume DOOM patch format */ if (!XTRbmpSave(&insrX, &insrY, &pdir[p], PGRAPH, DataDir, "TX_START", &pwad, Picture, WSafe, cusage)) { Warning("EX71", "Failed to write TX_START texture %s", lump_name(pwad.dir[p].name)); } else { TXTaddEntry(TXT, pdir[p].name, NULL, INVALIDINT, INVALIDINT, false, false); } } else { /* write PNG or JPEG as a raw file, no conversion */ const char *ext = "PNG"; if (detect_type == PICJPEG) ext = "JPG"; res = MakeFileName(file, DataDir, "TX_START", "", pdir[p].name, ext); if (WSafe && res) { Warning("EX73", "Will not overwrite file %s", file); } else { WADRsaveEntry(&pwad, p, file); Detail("EX74", "Saved lump as %s", fname(file)); } TXTaddEntry(TXT, pdir[p].name, NULL, 1, 0, false, true); } } } } /* ** SOUNDS */ if (select & BSOUND) { Phase("EX25", "Extracting sounds..."); ostart = 0x80000000L; osize = 0; for (EntryFound = false, p = 0; p < pnb; p++) { if ((piden[p] & EMASK) == ESOUND) { if (!EntryFound) { MakeDir(file, DataDir, "SOUNDS", ""); TXTaddEmptyLine(TXT); TXTaddComment(TXT, "List of Sounds"); TXTaddSection(TXT, "sounds"); EntryFound = true; } if ((ostart == pwad.dir[p].start) && (osize == pwad.dir[p].size)) { TXTaddEntry(TXT, pdir[p].name, NULL, INVALIDINT, INVALIDINT, true, false); } else { ostart = pwad.dir[p].start; osize = pwad.dir[p].size; switch (piden[p]) { case ESNDPC: res = MakeFileName(file, DataDir, "SOUNDS", "", pdir[p].name, "TXT"); if (res && WSafe) { Warning("EX26", "Will not overwrite file %s", file); } else { char name[33]; strcpy(name, lump_name(pdir[p].name)); buffer = (char *) Malloc(pdir[p].size); WADRseek(&pwad, pdir[p].start); WADRreadBytes(&pwad, buffer, pdir[p].size); SNDsavePCSound(name, file, buffer, pdir[p].size); free(buffer); Detail("EX27", "Saved PC sound as %s", fname(file)); } TXTaddEntry(TXT, pdir[p].name, NULL, INVALIDINT, INVALIDINT, false, false); break; case ESNDWAV: switch (Sound) { case SNDWAV: extens = "WAV"; break; default: Bug("EX28", "Invalid snd type %d", Sound); } res = MakeFileName(file, DataDir, "SOUNDS", "", pdir[p].name, extens); if (res && WSafe) { Warning("EX29", "Will not overwrite file %s", fname(file)); } else { buffer = (char *) Malloc(pdir[p].size); WADRseek(&pwad, pdir[p].start); WADRreadBytes(&pwad, buffer, pdir[p].size); SNDsaveSound(file, buffer, pdir[p].size, Sound, pdir[p].name); Detail("EX30", "Saved sound as %s", fname(file)); free(buffer); } TXTaddEntry(TXT, pdir[p].name, NULL, INVALIDINT, INVALIDINT, false, false); break; default: Bug("EX31", "Invalid snd type %d", piden[p]); } } } } } /* ** MUSICS */ if (select & BMUSIC) { Phase("EX32", "Extracting musics..."); ostart = 0x80000000L; osize = 0; for (EntryFound = false, p = 0; p < pnb; p++) { if ((piden[p] & EMASK) == EMUSIC) { if (!EntryFound) { MakeDir(file, DataDir, "MUSICS", ""); TXTaddEmptyLine(TXT); TXTaddComment(TXT, "List of Musics"); TXTaddSection(TXT, "musics"); EntryFound = true; } if ((ostart == pwad.dir[p].start) && (osize == pwad.dir[p].size)) { TXTaddEntry(TXT, pdir[p].name, NULL, INVALIDINT, INVALIDINT, true, false); } else { ostart = pwad.dir[p].start; osize = pwad.dir[p].size; if (piden[p] == EMUS) { res = MakeFileName(file, DataDir, "MUSICS", "", pdir[p].name, "MUS"); } else { res = MakeFileName(file, DataDir, "MUSICS", "", pdir[p].name, "MID"); } if (res && WSafe) { Warning("EX33", "Will not overwrite file %s", fname(file)); } else { Detail("EX34", "Saving music as %s", fname(file)); WADRsaveEntry(&pwad, p, file); } TXTaddEntry(TXT, pdir[p].name, NULL, INVALIDINT, INVALIDINT, false, false); } } } } /* ** GRAPHICS */ if (select & BGRAPHIC) { Phase("EX35", "Extracting graphics..."); ostart = 0x80000000L; osize = 0; for (EntryFound = false, p = 0; p < pnb; p++) { if ((piden[p] & EMASK) == EGRAPHIC) { if (!EntryFound && cusage == NULL) { MakeDir(file, DataDir, "GRAPHICS", ""); } if ((ostart == pwad.dir[p].start) && (osize == pwad.dir[p].size)) { TXTaddEntry(TXT, pdir[p].name, NULL, INVALIDINT, INVALIDINT, true, false); } else { ostart = pwad.dir[p].start; osize = pwad.dir[p].size; if (XTRbmpSave (&insrX, &insrY, &pdir[p], PGRAPH, DataDir, "GRAPHICS", &pwad, Picture, WSafe, cusage)) { if (!EntryFound) { TXTaddEmptyLine(TXT); TXTaddComment(TXT, "List of Pictures (with insertion point)"); TXTaddSection(TXT, "graphics"); EntryFound = true; } TXTaddEntry(TXT, pdir[p].name, NULL, insrX, insrY, false, true); } else if (XTRbmpSave (&insrX, &insrY, &pdir[p], PFLAT, DataDir, "LUMPS", &pwad, Picture, WSafe, cusage)) { /*Was saved as graphic lump */ char *name = Malloc(sizeof pdir[p].name + 1); sprintf(name, "%.*s", (int) sizeof pdir[p].name, pdir[p].name); TXTaddComment(TXT, name); free(name); } else if (cusage == NULL) { if (MakeFileName (file, DataDir, "LUMPS", "", pdir[p].name, "LMP")) { Warning("EX36", "Will not overwrite file %s", fname(file)); } else { WADRsaveEntry(&pwad, p, file); Detail("EX37", "Saved lump as %s", fname(file)); } { char *name = Malloc(sizeof pdir[p].name + 1); sprintf(name, "%.*s", (int) sizeof pdir[p].name, pdir[p].name); TXTaddComment(TXT, name); free(name); } } } } } } /* ** SPRITES */ if (select & BSPRITE) { Phase("EX40", "Extracting sprites..."); ostart = 0x80000000L; osize = 0; for (EntryFound = false, p = 0; p < pnb; p++) { if ((piden[p] & EMASK) == ESPRITE) { if (!EntryFound) { if (cusage == NULL) MakeDir(file, DataDir, "SPRITES", ""); TXTaddEmptyLine(TXT); TXTaddComment(TXT, "List of Sprites"); TXTaddSection(TXT, "sprites"); EntryFound = true; } if ((ostart == pwad.dir[p].start) && (osize == pwad.dir[p].size)) { TXTaddEntry(TXT, pdir[p].name, NULL, INVALIDINT, INVALIDINT, true, false); } else { ostart = pwad.dir[p].start; osize = pwad.dir[p].size; if (!XTRbmpSave (&insrX, &insrY, &pdir[p], PSPRIT, DataDir, "SPRITES", &pwad, Picture, WSafe, cusage)) { Warning("EX41", "Failed to write sprite %s", lump_name(pwad.dir[p].name)); } else { TXTaddEntry(TXT, pdir[p].name, NULL, insrX, insrY, false, true); } } } } } /* ** PATCHES */ if (select & BPATCH) { Phase("EX45", "Extracting patches..."); for (EntryFound = false, p = 0; p < pnb; p++) { if ((piden[p] & EMASK) == EPATCH) { if (!EntryFound) { if (cusage == NULL) MakeDir(file, DataDir, "PATCHES", ""); TXTaddEmptyLine(TXT); TXTaddComment(TXT, "List of patches"); TXTaddSection(TXT, "patches"); EntryFound = true; } if (XTRbmpSave (&insrX, &insrY, &pdir[p], PPATCH, DataDir, "PATCHES", &pwad, Picture, WSafe, cusage)) { TXTaddEntry(TXT, pdir[p].name, NULL, INVALIDINT, INVALIDINT, false, false); } else { Warning("EX46", "Failed to write patch %s", lump_name(pwad.dir[p].name)); } } } } /* ** PNAMES not needed anymore */ PNMfree(); /* ** FLATS */ if (select & BFLAT) { Phase("EX50", "Extracting flats..."); ostart = 0x80000000L; osize = 0; for (EntryFound = false, p = 0; p < pnb; p++) { if ((piden[p] & EMASK) == EFLAT) { if (!EntryFound) { if (cusage == NULL) MakeDir(file, DataDir, "FLATS", ""); TXTaddEmptyLine(TXT); TXTaddComment(TXT, "List of Floors and Ceilings"); TXTaddSection(TXT, "flats"); EntryFound = true; } if ((ostart == pwad.dir[p].start) && (osize == pwad.dir[p].size)) { TXTaddEntry(TXT, pdir[p].name, NULL, INVALIDINT, INVALIDINT, true, false); } else { ostart = pwad.dir[p].start; osize = pwad.dir[p].size; if (!XTRbmpSave (&insrX, &insrY, &pdir[p], PFLAT, DataDir, "FLATS", &pwad, Picture, WSafe, cusage)) { if (strncmp(pwad.dir[p].name, "F_SKY1", 6) != 0) Warning("EX51", "Failed to write flat %s", lump_name(pwad.dir[p].name)); } else TXTaddEntry(TXT, pdir[p].name, NULL, INVALIDINT, INVALIDINT, false, false); } } } } /* Extract all sneaps */ if (select & BSNEAP) { ENTRY type = ESNEAP; Phase("EX55", "Extracting %s...", entry_type_plural(type)); ostart = 0x80000000L; osize = 0; for (EntryFound = false, p = 0; p < pnb; p++) { if (piden[p] == type) { if (!EntryFound) { char comment[40]; if (cusage == NULL) MakeDir(file, DataDir, entry_type_dir(type), ""); TXTaddEmptyLine(TXT); sprintf(comment, "List of %.20s", entry_type_plural(type)); TXTaddComment(TXT, comment); TXTaddSection(TXT, entry_type_section(type)); EntryFound = true; } if ((ostart == pwad.dir[p].start) && (osize == pwad.dir[p].size)) { TXTaddEntry(TXT, pdir[p].name, NULL, INVALIDINT, INVALIDINT, true, false); } else { ostart = pwad.dir[p].start; osize = pwad.dir[p].size; if (!XTRbmpSave (&insrX, &insrY, &pdir[p], entry_type_pictype(type), DataDir, entry_type_dir(type), &pwad, Picture, WSafe, cusage)) { Warning("EX56", "Failed to write %.20s %s", entry_type_name(type), lump_name(pwad.dir[p].name)); } else { TXTaddEntry(TXT, pdir[p].name, NULL, INVALIDINT, INVALIDINT, false, false); } } } } } /* Extract all sneats */ if (select & BSNEAT) { ENTRY type = ESNEAT; Phase("EX60", "Extracting %s...", entry_type_plural(type)); ostart = 0x80000000L; osize = 0; for (EntryFound = false, p = 0; p < pnb; p++) { if (piden[p] == type) { if (!EntryFound) { char comment[40]; if (cusage == NULL) MakeDir(file, DataDir, entry_type_dir(type), ""); TXTaddEmptyLine(TXT); sprintf(comment, "List of %.20s", entry_type_plural(type)); TXTaddComment(TXT, comment); TXTaddSection(TXT, entry_type_section(type)); EntryFound = true; } if ((ostart == pwad.dir[p].start) && (osize == pwad.dir[p].size)) { TXTaddEntry(TXT, pdir[p].name, NULL, INVALIDINT, INVALIDINT, true, false); } else { ostart = pwad.dir[p].start; osize = pwad.dir[p].size; if (!XTRbmpSave (&insrX, &insrY, &pdir[p], entry_type_pictype(type), DataDir, entry_type_dir(type), &pwad, Picture, WSafe, cusage)) { Warning("EX61", "Failed to write %.20s %s", entry_type_name(type), lump_name(pwad.dir[p].name)); } else { TXTaddEntry(TXT, pdir[p].name, NULL, INVALIDINT, INVALIDINT, false, false); } } } } } /* Extract all Strife scripts */ if (select & BSCRIPT) { ENTRY type = ESSCRIPT; Phase("EX65", "Extracting %s...", entry_type_plural(type)); ostart = 0x80000000L; osize = 0; for (EntryFound = false, p = 0; p < pnb; p++) { if (piden[p] == type) { if (!EntryFound) { char comment[40]; if (cusage == NULL) MakeDir(file, DataDir, entry_type_dir(type), ""); TXTaddEmptyLine(TXT); sprintf(comment, "List of %.20s", entry_type_plural(type)); TXTaddComment(TXT, comment); TXTaddSection(TXT, entry_type_section(type)); EntryFound = true; } if ((ostart == pwad.dir[p].start) && (osize == pwad.dir[p].size)) { TXTaddEntry(TXT, pdir[p].name, NULL, INVALIDINT, INVALIDINT, true, false); } else { ostart = pwad.dir[p].start; osize = pwad.dir[p].size; { res = MakeFileName(file, DataDir, entry_type_dir(type), "", pdir[p].name, "txt"); if (res && WSafe) { Warning("EX66", "Will not overwrite file %s", file); } else { if (sscript_save(&pwad, p, file)) { Warning("EX67", "Failed to write %.20s %s", entry_type_name(type), lump_name(pwad.dir[p].name)); } else { TXTaddEntry(TXT, pdir[p].name, NULL, INVALIDINT, INVALIDINT, false, false); } } } } } } } /* If -usedidx, print statistics */ if (cusage != NULL) { int n; printf("Npixels Idx Nlumps First lump\n"); printf("--------- --- ------ ----------\n"); for (n = 0; n < NCOLOURS; n++) printf("%9lu %3d %5lu %s\n", cusage->uses[n], n, cusage->nlumps[n], (cusage->uses[n] == 0) ? "" : lump_name(cusage->where_first[n])); putchar('\n'); } /* ** exit graphics and end */ COLfree(); free(piden); WADRclose(&pwad); TXTaddEmptyLine(TXT); TXTaddComment(TXT, "End of extraction"); TXTcloseW(TXT); Phase("EX99", "End of extraction"); } void XTRgetEntry(const char *doomwad, const char *DataDir, const char *wadin, const char *entry, IMGTYPE Picture, SNDTYPE Sound, char trnR, char trnG, char trnB) { static struct WADINFO pwad; static struct WADINFO iwad; static char Name[8]; int16_t e; char *Entry; int32_t Entrysz; char *Colors = NULL; int16_t insrX, insrY; char *extens = NULL; bool Found = false; Normalise(Name, entry); iwad.ok = 0; WADRopenR(&iwad, doomwad); /*find PLAYPAL */ e = WADRfindEntry(&iwad, palette_lump); if (e >= 0) Colors = WADRreadEntry(&iwad, e, &Entrysz); else ProgError("GE00", "%s: no %s lump in the iwad", fname(iwad.filename), lump_name(palette_lump)); WADRclose(&iwad); pwad.ok = 0; WADRopenR(&pwad, wadin); e = WADRfindEntry(&pwad, palette_lump); if (e >= 0) { free(Colors); Colors = WADRreadEntry(&pwad, e, &Entrysz); } COLinit(trnR, trnG, trnB, Colors, (int16_t) Entrysz, pwad.filename, palette_lump); free(Colors); e = WADRfindEntry(&pwad, Name); if (e < 0) ProgError("GE01", "%s: %s: lump not found", fname(pwad.filename), lump_name(entry)); Phase("GE02", "%s: %s: extracting", fname(wadin), lump_name(entry)); Entry = WADRreadEntry(&pwad, e, &Entrysz); /*try graphic */ if (!Found) if (Entrysz > 8) { switch (Picture) { #ifdef HAVE_LIBPNG case PICPNG: extens = "PNG"; break; #endif case PICGIF: extens = "GIF"; break; case PICBMP: extens = "BMP"; break; case PICPPM: extens = "PPM"; break; default: Bug("GE03", "Invalid img type %d", (int) Picture); } MakeFileName(file, DataDir, "", "", Name, extens); if (PICsaveInFile (file, PGRAPH, Entry, Entrysz, &insrX, &insrY, Picture, Name, NULL)) { Info("GE04", "Picture insertion point is (%d,%d)", insrX, insrY); Found = true; } else if ((Entrysz == 0x1000) || (Entrysz == 0x1040)) { if (PICsaveInFile (file, PFLAT, Entry, Entrysz, &insrX, &insrY, Picture, Name, NULL)) { Found = true; } } else if (Entrysz == 64000L) { if (PICsaveInFile (file, PLUMP, Entry, Entrysz, &insrX, &insrY, Picture, Name, NULL)) { Found = true; } } } if (!Found) if (peek_i16_le(Entry) == 3) if (Entrysz >= 8 + peek_i32_le(Entry + 4)) { /*save as sound */ switch (Sound) { case SNDWAV: extens = "WAV"; break; default: Bug("GE05", "Invalid snd type %d", (int) Sound); } MakeFileName(file, DataDir, "", "", Name, extens); SNDsaveSound(file, Entry, Entrysz, Sound, Name); Found = true; } if (!Found) { /*save as lump */ MakeFileName(file, DataDir, "", "", Name, "LMP"); WADRsaveEntry(&pwad, e, file); } free(Entry); WADRclose(&pwad); } deutex-5.2.3/src/extract.h000066400000000000000000000020051475516661100154420ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ /*extract.c: list dir and extract entries*/ void XTRextractWAD(const char *doomwad, const char *DataDir, const char *wadin, const char *texout, IMGTYPE Picture, SNDTYPE Sound, NTRYB select, char trnR, char trnG, char trnB, bool WSafe, cusage_t * cusage); /*extract.c: get a single entry*/ void XTRgetEntry(const char *doomwad, const char *DataDir, const char *wadin, const char *entry, IMGTYPE Picture, SNDTYPE Sound, char trnR, char trnG, char trnB); /*obsolete junk*/ void XTRtextureList(char *doomwad, char *DataDir); void XTRpatchList(char *doomwad, char *DataDir, IMGTYPE Picture, char trnR, char trnG, char trnB); deutex-5.2.3/src/ident.c000066400000000000000000000762231475516661100151030ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ #include "deutex.h" #include #include "tools.h" #include "endianm.h" #include "mkwad.h" #include "picture.h" #include "texture.h" #include "ident.h" /* ** This file contains all the routines to identify DOOM specific ** entries and structures. Fear the bugs! */ /* Functions that are going to call IDENTsetType() or IDENTdirSet() put their name here before. Thus, with -di, IDENTsetType() and IDENTdirSet() can print the name of the function that actually did the identification--their caller. */ static const char *ident_func = NULL; /****************IDENT module ***********************/ /* identify ExMx or MAPxx entries ** which begin a DOOM level. ** returns -1 if not correct */ /* Doom, Doom II, Heretic, Hexen and Strife */ static const struct { const char *name; char mandatory; /* If non-zero, warn if lump is missing */ char level_format; /* 'n' : denotes Doom/Heretic/Hexen/Strife 'a' : denotes Doom alpha '?' : can belong to either */ const char *flags; } Part[] = { {"name", 1, '?', NULL}, {"BEHAVIOR", 0, 'n', "n11"}, /* Hexen only */ {"BLOCKMAP", 1, 'n', "n10"}, {"DIALOGUE", 0, 'n', "n15"}, /* UDMF */ {"ENDMAP", 0, 'n', "n12"}, /* UDMF */ {"FLATNAME", 1, 'a', "a1"}, /* Doom alpha 0.4 and 0.5 only */ {"LINEDEFS", 1, 'n', "n2"}, {"LINES", 1, 'a', "a3"}, /* Doom alpha 0.4 and 0.5 only */ {"NODES", 1, 'n', "n7"}, {"POINTS", 1, 'a', "a2"}, /* Doom alpha 0.4 and 0.5 only */ {"REJECT", 1, 'n', "n9"}, /* Not in Doom PR */ {"SCRIPTS", 0, 'n', "n22"}, {"SECTORS", 1, '?', "a4n8"}, {"SEGS", 1, 'n', "n5"}, {"SIDEDEFS", 1, 'n', "n3"}, {"SSECTORS", 1, 'n', "n6"}, {"TEXTMAP", 0, 'n', "n13"}, /* UDMF */ {"THINGS", 1, '?', "a5n1"}, {"VERTEXES", 1, 'n', "n4"}, {"ZNODES", 0, 'n', "n14"}, /* UDMF */ {"GL_VERT", 0, 'n', "n16"}, /* GZDoom */ {"GL_SEGS", 0, 'n', "n17"}, /* GZDoom */ {"GL_SSECT", 0, 'n', "n18"}, /* GZDoom */ {"GL_NODES", 0, 'n', "n19"}, /* GZDoom */ {"GL_PVS", 0, 'n', "n20"}, {"GL_HEAD", 0, 'n', "n21"}, /* GZDoom, not an actual lump, just indicates a header */ }; static int IDENTlevelPartMax(void) { return sizeof Part / sizeof *Part - 1; } int IDENTlevelPart(const char *name) { int n; for (n = 1; n < sizeof Part / sizeof *Part; n++) { if (strncmp(Part[n].name, name, 8) == 0) return n; } //If this is is a GL node header... if (strncmp("GL_", name, 3) == 0) { for (n = 1; n < sizeof Part / sizeof *Part; n++) { if (strncmp(Part[n].name, "GL_HEAD", 7) == 0) return n; } } return -1; } int16_t IDENTlevel(const char *buffer) { if (buffer[0] == 'E' && buffer[1] >= '1' && buffer[1] <= '9' && buffer[2] == 'M' && buffer[3] >= '1' && buffer[3] <= '9') { /* ExMy */ if (buffer[4] == '\0') return ((buffer[1] & 0x0f) << 4) + (buffer[3] & 0x0f); /* ExMyz -- Doom alpha */ if (buffer[4] >= '0' && buffer[4] <= '9' && buffer[5] == '\0') { int r = 100 * (buffer[1] - '0') + 10 * (buffer[3] - '0') + buffer[4] - '0'; if (r & EMASK) return -1; /* Overflow. E2M55 is the limit. */ return r; } } /* MAPxy */ if (buffer[0] == 'M' && buffer[1] == 'A' && buffer[2] == 'P' && buffer[3] >= '0' && buffer[3] <= '9' && buffer[4] >= '0' && buffer[4] <= '9') return (int16_t) ((buffer[3] & 0xF) * 10) + (buffer[4] & 0xF); return -1; } /* ** calculate default insertion point */ int16_t IDENTinsrX(PICTYPE type, int16_t insrX, int16_t szx) { if (insrX != INVALIDINT) if (insrX > -4096) if (insrX < 4096) return insrX; /* default insertion point */ switch (type) { case PPATCH: /*mid, lower-5 ???? */ return (int16_t) (szx / 2); case PSPRIT: /*mid, lower-5 */ return (int16_t) (szx / 2); case PWEAPN: /*absolute, in 320*200 */ return (int16_t) (-(320 - szx) / 2); /* -160+X?? */ case PFLAT: /*no insertion point */ case PLUMP: /*no insertion point */ case PGRAPH: /*0,0 by default */ return (int16_t) 0; default: Bug("FB25", "Idinx (%d)", (int) type); } return (int16_t) 0; } int16_t IDENTinsrY(PICTYPE type, int16_t insrY, int16_t szy) { if (insrY != INVALIDINT) if (insrY > -4096) if (insrY < 4096) return insrY; /* default insertion point */ switch (type) { case PPATCH: /*mid, lower-5 ???? */ return (int16_t) (szy - 5); case PSPRIT: /*mid, lower-5 */ return (int16_t) (szy - 5); case PWEAPN: /*absolute, in 320*200 */ return (int16_t) (-(200 - szy)); case PFLAT: /*no insertion point */ case PLUMP: /*no insertion point */ case PGRAPH: /*0,0 by default */ return (int16_t) 0; default: Bug("FB35", "Idiny (%d)", (int) type); } return 0; } /* * IDENTgraphic * Look at the contents of lump number from wad * and return the probability that it contained a picture, * from 0 to 100. */ int IDENTgraphic(struct WADINFO *info, int16_t n) { int32_t start = info->dir[n].start; int32_t size = info->dir[n].size; unsigned char *buf; pic_head_t h; int x; int bad_order = 0; int32_t ofs_prev = 0xdeadbeef; /* Slurp the whole lump. */ buf = Malloc(size); WADRseek(info, start); WADRreadBytes(info, buf, size); /* If parse_pic_header() chokes, it must not be a valid picture */ if (parse_pic_header(buf, size, &h, NULL)) { free(buf); return 0; } /* Be even more paranoid than parse_pic_header(): check column offsets */ bad_order = 0; for (x = 0; x < h.width; x++) { int32_t ofs = 0xdeadbeef; /* Cut and pasted from picture.c. Bleagh. */ if (h.colofs_size == 4) { int32_t o; read_i32_le(((const int32_t *) h.colofs) + x, &o); ofs = o; } else if (h.colofs_size == 2) { /* In principle, the offset is signed. However, considering it unsigned helps extracting patches larger than 32 kB, like W18_1 (alpha) or SKY* (PR). Interestingly, Doom alpha and Doom PR treat the offset as signed, which is why some textures appear with tutti-frutti on the right. -- AYM 1999-09-18 */ uint16_t o; read_i16_le(((const int16_t *) h.colofs) + x, (int16_t *) & o); ofs = o; } else { /* Can't happen */ Bug("ID65", "Invalid colofs_size %d", (int) h.colofs_size); } if (buf + ofs < h.data || ofs >= size) { free(buf); return 0; } /* In a picture lump, columns appear in increasing X order. This is not mandated but, in practice, I think it's always true. If they're not, the lump is somewhat suspicious and therefore, this function returns only 50. This additional checking allows us not to mistake Doom alpha 0.4 WORLD1 for a picture. It's really a snea but it passes all the other tests of picturehood. */ if (x > 0) { int32_t delta_ofs = ofs - ofs_prev; if (delta_ofs < 1) bad_order++; } ofs_prev = ofs; } /*valid...graphic...maybe... */ free(buf); if (bad_order) return 50; else return 100; } /* * IDENTsnea * Look at the contents of lump number from wad * and return the probability that it contained a snea, * from 0 to 100. * * The snea format was used for certain graphics in Doom * alpha 0.4 and 0.5. It consists in a 2-byte header * followed by an interleaved bitmap. The first byte, W, is * the quarter of the width. The second byte, H is the * height. The bitmap is made of 4xWxH bytes. The first WxH * bytes contain the bitmap for columns 0, 4, 8, etc. The * next WxH bytes contain the bitmap for columns 1, 5, 9, * etc., and so on. No transparency. */ int IDENTsnea(struct WADINFO *info, int16_t n) { char width; char height; if (info->dir[n].size < 2) return 0; WADRseek(info, info->dir[n].start); WADRreadBytes(info, &width, 1); WADRreadBytes(info, &height, 1); if (info->dir[n].size - 2 != 4l * width * height) return 0; return 100; } /* ** set identity of an entry with known name ** set only the first entry that match this name */ static void IDENTdirSet(ENTRY * ids, struct WADINFO *info, const char *name, ENTRY ident) { int16_t n; n = WADRfindEntry(info, name); if (n >= 0) /*found it? */ if (n < (info->ntry)) if (ids[n] == EZZZZ) { if (debug_ident != NULL && ((debug_ident[0] == '*' && debug_ident[1] == '\0') || !strncmp(debug_ident, name, 8))) Info("ID90", "Ident: %-8s as %-8.32s by %.32s", lump_name(name), entry_type_name(ident), ident_func); ids[n] = ident; } } /* * IDENTsetType * Set the type of an entry */ static void IDENTsetType(ENTRY * ids, struct WADINFO *info, int n, ENTRY type) { if (debug_ident != NULL && ((debug_ident[0] == '*' && debug_ident[1] == '\0') || !strncmp(debug_ident, info->dir[n].name, 8))) Info("ID91", "Ident: %-8s as %-8.32s by %.32s", lump_name(info->dir[n].name), entry_type_name(type), ident_func); ids[n] = type; } /* ** identifies sprites from: ** S_START SS_START S_END SS_END delimiters if exist ** S_END SS_END delimiter and crawl back ** ** Precond: ids contains EZZZZ for unidentified entries */ static void IDENTdirSprites(ENTRY * ids, struct WADINFO *info, bool Check) { int16_t s_end, s_start; int16_t n; ident_func = "IDENTdirSprites"; /* ** check if there are sprites */ s_end = WADRfindEntry(info, "S_END"); if (s_end < 0) s_end = WADRfindEntry(info, "SS_END"); if (s_end < 0) return; IDENTsetType(ids, info, s_end, EVOID); /* ** check if there is a sprites begining */ s_start = WADRfindEntry(info, "S_START"); if (s_start < 0) s_start = WADRfindEntry(info, "SS_START"); /* ** guess sprite location */ if (s_start < 0) { for (n = s_end - 1; n >= 0; n--) { if (ids[n] != EZZZZ) break; /*last sprite */ if (info->dir[n].size < 8) break; /*last sprite */ if (Check) { if (IDENTgraphic(info, n) == 0) break; } IDENTsetType(ids, info, n, ESPRITE); } } /* ** declare sprites */ else { IDENTsetType(ids, info, s_start, EVOID); for (n = s_end - 1; n > s_start; n--) { if (info->dir[n].size > 8) { IDENTsetType(ids, info, n, ESPRITE); } } } } /* ** identifies flats from: ** F_START FF_START F_END FF_END delimiters if exist ** F_END FF_END delimiter and crawl back ** ** Precond: ids contains EZZZZ for unidentified entries */ static void IDENTdirFlats(ENTRY * ids, struct WADINFO *info) { int16_t f_end, f_start; int16_t n; ident_func = "IDENTdirFlats"; /* ** check if there are flats */ f_end = WADRfindEntry(info, "F_END"); if (f_end < 0) f_end = WADRfindEntry(info, "FF_END"); if (f_end < 0) return; IDENTsetType(ids, info, f_end, EVOID); IDENTdirSet(ids, info, "F1_START", EVOID); IDENTdirSet(ids, info, "F1_END", EVOID); IDENTdirSet(ids, info, "F2_START", EVOID); IDENTdirSet(ids, info, "F2_END", EVOID); IDENTdirSet(ids, info, "F3_START", EVOID); IDENTdirSet(ids, info, "F3_END", EVOID); /*F_SKY1 is not a real flat, but it must be among them */ IDENTdirSet(ids, info, "F_SKY1", EFLAT); /* ** check if there is a flats begining */ f_start = WADRfindEntry(info, "F_START"); if (f_start < 0) f_start = WADRfindEntry(info, "FF_START"); /* ** guess flat location */ if (f_start < 0) { for (n = f_end - 1; n > 0; n--) { if (ids[n] != EZZZZ) if (ids[n] != EVOID) if (ids[n] != EFLAT) break; /*last flat */ if ((info->dir[n].size == 0x1000) || (info->dir[n].size == 0x2000) || (info->dir[n].size == 0x1040)) { IDENTsetType(ids, info, n, EFLAT); } } } /* ** declare flats */ else { IDENTsetType(ids, info, f_start, EVOID); for (n = f_end - 1; n > f_start; n--) { if ((info->dir[n].size == 0x1000) || (info->dir[n].size == 0x2000) || (info->dir[n].size == 0x1040)) { IDENTsetType(ids, info, n, EFLAT); } } } } static void IDENTdirPatches(ENTRY * ids, struct WADINFO *info, char *Pnam, int32_t Pnamsz, bool Check) { int16_t p_end, p_start; int16_t n, p; char *Pnames; ident_func = "IDENTdirPatches"; /* ** find texture and pname entries */ if (texture_lump == TL_NORMAL) { IDENTdirSet(ids, info, "TEXTURE1", ETEXTUR + 1); IDENTdirSet(ids, info, "TEXTURE2", ETEXTUR + 2); } else if (texture_lump == TL_TEXTURES) { IDENTdirSet(ids, info, "TEXTURES", ETEXTUR + 1); } else if (texture_lump == TL_NONE) { ; /* No texture lump. Do nothing */ } else { Bug("IP10", "Invalid tl %d", (int) texture_lump); } IDENTdirSet(ids, info, "PNAMES", EPNAME); /* ** check if there are flats */ p_end = WADRfindEntry(info, "P_END"); if (p_end < 0) p_end = WADRfindEntry(info, "PP_END"); if (p_end >= 0) { IDENTsetType(ids, info, p_end, EVOID); /* ** check if there is a patch begining */ IDENTdirSet(ids, info, "P1_START", EVOID); IDENTdirSet(ids, info, "P2_START", EVOID); IDENTdirSet(ids, info, "P3_START", EVOID); IDENTdirSet(ids, info, "P1_END", EVOID); IDENTdirSet(ids, info, "P2_END", EVOID); IDENTdirSet(ids, info, "P3_END", EVOID); p_start = WADRfindEntry(info, "P_START"); if (p_start < 0) p_start = WADRfindEntry(info, "PP_START"); /* ** declare patches */ if (p_start >= 0) { IDENTsetType(ids, info, p_start, EVOID); for (n = p_end - 1; n > p_start; n--) { if (info->dir[n].size > 8) IDENTsetType(ids, info, n, EPATCH); } } } /* ** check for lost patches ** */ if (Check) { /*checkif PNAMES is redefined */ n = WADRfindEntry(info, "PNAMES"); if (n >= 0) { Pnames = (char *) Malloc(info->dir[n].size); WADRseek(info, info->dir[n].start); WADRreadBytes(info, Pnames, info->dir[n].size); PNMinit(Pnames, info->dir[n].size); free(Pnames); } else { /*init with default DOOM Pnames */ if (Pnam != NULL && Pnamsz != 0) PNMinit(Pnam, Pnamsz); } /*check for lost patches */ for (n = 0; n < info->ntry; n++) { if (ids[n] == EZZZZ) if (info->dir[n].size > 8) { p = PNMindexOfPatch(info->dir[n].name); /*Gcc */ if (p >= 0) { if (IDENTgraphic(info, n) != 0) IDENTsetType(ids, info, n, EPATCH); } } } PNMfree(); } } /* ** identifies graphics between TX_START and TX_END ** ** Precond: ids contains EZZZZ for unidentified entries */ static void IDENTdirTXSTART(ENTRY * ids, struct WADINFO *info) { int16_t tx_start; int16_t tx_end; int16_t n; ident_func = "IDENTdirTXSTART"; /* ** check if there are TX_START textures */ tx_start = WADRfindEntry(info, "TX_START"); tx_end = WADRfindEntry(info, "TX_END"); if (tx_start < 0 || tx_end < 0) { if (tx_start >= 0) Warning("ITX1", "TX_START exists without matching TX_END"); else if (tx_end >= 0) Warning("ITX2", "TX_END exists without matching TX_START"); return; } if (tx_end < tx_start) { Warning("ITX3", "TX_START and TX_END occur in wrong order"); return; } /* ** declare TX_START textures */ IDENTsetType(ids, info, tx_start, EVOID); IDENTsetType(ids, info, tx_end, EVOID); for (n = tx_end - 1; n > tx_start; n--) { /* format is determined later, when extracting it */ IDENTsetType(ids, info, n, ETXSTART); } } /* ** Ident unreferenced graphics */ static void IDENTdirGraphics(ENTRY * ids, struct WADINFO *info) { int16_t n; ident_func = "IDENTdirGraphics"; /*heretic fonts */ IDENTdirSet(ids, info, "FONTA_S", ELUMP); IDENTdirSet(ids, info, "FONTA_E", ELUMP); IDENTdirSet(ids, info, "FONTB_S", ELUMP); IDENTdirSet(ids, info, "FONTB_E", ELUMP); for (n = 0; n < info->ntry; n++) { if (ids[n] == EZZZZ) { if (info->dir[n].size > 8) { if (strncmp(info->dir[n].name, "FONT", 4) == 0) { IDENTsetType(ids, info, n, EGRAPHIC); } else if (strncmp(info->dir[n].name, "M_", 2) == 0) { IDENTsetType(ids, info, n, EGRAPHIC); } } } } } static void IDENTdirGraphics2(ENTRY * ids, struct WADINFO *info, bool Check) { int16_t n; ident_func = "IDENTdirGraphics2"; for (n = 0; n < info->ntry; n++) { if (ids[n] == EZZZZ) { if (info->dir[n].size > 8) { /* It's not quite clear to me why the following 6 lines are here and not in IDENTdirGraphics(), since these are name-based idents. */ if (strncmp(info->dir[n].name, "WI", 2) == 0) { IDENTsetType(ids, info, n, EGRAPHIC); } else if (strncmp(info->dir[n].name, "ST", 2) == 0) { IDENTsetType(ids, info, n, EGRAPHIC); } else if (Check) { int is_picture = IDENTgraphic(info, n); int is_snea = IDENTsnea(info, n); /* Looks more like a picture */ if (is_picture > 0 && is_picture > is_snea) IDENTsetType(ids, info, n, EGRAPHIC); /* Looks more like a snea */ else if (is_snea > 0 && is_snea > is_picture) { if (!strncmp(info->dir[n].name, "TITLEPIC", 8)) /* Snea, TITLEPAL */ IDENTsetType(ids, info, n, ESNEAT); else /* Snea, PLAYPAL */ IDENTsetType(ids, info, n, ESNEAP); } /* Looks like something that the cat brought in :-) */ else { if (is_snea > 0 && is_picture > 0 && is_snea == is_picture) Warning("IG10", "Ambiguous type for %s (picture or snea ?)", lump_name(info->dir[n].name)); IDENTsetType(ids, info, n, ELUMP); } } else { /* Never used. Too dangerous, if you want my opinion. */ IDENTsetType(ids, info, n, EGRAPHIC); } } } } } /* * IDENTdirSscripts * Identify Strife scripts (SCRIPTnn). This function does * not make sense for other games than Strife. */ static void IDENTdirSscripts(ENTRY * ids, struct WADINFO *info) { int16_t n; ident_func = "IDENTdirSscripts"; for (n = 0; n < info->ntry; n++) { if (ids[n] == EZZZZ) { if (memcmp(info->dir[n].name, "SCRIPT", 6) == 0 && isdigit(info->dir[n].name[6]) && isdigit(info->dir[n].name[7])) { IDENTsetType(ids, info, n, ESSCRIPT); } } } } /* ** Ident PC sounds */ static void IDENTdirPCSounds(ENTRY * ids, struct WADINFO *info, bool Check) { int16_t n; ident_func = "IDENTdirPCSounds"; for (n = 0; n < info->ntry; n++) { if (ids[n] == EZZZZ) { if (info->dir[n].size > 4) /*works only for DOOM, not HERETIC */ if (strncmp(info->dir[n].name, "DP", 2) == 0) { if (Check) { WADRseek(info, info->dir[n].start); if (WADRreadShort(info) == 0x0) IDENTsetType(ids, info, n, ESNDPC); } } } } } /* * IDENTdirMusics */ static void IDENTdirMusics(ENTRY * ids, struct WADINFO *info, bool Check) { int16_t n; ident_func = "IDENTdirMusics"; for (n = 0; n < info->ntry; n++) { if (ids[n] == EZZZZ) { if (info->dir[n].size >= 4) { WADRseek(info, info->dir[n].start); if (WADRreadShort(info) == 0x554D && WADRreadShort(info) == 0x1A53) { IDENTsetType(ids, info, n, EMUS); } WADRseek(info, info->dir[n].start); if (WADRreadShort(info) == 0x544D && WADRreadShort(info) == 0x6468) { IDENTsetType(ids, info, n, EMIDI); } } } } } /* ** Ident sounds */ static void IDENTdirSounds(ENTRY * ids, struct WADINFO *info, bool Doom) { int16_t n; ident_func = "IDENTdirSounds"; for (n = 0; n < info->ntry; n++) { if (ids[n] == EZZZZ) { if (info->dir[n].size > 8) { /*works only for DOOM, not HERETIC */ if (strncmp(info->dir[n].name, "DS", 2) == 0) { IDENTsetType(ids, info, n, ESNDWAV); } else if (!Doom) { WADRseek(info, info->dir[n].start); if (WADRreadShort(info) == 0x3) if (WADRreadShort(info) == 0x2B11) IDENTsetType(ids, info, n, ESNDWAV); } } } } } /* * IDENTdirLevels * This function is more complicated than I'd like it to be. */ static void IDENTdirLevels(ENTRY * ids, struct WADINFO *info) { int16_t n, l; char name[8]; char level_name[8]; ENTRY level = EVOID; /* int wrong_order = 0; */ const int part_num_max = IDENTlevelPartMax(); int in_level = 0; int lump_present[sizeof Part / sizeof *Part]; /* Really sizeof Parts / sizeof *Parts */ char level_format = '\0'; /* Initialised to avoid a warning */ int n0 = 0; /* Initialised to avoid a warning */ ident_func = "IDENTdirLevels"; for (n = 0; n < info->ntry; n++) { Normalise(name, info->dir[n].name); if (!in_level) { if (ids[n] != EZZZZ) continue; l = IDENTlevel(name); if (l >= 0) { Normalise(level_name, info->dir[n].name); level = (*name == 'M') ? EMAP : ELEVEL; level |= l; n0 = n; lump_present[0] = 1; { int n; for (n = 1; n <= part_num_max; n++) lump_present[n] = 0; } /* Don't know whether it's Doom alpha or Doom/Heretic/Hexen/Strife yet. */ level_format = '?'; in_level = 1; } } else if (in_level) { int have_next = 0; char next_name[8]; int l_next = 0; /* Initialised to avoid a warning */ int p; int p_next = 0; /* Initialised to avoid a warning */ p = IDENTlevelPart(name); lump_present[p] = 1; if (Part[p].level_format != '?') level_format = Part[p].level_format; /* Was that the last lump of the level ? */ if (n + 1 < info->ntry && ids[n + 1] == EZZZZ) { have_next = 1; Normalise(next_name, info->dir[n + 1].name); l_next = IDENTlevel(next_name); p_next = IDENTlevelPart(next_name); } if (!have_next || l_next >= 0 || p_next < 0 || (Part[p_next].level_format != '?' && level_format != '?' && Part[p_next].level_format != level_format) || lump_present[p_next]) { in_level = 0; { int i; for (i = n0; i <= n; i++) IDENTsetType(ids, info, i, level); } } } } if (in_level) Bug("IL11", "Reached EOD while in level"); } /* ** IWAD: we assume all is correct ** if Fast = true then sounds and most graphics are reported as lumps ** (this is for merge. no problem. bad identification only to be feared in PWAD) */ ENTRY *IDENTentriesIWAD(struct WADINFO *info, char *Pnam, int32_t Pnamsz, bool Fast) { int16_t n; bool Doom = false; ENTRY *ids; Phase("ID50", "IWAD entry identification..."); if (!info->ok) Bug("ID51", "IdnOeI"); ids = (ENTRY *) Malloc((info->ntry) * sizeof(ENTRY)); if (WADRfindEntry(info, "ENDTEXT") < 0) /*Not Heretic */ if (WADRfindEntry(info, "ENDOOM") >= 0) Doom = true; /* ** identify for IWAD */ for (n = 0; n < info->ntry; n++) ids[n] = EZZZZ; IDENTdirTXSTART(ids, info); /* fast */ IDENTdirSprites(ids, info, false); /*fast */ IDENTdirFlats(ids, info); /*fast */ IDENTdirLevels(ids, info); /*fast */ IDENTdirMusics(ids, info, false); /*fast */ IDENTdirPCSounds(ids, info, false); /*fast */ IDENTdirPatches(ids, info, Pnam, Pnamsz, false); /*fast */ IDENTdirGraphics(ids, info); /*fast */ /* ** Check if Strife's ENDSTRF lump is present */ if (WADRfindEntry(info, "ENDSTRF") >= 0) { IDENTdirSscripts(ids, info); } if (!Fast) { IDENTdirSounds(ids, info, Doom); /*slow! */ IDENTdirGraphics2(ids, info, true); /*slow! */ } /* unidentified entries are considered LUMPs */ ident_func = "IDENTentriesIWAD"; for (n = 0; n < info->ntry; n++) { if (ids[n] == EZZZZ) { if (info->dir[n].size >= 6) IDENTsetType(ids, info, n, ELUMP); else IDENTsetType(ids, info, n, EDATA); } } /*the end. WADR is still opened */ return ids; } ENTRY *IDENTentriesPWAD(struct WADINFO * info, char *Pnam, int32_t Pnamsz) { int16_t n; ENTRY *ids; Phase("ID10", "PWAD entry identification..."); if (!info->ok) Bug("ID11", "IdnOeP"); ids = (ENTRY *) Malloc((info->ntry) * sizeof(ENTRY)); /* ** identify for PWAD */ for (n = 0; n < info->ntry; n++) ids[n] = EZZZZ; IDENTdirTXSTART(ids, info); IDENTdirSprites(ids, info, true); IDENTdirFlats(ids, info); IDENTdirLevels(ids, info); IDENTdirMusics(ids, info, true); IDENTdirPCSounds(ids, info, true); IDENTdirPatches(ids, info, Pnam, Pnamsz, true); IDENTdirGraphics(ids, info); /* ** Check if Strife's ENDSTRF lump is present */ if (WADRfindEntry(info, "ENDSTRF") >= 0) { IDENTdirSscripts(ids, info); } IDENTdirSounds(ids, info, false); IDENTdirGraphics2(ids, info, true); ident_func = "IDENTentriesPWAD"; for (n = 0; n < info->ntry; n++) { if (ids[n] == EZZZZ) { if (info->dir[n].size > 16) IDENTsetType(ids, info, n, ELUMP); else IDENTsetType(ids, info, n, EDATA); } } /* ** unidentified entries are considered LUMPs */ /*the end. WADR is still opened */ return ids; } /* * entry_type_name * Return human-readable string for numeric entry type */ typedef struct { ENTRY type; const char *name; const char *plural; PICTYPE pictype; } entry_type_def_t; static const entry_type_def_t entry_type_def[] = { {EVOID, "label", "labels", -1}, {ELEVEL, "level(ExMy)", "levels", -1}, {EMAP, "level(MAPxy)", "levels", -1}, {ELUMP, "lump", "lumps", PLUMP}, {ETEXTUR, "texture", "textures", -1}, {EPNAME, "pname", "pnames", -1}, {ESOUND, "sound", "sounds", -1}, {EGRAPHIC, "graphics", "graphics", PGRAPH}, {ESPRITE, "sprite", "sprites", PSPRIT}, {EPATCH, "patch", "patches", PPATCH}, {EFLAT, "flat", "flats", PFLAT}, {EMUSIC, "music", "musics", -1}, {EDATA, "data", "datas", -1}, /* Counting sheeps */ {ESNEA, "snea", "sneas", -1}, {ESNEAP, "sneap", "sneaps", PSNEAP}, {ESNEAT, "sneat", "sneats", PSNEAT}, {ESSCRIPT, "script", "scripts", -1}, {0, NULL, NULL, -1} }; static ENTRY last_type = -1; static const entry_type_def_t *last_def = NULL; static const entry_type_def_t *get_entry_type_def(ENTRY type) { const entry_type_def_t *p; if (type == last_type) return last_def; for (p = entry_type_def; p->name != NULL; p++) if (p->type == type) { last_type = type; last_def = p; return p; } for (p = entry_type_def; p->name != NULL; p++) if (p->type == (type & EMASK)) { last_type = type; last_def = p; return p; } return NULL; } const char *entry_type_name(ENTRY type) { const entry_type_def_t *def = get_entry_type_def(type); if (def == NULL) return "(unknown)"; else return def->name; } const char *entry_type_plural(ENTRY type) { const entry_type_def_t *def = get_entry_type_def(type); if (def == NULL) return "(unknown)"; else return def->plural; } const char *entry_type_dir(ENTRY type) { return entry_type_plural(type); } const char *entry_type_section(ENTRY type) { return entry_type_plural(type); } PICTYPE entry_type_pictype(ENTRY type) { const entry_type_def_t *def = get_entry_type_def(type); if (def == NULL) return -1; else return def->pictype; } deutex-5.2.3/src/ident.h000066400000000000000000000023241475516661100150770ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ /* DOOM specifics*/ /* Entry Identification */ ENTRY *IDENTentriesIWAD(struct WADINFO * wad, char *Pnam, int32_t Pnamsz, bool Fast); ENTRY *IDENTentriesPWAD(struct WADINFO *wad, char *Pnam, int32_t Pnamsz); int16_t IDENTlevel(const char *buffer); /* Level Part Identification */ int IDENTlevelPart(const char *name); /* Insertion point determination*/ int16_t IDENTinsrY(PICTYPE type, int16_t insrY, int16_t szy); int16_t IDENTinsrX(PICTYPE type, int16_t insrX, int16_t szx); ENTRY IDENTsneap(struct WADINFO *info, int16_t n); const char *entry_type_name(ENTRY type); /* For EFLATS, return "flat" */ const char *entry_type_plural(ENTRY type); /* For EFLATS, return "flats" */ const char *entry_type_dir(ENTRY type); /* For EFLATS, return "flats" */ const char *entry_type_section(ENTRY type); /* For EFLATS, return "flats" */ PICTYPE entry_type_pictype(ENTRY type); /* For EFLATS, return PFLAT */ deutex-5.2.3/src/listdir.c000066400000000000000000000507201475516661100154440ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ #include "deutex.h" #include "tools.h" #include "endianm.h" #include "mkwad.h" #include "texture.h" #include "ident.h" extern char file[128]; /* ** list WAD directory and identify entries */ static char *Views[] = { "?", "All ", "Front", "FrRgt", "Right", "RrRgt", "Rear ", "RrLft", "Left ", "FrLft" }; static char *IdentView(char view) { switch (view) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': return Views[1 + view - '0']; } return Views[0]; } /* * XTRlistDir - implementation of -wadir */ void XTRlistDir(const char *doomwad, const char *wadin, NTRYB select) { int16_t n; static struct WADINFO pwad; static struct WADINFO iwad; static char buffer[128]; ENTRY *iden; ENTRY type; int32_t ntry; struct WADDIR *dir; char *typ; int16_t pnm; char *Pnam; int32_t Pnamsz = 0; /*open iwad,get iwad directory */ iwad.ok = 0; WADRopenR(&iwad, doomwad); /*find PNAMES */ pnm = WADRfindEntry(&iwad, "PNAMES"); if (pnm < 0) ProgError("LS10", "Can't find PNAMES in main WAD"); Pnam = WADRreadEntry(&iwad, pnm, &Pnamsz); WADRclose(&iwad); if (wadin != NULL) { pwad.ok = 0; WADRopenR(&pwad, wadin); iden = IDENTentriesPWAD(&pwad, Pnam, Pnamsz); ntry = pwad.ntry; dir = pwad.dir; } else { iwad.ok = 0; WADRopenR(&iwad, doomwad); iden = IDENTentriesIWAD(&iwad, Pnam, Pnamsz, false); ntry = iwad.ntry; dir = iwad.dir; } free(Pnam); Output("\nListing of WAD directory for %s\n\n", wadin != NULL ? wadin : doomwad); Output("Entry\t Size\tType\n\n"); for (n = 0; n < ntry; n++) { type = iden[n]; typ = "unknown"; /*Don't list unwanted entries */ switch (type & EMASK) { case EVOID: case EDATA: if ((select & BALL) != BALL) continue; break; /*show VOID? only if all entries */ case ELEVEL: if (!(select & BLEVEL)) continue; break; case EMAP: if (!(select & BLEVEL)) continue; break; case ETEXTUR: if (!(select & BTEXTUR)) continue; break; case EPNAME: if (!(select & BTEXTUR)) continue; break; case ESOUND: if (!(select & BSOUND)) continue; break; case EGRAPHIC: if (!(select & BGRAPHIC)) continue; break; case ELUMP: if (!(select & BLUMP)) continue; break; case ESPRITE: if (!(select & BSPRITE)) continue; break; case EPATCH: if (!(select & BPATCH)) continue; break; case EFLAT: if (!(select & BFLAT)) continue; break; case EMUSIC: if (!(select & BMUSIC)) continue; break; } switch (type & EMASK) { case EVOID: case EDATA: typ = "."; break; case ELEVEL: sprintf(buffer, "Episod %c Map %c", '0' + ((type & 0xF0) >> 4), '0' + (type & 0xF)); typ = buffer; break; case EMAP: sprintf(buffer, "Level Map %d", (type & 0xFF)); typ = buffer; break; case ETEXTUR: typ = "List of textures"; break; case EPNAME: typ = "List of patches"; break; case ESOUND: typ = "Sound"; break; case EMUSIC: typ = "Music"; break; case EGRAPHIC: if ((type & 0xFF) == 0xFF) { typ = "Graphic"; } else { sprintf(buffer, "Graphic"); typ = buffer; } break; case ELUMP: typ = "Lump of raw data"; break; case ESPRITE: typ = "Sprite"; if (strncmp(dir[n].name, "ARTI", 4) == 0) { sprintf(buffer, "Artifact\t%4.4s", &(dir[n].name[4])); } else if (dir[n].name[6] != '\0') { sprintf(buffer, "Sprite %4.4s\tph:%c %s\tph:%c %s inv.", dir[n].name, dir[n].name[4], IdentView(dir[n].name[5]), dir[n].name[6], IdentView(dir[n].name[7])); } else { sprintf(buffer, "Sprite %4.4s\tph:%c %s", dir[n].name, dir[n].name[4], IdentView(dir[n].name[5])); } typ = buffer; break; case EPATCH: typ = "Patch"; break; case EFLAT: typ = "Flat"; break; case EZZZZ: typ = "?"; break; } switch (type) { case EPATCH1: typ = "Patch 1"; break; case EPATCH2: typ = "Patch 2"; break; case EPATCH3: typ = "Patch 3"; break; case EFLAT1: typ = "Flat 1"; break; case EFLAT2: typ = "Flat 2"; break; case EFLAT3: typ = "Flat 3"; break; case ESNDPC: typ = "PC sound"; break; case ESNDWAV: typ = "WAV sound"; break; } Output("%-8s %8ld\t%s\n", lump_name(dir[n].name), dir[n].size, typ); } if (wadin != NULL) WADRclose(&pwad); else WADRclose(&iwad); free(iden); } int XTRdirCmp(const void *d1, const void *d2) { int32_t res; struct WADDIR *dir1 = (struct WADDIR *) d1; struct WADDIR *dir2 = (struct WADDIR *) d2; res = (dir1->start) - (dir2->start); if (res < 0) return -1; if (res > 0) return 1; res = (dir1->size) - (dir2->size); if (res < 0) return -1; if (res > 0) return 1; return 0; } void XTRvoidSpacesInWAD(const char *wadin) { int16_t n; static struct WADINFO pwad; int32_t ntry; struct WADDIR *dir; int32_t startpos, lastpos, ll, diff, wtotal; int32_t w3, w20, w100, w1000, w10000, w100000; wtotal = w3 = w20 = w100 = w1000 = w10000 = w100000 = 0; pwad.ok = 0; WADRopenR(&pwad, wadin); ntry = pwad.ntry; dir = pwad.dir; qsort(dir, (size_t) ntry, sizeof(struct WADDIR), XTRdirCmp); Output("\nListing of unused spaces in WAD %s\n", wadin); lastpos = 4 + 4 + 4; for (n = 0; n < ntry; n++) { diff = dir[n].start - lastpos; startpos = lastpos; ll = dir[n].start + dir[n].size; if (lastpos < ll) lastpos = ll; if (diff < 0) continue; wtotal += diff; if (diff <= 3) w3 += diff; else if (diff <= 20) w20 += diff; else if (diff <= 100) w100 += diff; else if (diff <= 1000) w1000 += diff; else if (diff <= 10000) w10000 += diff; else w100000 += diff; if (diff >= 4) /*suppress word alignement */ Output(" At offset 0x%8.8lx, %ld bytes wasted\n", startpos, diff); } Output("\nRepartition of wasted bytes:\n"); Output(" All blocks<=3 \t%ld\n", w3); Output(" All blocks<=20 \t%ld\n", w20); Output(" All blocks<=100 \t%ld\n", w100); Output(" All blocks<=1000 \t%ld\n", w1000); Output(" All blocks<=10000\t%ld\n", w10000); Output(" All blocks> 10000\t%ld\n", w100000); Output(" \t-------\n"); Output(" Total wasted bytes\t%ld\n", wtotal); WADRclose(&pwad); } struct SIDEDEF { int16_t ofsx; /* X offset for texture */ int16_t ofsy; /* Y offset for texture */ char Above[8]; /* texture name for the part above */ char Below[8]; /* texture name for the part below */ char Center[8]; /* texture name for the regular part */ int16_t Sector; /* adjacent sector */ }; /* ** Check a level ** Assumes TEXTURES are already read somewhere. */ void CheckTexture(char *tex, bool IsDef) { int n; char Name[8]; for (n = 0; n < 8; n++) { Name[n] = tex[n]; if (tex[n] == '\0') break; } Normalise(Name, Name); switch (Name[0]) { case '-': case '\0': break; default: if (IsDef) { /*if we only wish to declare the tex */ TXUfakeTex(Name); } else { if (!TXUexist(Name)) Output("Warning: undefined sidedef %s\n", lump_name(Name)); } } } /* **check textures ** IsDef=true if we just wish to declare textures */ void CheckSideDefs(struct WADINFO *pwad, int32_t start, int32_t size, bool IsDef) { struct SIDEDEF *sid; struct SIDEDEF *side; int32_t s; sid = (struct SIDEDEF *) Malloc(size); WADRseek(pwad, start); WADRreadBytes(pwad, (char *) sid, size); for (s = 0; (s * sizeof(struct SIDEDEF)) < size; s += 1) { side = sid + s; CheckTexture(side->Above, IsDef); CheckTexture(side->Below, IsDef); CheckTexture(side->Center, IsDef); } free(sid); } void CheckLevels(struct WADINFO *pwad, bool IsDef) { int16_t lev, lin, id, top; int32_t ntry = pwad->ntry; struct WADDIR *pdir = pwad->dir; for (lev = 0; lev < ntry; lev++) { id = IDENTlevel(pdir[lev].name); if (id >= 0) { for (lin = lev; lin < ntry; lin++) { top = lev + 11; if (lin > top) break; if (strncmp(pdir[lin].name, "SIDEDEFS", 8) == 0) { Output("\nChecking LEVEL %s\n\n", lump_name(pdir[lev].name)); CheckSideDefs(pwad, pdir[lin].start, pdir[lin].size, IsDef); } } } } } /* ** Test a PWAD (-check) ** ** this is absolutely sub optimal. */ void XTRstructureTest(const char *doomwad, const char *wadin) { static struct WADINFO pwad, iwad; char *Pnames; int16_t p, pnm, nbPatchs; int32_t size; static struct PICH { int16_t Xsz; int16_t Ysz; } pich; int16_t *PszX; static char name[8]; char *buffer; int16_t cs, ce; /*read PNAME in wad, if defined */ Phase("LS21", "Reading WADs"); iwad.ok = 0; WADRopenR(&iwad, doomwad); pwad.ok = 0; WADRopenR(&pwad, wadin); Phase("LS23", "Reading Patches"); pnm = WADRfindEntry(&pwad, "PNAMES"); if (pnm >= 0) { size = pwad.dir[pnm].size; Pnames = (char *) Malloc(size); WADRseek(&pwad, pwad.dir[pnm].start); WADRreadBytes(&pwad, Pnames, size); } else { pnm = WADRfindEntry(&iwad, "PNAMES"); if (pnm < 0) ProgError("LS25", "PNAMES not found"); size = iwad.dir[pnm].size; Pnames = (char *) Malloc(size); WADRseek(&iwad, iwad.dir[pnm].start); WADRreadBytes(&iwad, Pnames, size); } PNMinit(Pnames, size); free(Pnames); /* ** find each PNAME and identify Xsz Ysz */ Phase("LS27", "Checking Patches"); nbPatchs = PNMgetNbOfPatch(); PszX = (int16_t *) Malloc(nbPatchs * sizeof(int16_t)); for (p = 0; p < nbPatchs; p++) { /*for all patches */ PNMgetPatchName(name, p); pnm = WADRfindEntry(&pwad, name); if (pnm >= 0) { WADRseek(&pwad, pwad.dir[pnm].start); WADRreadBytes(&pwad, (char *) &pich, sizeof(struct PICH)); } else { pnm = WADRfindEntry(&iwad, name); if (pnm < 0) Output("Warning: Patch %s not found\n", lump_name(name)); else { WADRseek(&iwad, iwad.dir[pnm].start); WADRreadBytes(&iwad, (char *) &pich, sizeof(struct PICH)); } } PszX[p] = peek_i16_le(&pich.Xsz); } /* ** read TEXTURES */ Phase("LS29", "Reading Textures"); pnm = WADRfindEntry(&pwad, "TEXTURE1"); if (pnm >= 0) { /* read texture */ const struct WADDIR *e = pwad.dir + pnm; buffer = (char *) Malloc(e->size); WADRseek(&pwad, e->start); WADRreadBytes(&pwad, buffer, e->size); TXUinit(); TXUreadTEXTURE(e->name, buffer, e->size, NULL, 0, true); free(buffer); /*for each textures, check what is covered */ Phase("LS31", "Checking TEXTURE1"); TXUcheckTex(nbPatchs, PszX); TXUfree(); } pnm = WADRfindEntry(&pwad, "TEXTURE2"); if (pnm >= 0) { /* read texture */ const struct WADDIR *e = pwad.dir + pnm; buffer = (char *) Malloc(e->size); WADRseek(&pwad, e->start); WADRreadBytes(&pwad, buffer, e->size); TXUinit(); TXUreadTEXTURE(e->name, buffer, e->size, NULL, 0, true); free(buffer); /*for each textures, check what is covered */ Phase("LS33", "Checking TEXTURE2"); TXUcheckTex(nbPatchs, PszX); TXUfree(); } free(PszX); /* ** check if all textures composing walls are here ** */ Phase("LS35", "Checking Level SIDEDEFS for missing textures"); TXUinit(); pnm = WADRfindEntry(&pwad, "TEXTURE1"); if (pnm >= 0) { const struct WADDIR *e = pwad.dir + pnm; buffer = (char *) Malloc(e->size); WADRseek(&pwad, e->start); WADRreadBytes(&pwad, buffer, e->size); TXUreadTEXTURE(e->name, buffer, e->size, NULL, 0, true); free(buffer); } else { pnm = WADRfindEntry(&iwad, "TEXTURE1"); if (pnm >= 0) { const struct WADDIR *e = iwad.dir + pnm; buffer = (char *) Malloc(e->size); WADRseek(&iwad, e->start); WADRreadBytes(&iwad, buffer, e->size); TXUreadTEXTURE(e->name, buffer, e->size, NULL, 0, true); free(buffer); } } pnm = WADRfindEntry(&pwad, "TEXTURE2"); if (pnm >= 0) { const struct WADDIR *e = pwad.dir + pnm; buffer = (char *) Malloc(e->size); WADRseek(&pwad, e->start); WADRreadBytes(&pwad, buffer, e->size); TXUreadTEXTURE(e->name, buffer, e->size, NULL, 0, true); free(buffer); } else { pnm = WADRfindEntry(&iwad, "TEXTURE2"); if (pnm >= 0) { const struct WADDIR *e = iwad.dir + pnm; buffer = (char *) Malloc(e->size); WADRseek(&iwad, e->start); WADRreadBytes(&iwad, buffer, e->size); TXUreadTEXTURE(e->name, buffer, e->size, NULL, 0, true); free(buffer); } } CheckLevels(&pwad, false); TXUfree(); PNMfree(); WADRclose(&iwad); /* ** check sprite markers ** */ Phase("LS37", "Checking Sprites"); for (cs = ce = 0, p = 0; p < pwad.ntry; p++) { if (strncmp(pwad.dir[p].name, "S_START", 8) == 0) cs++; if (strncmp(pwad.dir[p].name, "SS_START", 8) == 0) cs++; if (strncmp(pwad.dir[p].name, "SS_END", 8) == 0) ce++; if (strncmp(pwad.dir[p].name, "S_END", 8) == 0) ce++; } if (cs > 1) ProgError("LS39", "Duplicate S_START"); if (ce > 1) ProgError("LS41", "Duplicate S_END"); if ((cs > 0) & (ce < 1)) Output("Warning: S_START but no S_END\n"); if ((cs < 1) & (ce < 1)) Info("LS43", "No need to check sprites"); else { for (cs = ce = 0, p = 0; p < pwad.ntry; p++) { if (strncmp(pwad.dir[p].name, "S_START", 8) == 0) cs = p + 1; if (strncmp(pwad.dir[p].name, "SS_START", 8) == 0) cs = p + 1; if (strncmp(pwad.dir[p].name, "SS_END", 8) == 0) ce = p; if (strncmp(pwad.dir[p].name, "S_END", 8) == 0) ce = p; } } /* ** Check flat markers */ Phase("LS45", "Checking Flats"); for (cs = ce = 0, p = 0; p < pwad.ntry; p++) { if (strncmp(pwad.dir[p].name, "F_START", 8) == 0) cs++; if (strncmp(pwad.dir[p].name, "FF_START", 8) == 0) cs++; if (strncmp(pwad.dir[p].name, "FF_END", 8) == 0) ce++; if (strncmp(pwad.dir[p].name, "F_END", 8) == 0) ce++; } if (cs > 1) Output("Warning: Duplicate F_START\n"); if (ce > 1) Output("Warning: Duplicate F_END\n"); if ((cs > 0) & (ce < 1)) Output("Warning: F_START but no F_END\n"); if ((cs < 1) & (ce < 1)) Info("LS47", "No need to check flats"); WADRclose(&pwad); } /* ** Test a PWAD (-usedtex) ** */ void XTRtextureUsed(const char *wadin) { static struct WADINFO pwad; /*read PNAME in wad, if defined */ Phase("LS50", "Listing texture used in the levels of %s", fname(wadin)); pwad.ok = 0; WADRopenR(&pwad, wadin); /* ** list all textures composing walls */ TXUinit(); CheckLevels(&pwad, true); Output("List of textures used in %s\n\n", wadin); TXUlistTex(); TXUfree(); WADRclose(&pwad); } /* ** Detect duplicate entries (-packgfx, -packnorm) ** ShowIdx = true if we also output the indexes ** ** Optimise for speed with a CRC-based check */ void XTRcompakWAD(const char *DataDir, const char *wadin, const char *texout, bool ShowIdx) { static struct WADINFO pwad; struct WADDIR *pdir; int16_t pnb; int16_t p, bas, tst, ofsx, ofsy; int32_t size, rsize, sz; bool *psame; FILE *out; char *bbas; char *btst; Phase("LS60", "Detecting duplicate entries in WAD %s", fname(wadin)); pwad.ok = 0; WADRopenR(&pwad, wadin); pnb = (int16_t) pwad.ntry; pdir = pwad.dir; psame = (bool *) Malloc(pnb); for (p = 0; p < pnb; p++) { psame[p] = false; } if (texout == NULL) MakeFileName(file, DataDir, "", "", "WADINFOP", "TXT"); else sprintf(file, "%.120s", texout); out = fopen(file, FOPEN_WT); fprintf(out, "; Indication of similar entries\n\n"); bbas = (char *) Malloc(MEMORYCACHE); btst = (char *) Malloc(MEMORYCACHE); for (bas = 0; bas < pnb; bas++) if (!psame[bas]) { /*skip already treated */ size = pdir[bas].size; if (pdir[bas].start <= 8) continue; if (size < 1) continue; if ((size >= 8) && ShowIdx) { WADRseek(&pwad, pdir[bas].start); WADRreadBytes(&pwad, bbas, 8); ofsx = ((bbas[5] << 8) & 0xFF00) + (bbas[4] & 0xFF); ofsy = ((bbas[7] << 8) & 0xFF00) + (bbas[6] & 0xFF); fprintf(out, "%-8.8s\t%d\t%d\n", pdir[bas].name, ofsx, ofsy); } else fprintf(out, "%-8.8s\n", pdir[bas].name); for (tst = bas + 1; tst < pnb; tst++) { /*if same size */ if (pdir[tst].start < 0) continue; if (pdir[tst].size != size) continue; /*check that equal */ for (sz = rsize = 0; rsize < size; rsize += sz) { sz = (size - rsize > MEMORYCACHE) ? MEMORYCACHE : size - rsize; WADRseek(&pwad, pdir[bas].start + rsize); WADRreadBytes(&pwad, bbas, sz); WADRseek(&pwad, pdir[tst].start + rsize); WADRreadBytes(&pwad, btst, sz); for (p = 0; p < sz; p++) { if (bbas[p] != btst[p]) break; } if (p < sz) break; } if (rsize == size) { /*entry identical to reference */ psame[tst] = true; fprintf(out, "%-8.8s\t*\n", pdir[tst].name); } } } fclose(out); WADRclose(&pwad); free(bbas); free(btst); free(psame); } deutex-5.2.3/src/lists.c000066400000000000000000000463421475516661100151350ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ /* This codes needs a serious cleaning now that it's stable! Too many parts are non optimised. */ #include "deutex.h" #include "tools.h" #include "mkwad.h" /*for find entry */ #include "lists.h" #define X_NONE 0 #define X_START 2 #define X_END 1 /* ** list of distinct entries types */ struct ELIST { int16_t Top; /*num of entries allocated for list */ int16_t Pos; /*current top position */ struct WADDIR *Lst; }; static struct ELIST LISlmp; static struct ELIST LISspr; static struct ELIST LISpat; static struct ELIST LISflt; /* ** init */ static void LISinitLists(void) { /* GRAPHIC, LEVEL, LUMP */ LISlmp.Top = 1; LISlmp.Pos = 0; LISlmp.Lst = NULL; LISspr.Top = 1; LISspr.Pos = 0; LISspr.Lst = NULL; LISpat.Top = 1; LISpat.Pos = 0; LISpat.Lst = NULL; LISflt.Top = 1; LISflt.Pos = 0; LISflt.Lst = NULL; } /* ** count the number of entry types */ static void LIScountTypes(ENTRY * ids, int16_t nb) { int16_t i; for (i = 0; i < nb; i++) { switch (ids[i] & EMASK) { case EPNAME: case ETEXTUR: case EMAP: case ELEVEL: case ELUMP: case EGRAPHIC: case EMUSIC: case ESOUND: case EDATA: LISlmp.Top++; break; case ESPRITE: LISspr.Top++; break; case EPATCH: LISpat.Top++; break; case EFLAT: LISflt.Top++; break; case EZZZZ: case EVOID: break; default: Bug("LI01", "LisUkn"); } } } static void LISallocLists(void) { /*allocate memory for the lists */ LISlmp.Lst = (struct WADDIR *) Malloc(LISlmp.Top * sizeof(struct WADDIR)); LISspr.Lst = (struct WADDIR *) Malloc(LISspr.Top * sizeof(struct WADDIR)); LISpat.Lst = (struct WADDIR *) Malloc(LISpat.Top * sizeof(struct WADDIR)); LISflt.Lst = (struct WADDIR *) Malloc(LISflt.Top * sizeof(struct WADDIR)); } static void LISfreeLists(void) { free(LISflt.Lst); free(LISpat.Lst); free(LISspr.Lst); free(LISlmp.Lst); } /* ** Delete unwanted sprites ** */ static bool LISunwantedPhase(char pv[2], char phase, char view, bool AllViews) { if (pv[0] != phase) return false; if (pv[1] == view) return true; /*delete phase if equal */ switch (pv[1]) { /*view 0 unwanted if any other view exist */ case '0': return (AllViews == true); /*view unwanted if view 0 exist */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': return (AllViews == false); } return false; } static bool LISdeleteSprite(char root[8], char phase, char view) { int16_t l, sz; bool Okay = false; bool AllViews; switch (view) { case '0': AllViews = false; break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': AllViews = true; break; case '\0': default: /*Artifacts */ return false; } /*root: only the 4 first char are to be tested */ for (l = LISspr.Pos - 1; l >= 0; l--) { if (strncmp(LISspr.Lst[l].name, root, 4) == 0) { /*2nd unwanted: replace by 0 */ if (LISunwantedPhase (&(LISspr.Lst[l].name[6]), phase, view, AllViews)) { LISspr.Lst[l].name[6] = '\0'; LISspr.Lst[l].name[7] = '\0'; Okay = true; } /*1st unwanted: replace by second */ if (LISunwantedPhase (&(LISspr.Lst[l].name[4]), phase, view, AllViews)) { LISspr.Lst[l].name[4] = LISspr.Lst[l].name[6]; LISspr.Lst[l].name[5] = LISspr.Lst[l].name[7]; LISspr.Lst[l].name[6] = '\0'; LISspr.Lst[l].name[7] = '\0'; Okay = true; } /*neither 1st nor second wanted: delete */ if (LISspr.Lst[l].name[4] == '\0') { sz = LISspr.Pos - (l + 1); if (sz > 0) Memcpy(&(LISspr.Lst[l]), &(LISspr.Lst[l + 1]), (sz) * sizeof(struct WADDIR)); LISspr.Pos--; Okay = true; } } } return Okay; } /* ** list management. very basic. no hash table. */ static void LISaddMission(struct WADDIR *dir, int16_t found) { int16_t l = 0, dummy = 0; /*check */ if (LISlmp.Pos >= LISlmp.Top) Bug("LI03", "LisTsm"); /*level list too small */ /*try to locate mission entry at l */ for (l = 0; l < LISlmp.Pos; l++) { if (strncmp(LISlmp.Lst[l].name, dir[0].name, 8) == 0) break; /*l LISlmp.Pos) LISlmp.Pos = dummy; if (LISlmp.Pos > LISlmp.Top) Bug("LI05", "LisTsm"); /*level list too small */ /*copy new level, or replace existing */ Memcpy((&(LISlmp.Lst[l])), (dir), found * sizeof(struct WADDIR)); } static void LISadd(struct ELIST *L, struct WADDIR *dir) { /*check current position */ if ((L->Pos) >= L->Top) Bug("LI07", "LisSml"); /*list count too small */ /* ADD in List */ Memcpy(&(L->Lst[(L->Pos)]), (dir), sizeof(struct WADDIR)); (L->Pos)++; /*increase pointer in list */ return; } /* ** Find in list, and replace if exist ** */ static bool LISfindRplc(struct ELIST *L, struct WADDIR *dir) { int16_t l; for (l = 0; l < (L->Pos); l++) if (strncmp(L->Lst[l].name, dir[0].name, 8) == 0) { Memcpy(&(L->Lst[l]), &(dir[0]), sizeof(struct WADDIR)); return true; } return false; /*not found */ } /* Substitute In List ** if entry exist, it is substituted, else added at the end of list ** suitable for LUMPS and SPRITES ** but generally those shall not be added: they may not be recognized */ static void LISsubstit(struct ELIST *L, struct WADDIR *dir) { /* SUBSTIT in List */ if (LISfindRplc(L, dir)) return; Warning("LI09", "Entry %s might be ignored by the game", lump_name(dir[0].name)); LISadd(L, dir); } /* Move In List ** if entry already exist, destroy it, and put at the end ** suitable for PATCH and FLATS (to define new animations) */ static void LISmove(struct ELIST *L, struct WADDIR *dir) { int16_t l, sz; for (l = 0; l < (L->Pos); l++) if (strncmp(L->Lst[l].name, dir[0].name, 8) == 0) { /*entry already exist. destroy it */ sz = (L->Pos) - (l + 1); if (sz > 0) Memcpy(&(L->Lst[l]), &(L->Lst[l + 1]), (sz) * sizeof(struct WADDIR)); (L->Pos)--; } LISadd(L, dir); } /* Put Sprites In List ** if entry already exist, replace it ** if entry does not exist, check if it does not obsolete ** an IWAD sprite entry ** suitable for SPRITES only */ static void LISaddSprite(struct WADDIR *dir, bool Warn) { bool Okay = false; /*warn: false= no warning. true = warn. */ /* If entry already exists, replace it */ if (LISfindRplc(&LISspr, dir)) return; /* Entry does not exist. Check that this sprite ** viewpoint doesn't obsolete other sprite viewpoints. */ if (dir[0].name[4] != '\0') { Okay |= LISdeleteSprite(dir[0].name, dir[0].name[4], dir[0].name[5]); if (dir[0].name[6] != '\0') Okay |= LISdeleteSprite(dir[0].name, dir[0].name[6], dir[0].name[7]); } if (!Okay && Warn) { Warning("LI11", "Entry %s might be ignored by the game", lump_name(dir[0].name)); } LISadd(&LISspr, dir); } /* ** create a new directory list ** */ static void LISmakeNewDir(struct WADINFO *nwad, int16_t S_END, int16_t P_END, int16_t F_END, int16_t Pn, int16_t Fn) { int16_t n; struct WADDIR *dir; /*levels,lumps, graphics into new dir */ if (LISlmp.Pos > 0) { for (n = 0; n < LISlmp.Pos; n++) { dir = &LISlmp.Lst[n]; WADRdirAddPipo(nwad, dir->start, dir->size, dir->name); } } /*sprites into new dir */ if (LISspr.Pos > 0) { WADRdirAddPipo(nwad, 0, 0, ((S_END & X_START) ? "S_START" : "SS_START")); for (n = 0; n < LISspr.Pos; n++) { dir = &LISspr.Lst[n]; WADRdirAddPipo(nwad, dir->start, dir->size, dir->name); } WADRdirAddPipo(nwad, 0, 0, ((S_END & X_END) ? "S_END" : "SS_END")); } /*patch 1,2,3 into new dir */ if (LISpat.Pos > 0) { WADRdirAddPipo(nwad, 0, 0, ((P_END & X_START) ? "P_START" : "PP_START")); if ((Pn >= 1) && (P_END & X_START)) WADRdirAddPipo(nwad, 0, 0, "P1_START"); for (n = 0; n < LISpat.Pos; n++) { dir = &LISpat.Lst[n]; WADRdirAddPipo(nwad, dir->start, dir->size, dir->name); } if ((Pn >= 1) && (P_END & X_START)) { WADRdirAddPipo(nwad, 0, 0, "P1_END"); if (Pn >= 2) { WADRdirAddPipo(nwad, 0, 0, "P2_START"); WADRdirAddPipo(nwad, 0, 0, "P2_END"); if (Pn >= 3) { WADRdirAddPipo(nwad, 0, 0, "P3_START"); WADRdirAddPipo(nwad, 0, 0, "P3_END"); } } } WADRdirAddPipo(nwad, 0, 0, ((P_END & X_END) ? "P_END" : "PP_END")); } /*flat 1,2,3 into new dir */ if (LISflt.Pos > 0) { WADRdirAddPipo(nwad, 0, 0, ((F_END & X_START) ? "F_START" : "FF_START")); if ((Fn >= 1) && (F_END & X_START)) WADRdirAddPipo(nwad, 0, 0, "F1_START"); for (n = 0; n < LISflt.Pos; n++) { dir = &LISflt.Lst[n]; WADRdirAddPipo(nwad, dir->start, dir->size, dir->name); } if ((Fn >= 1) && (F_END & X_START)) { WADRdirAddPipo(nwad, 0, 0, "F1_END"); if (Fn >= 2) { WADRdirAddPipo(nwad, 0, 0, "F2_START"); WADRdirAddPipo(nwad, 0, 0, "F2_END"); if (Fn >= 3) { WADRdirAddPipo(nwad, 0, 0, "F3_START"); WADRdirAddPipo(nwad, 0, 0, "F3_END"); } } } WADRdirAddPipo(nwad, 0, 0, ((F_END & X_END) ? "F_END" : "FF_END")); } } /* ** merge IWAD and PWAD directories ** This function is a good example of COMPLETELY INEFFICIENT CODING. ** but since it's so hard to have it work correctly, ** Optimising was out of question. ** ** */ struct WADDIR *LISmergeDir(int32_t * pNtry, bool Append, bool Complain, NTRYB select, struct WADINFO *iwad, ENTRY * iiden, int32_t iwadflag, struct WADINFO *pwad, ENTRY * piden, int32_t pwadflag) { struct WADDIR *idir; struct WADDIR *pdir; struct WADINFO nwad; int16_t inb, pnb; int16_t i, p, found; struct WADINFO *refwad; ENTRY type; int16_t Pn = 0; /*0=nothing 1=P1_ 2=P2_ 3=P3_ */ int16_t Fn = 0; /*0=nothing 1=F1_ 2=F2_ 3=F3_ */ bool NoSprite = false; /*no sprites declared. seek in graphics */ int16_t S_END = X_NONE; int16_t F_END = X_NONE; int16_t P_END = X_NONE; /* ** select Sprites markers (tricky hack!) */ refwad = (!Append || (select & BSPRITE)) ? iwad : pwad; if (WADRfindEntry(refwad, "S_END") >= 0) { /*full sprite list is here */ S_END |= X_END; } if (WADRfindEntry(refwad, "S_START") >= 0) { /*full sprite list is here */ S_END |= X_START; } if (!(select & BSPRITE)) { if (S_END & X_END) { /*sprites already appended. keep it so. */ Complain = false; } } /* ** select Patches markers (tricky hack!) */ refwad = (!Append) ? iwad : pwad; if (WADRfindEntry(refwad, "P_END") >= 0) { /*full patch list is here */ P_END |= X_END; if (WADRfindEntry(refwad, "P3_END") >= 0) Pn = 3; else if (WADRfindEntry(refwad, "P2_END") >= 0) Pn = 2; else if (WADRfindEntry(refwad, "P1_END") >= 0) Pn = 1; } if (WADRfindEntry(refwad, "P_START") >= 0) { /*full patch list is here */ P_END |= X_START; } /* ** select Flats markers (tricky hack!) */ refwad = (!Append || (select & BFLAT)) ? iwad : pwad; if (WADRfindEntry(refwad, "F_END") >= 0) { /*full flat list is here */ F_END |= X_END; if (WADRfindEntry(refwad, "F3_END") >= 0) Fn = 3; else if (WADRfindEntry(refwad, "F2_END") >= 0) Fn = 2; else if (WADRfindEntry(refwad, "F1_END") >= 0) Fn = 1; } if (WADRfindEntry(refwad, "F_START") >= 0) { /*full flat list is here */ F_END |= X_START; } if (!(select & BFLAT)) { if (F_END & X_END) { /*flats already appended. keep it so. */ Complain = false; } } /* ** make lists of types lists */ inb = (int16_t) iwad->ntry; pnb = (int16_t) pwad->ntry; idir = iwad->dir; pdir = pwad->dir; /*alloc memory for a fake new wad */ nwad.ok = 0; WADRopenPipo(&nwad, (int32_t) inb + (int32_t) pnb + 40); /*init lists */ LISinitLists(); /*identify the elements and count types */ LIScountTypes(iiden, (int16_t) iwad->ntry); LIScountTypes(piden, (int16_t) pwad->ntry); LISallocLists(); /* distribute IWAD enties */ for (i = 0; i < inb; i++) { idir[i].start |= iwadflag; } for (i = 0; i < inb; i++) { type = iiden[i]; switch (type & EMASK) { case ELEVEL: case EMAP: if (!Append) { /*APPEND doesn't need old enties */ if (select & BLEVEL) { for (found = 1; found < 11; found++) { if (iiden[i + found] != iiden[i]) break; } LISaddMission(&(idir[i]), found); i += found - 1; } } break; case ELUMP: case EGRAPHIC: case ETEXTUR: case EPNAME: case EMUSIC: case ESOUND: case EDATA: if (!Append) { /*APPEND doesn't need old enties */ LISadd(&LISlmp, &(idir[i])); } break; case EPATCH: /*if(AllPat!=true) type=ELUMP; */ if (!Append) { /*APPEND doesn't need old enties */ if (select & BPATCH) { LISadd(&LISpat, &(idir[i])); } } break; case ESPRITE: if (select & BSPRITE) { LISadd(&LISspr, &(idir[i])); } break; case EFLAT: if (select & BFLAT) { LISadd(&LISflt, &(idir[i])); } break; case EVOID: case EZZZZ: break; default: /*LisUkI */ Bug("LI13", "Iwad entry has unknown type %04X", (unsigned) type); break; } } free(iiden); /*update position of PWAD entries */ for (p = 0; p < pnb; p++) { pdir[p].start |= pwadflag; } /* ** detect the absence of sprites */ if (select & BSPRITE) { NoSprite = true; /*if no sprites, graphics could be sprites */ for (p = 0; p < pnb; p++) { if ((piden[p] & EMASK) == ESPRITE) { NoSprite = false; break; /*don't bother searching */ } } } /*put PWAD entries */ for (p = 0; p < pnb; p++) { type = piden[p]; switch (piden[p] & EMASK) { /*special treatment for missions */ case ELEVEL: case EMAP: for (found = 1; found < 11; found++) { if (piden[p + found] != piden[p]) break; } LISaddMission(&(pdir[p]), found); p += found - 1; break; case EPATCH: LISmove(&LISpat, &(pdir[p])); break; case EDATA: case ELUMP: if (!Append && Complain) /*warn if missing */ LISsubstit(&LISlmp, &(pdir[p])); else /*no warning needed */ LISmove(&LISlmp, &(pdir[p])); break; case ETEXTUR: case EPNAME: if (!Append && Complain) /*warn if missing */ LISsubstit(&LISlmp, &(pdir[p])); else /*no warning needed */ LISmove(&LISlmp, &(pdir[p])); break; case ESOUND: if (!Append && Complain) /*warn if missing */ LISsubstit(&LISlmp, &(pdir[p])); else /*no warning needed */ LISmove(&LISlmp, &(pdir[p])); break; case EMUSIC: if (!Append && Complain) /*warn if missing */ LISsubstit(&LISlmp, &(pdir[p])); else /*no warning needed */ LISmove(&LISlmp, &(pdir[p])); break; case EGRAPHIC: if (NoSprite) { /*special: look for sprites identified as graphics */ if (!LISfindRplc(&LISspr, &(pdir[p]))) { /*not a sprite. add in lumps */ LISmove(&LISlmp, &(pdir[p])); } } else { /*normal */ if (!Append && Complain) /*warn if missing */ LISsubstit(&LISlmp, &(pdir[p])); else /*no warning needed */ LISmove(&LISlmp, &(pdir[p])); } break; case ESPRITE: /*special sprite viewpoint kill */ /*warn if missing */ LISaddSprite(&(pdir[p]), Complain); break; case EFLAT: /*add and replace former flat if needed */ LISmove(&LISflt, &(pdir[p])); break; case EVOID: case EZZZZ: break; default: /* LisUkP */ Bug("LI15", "Pwad entry has unknown type %04X", (unsigned) type); break; } } free(piden); /*create the new directory */ LISmakeNewDir(&nwad, S_END, P_END, F_END, Pn, Fn); /*free memory */ LISfreeLists(); /*return parameters wad dir and wad dir size */ return WADRclosePipo(&nwad, /*size */ pNtry); } deutex-5.2.3/src/lists.h000066400000000000000000000012161475516661100151310ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ /*function to merge two WAD directories (complex)*/ struct WADDIR *LISmergeDir(int32_t * pNtry, bool OnlySF, bool Complain, NTRYB select, struct WADINFO *iwad, ENTRY * iiden, int32_t iwadflag, struct WADINFO *pwad, ENTRY * piden, int32_t pwadflag); deutex-5.2.3/src/lzw.c000066400000000000000000000411561475516661100146110ustar00rootroot00000000000000/* +-------------------------------------------------------------------+ */ /* | Copyright 1993, David Koblas (koblas@netcom.com) | */ /* | | */ /* | Permission to use, copy, modify, and to distribute this software | */ /* | and its documentation for any purpose is hereby granted without | */ /* | fee, provided that the above copyright notice appear in all | */ /* | copies and that both that copyright notice and this permission | */ /* | notice appear in supporting documentation. There is no | */ /* | representations about the suitability of this software for | */ /* | any purpose. this software is provided "as is" without express | */ /* | or implied warranty. | */ /* | | */ /* +-------------------------------------------------------------------+ */ /* ** This file is derived from ppmtogif.c and giftoppm.c ** only the compression/decompression routines were kept. ** ** Based on GIFENCOD by David Rowley .A ** Lempel-Zim compression based on "compress". ** ** Copyright (C) 1989 by Jef Poskanzer. ** ** Permission to use, copy, modify, and distribute this software and its ** documentation for any purpose and without fee is hereby granted, provided ** that the above copyright notice appear in all copies and that both that ** copyright notice and this permission notice appear in supporting ** documentation. This software is provided "as is" without express or ** implied warranty. ** ** The Graphics Interchange Format(c) is the Copyright property of ** CompuServe Incorporated. GIF(sm) is a Service Mark property of ** CompuServe Incorporated. ** ** This coder is included in DeuTex only because that of David Kaplan ** didn't work ** */ #include "deutex.h" #include "tools.h" /* * General DEFINEs */ #define BITS 12 #define HSIZE 5003 /* 80% occupancy */ typedef char char_type; typedef int16_t code_int; /* 2**BITS values of type int, and also -1 */ typedef int32_t count_int; /*static int16_t table[2][(1< static int16_t n_bits; /* number of bits/code */ static int16_t maxbits; /* user settable max # bits/code */ static code_int maxcode; /* maximum code, given n_bits */ static code_int maxmaxcode; /* should NEVER generate this code */ #define MAXCODE(n_bits) (((code_int) 1 << (n_bits)) - 1) #define HashTabOf(i) htab[i] #define CodeTabOf(i) codetab[i] static code_int hsize; /* for dynamic table sizing */ static uint32_t cur_accum; static int16_t cur_bits; /* * To save much memory, we overlay the table used by compress() with those * used by decompress(). The tab_prefix table is the same size and type * as the codetab. The tab_suffix table needs 2**BITS characters. We * get this from the beginning of htab. The output stack uses the rest * of htab, and contains characters. There is plenty of room for any * possible stack (stack used to be 8000 characters). */ #define tab_prefixof(i) CodeTabOf(i) #define tab_suffixof(i) ((char_type*)(htab))[i] #define de_stack ((char_type*)&tab_suffixof((code_int)1< 0) goto probe; nomatch: output((code_int) ent); out_count++; ent = c; if (free_ent < maxmaxcode) { CodeTabOf(i) = free_ent++; /* code -> hashtable */ HashTabOf(i) = fcode; } else cl_block(); } /* * Put out the final code. */ output((code_int) ent); ++out_count; output((code_int) EOFCode); } /***************************************************************** * TAG( output ) * * Output the given code. * Inputs: * code: A n_bits-bit integer. If == -1, then EOF. This assumes * that n_bits =< (int32_t)wordsize - 1. * Outputs: * Outputs code to the file. * Assumptions: * Chars are 8 bits int32_t. * Algorithm: * Maintain a BITS character long buffer (so that 8 codes will * fit in it exactly). Use the VAX insv instruction to insert each * code in turn. When the buffer fills up empty it and start over. */ static uint32_t masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; static void output(code_int code) { cur_accum &= masks[cur_bits]; if (cur_bits > 0) cur_accum |= ((int32_t) code << cur_bits); else cur_accum = code; cur_bits += n_bits; while (cur_bits >= 8) { char_out((uint16_t) (cur_accum & 0xff)); cur_accum >>= 8; cur_bits -= 8; } /* * If the next entry is going to be too big for the code size, * then increase it, if possible. */ if (free_ent > maxcode || clear_flg) { if (clear_flg) { maxcode = (code_int) (MAXCODE(n_bits = g_init_bits)); clear_flg = 0; } else { n_bits++; if (n_bits == maxbits) maxcode = maxmaxcode; else maxcode = (code_int) (MAXCODE(n_bits)); } } if (code == EOFCode) { /* At EOF, write the rest of the buffer. */ while (cur_bits > 0) { char_out((uint16_t) (cur_accum & 0xff)); cur_accum >>= 8; cur_bits -= 8; } flush_char(); fflush(g_outfile); } } /* * Clear out the hash table */ static void cl_block() { /* table clear for block compress */ cl_hash((count_int) hsize); free_ent = (code_int) (ClearCode + 2); clear_flg = 1; output((code_int) ClearCode); } static void cl_hash(register count_int hsize) { /* reset code table */ count_int *htab_p = htab + (int) hsize; register int32_t i; register int32_t m1 = -1; i = hsize - 16; do { /* might use Sys V memset(3) here */ *(htab_p - 16) = m1; *(htab_p - 15) = m1; *(htab_p - 14) = m1; *(htab_p - 13) = m1; *(htab_p - 12) = m1; *(htab_p - 11) = m1; *(htab_p - 10) = m1; *(htab_p - 9) = m1; *(htab_p - 8) = m1; *(htab_p - 7) = m1; *(htab_p - 6) = m1; *(htab_p - 5) = m1; *(htab_p - 4) = m1; *(htab_p - 3) = m1; *(htab_p - 2) = m1; *(htab_p - 1) = m1; htab_p -= 16; } while ((i -= 16) >= 0); for (i += 16; i > 0; --i) *--htab_p = m1; } /****************************************************************************** * * GIF Specific routines * ******************************************************************************/ /* * Number of characters so far in this 'packet' */ static int16_t a_count; /* * Set up the 'byte output' routine */ static void char_init() { a_count = 0; } /* * Define the storage for the packet accumulator */ static char accum[256]; /* * Add a character to the end of the current packet, and if it is 254 * characters, flush the packet to disk. */ static void char_out(int16_t c) { accum[a_count++] = (char) c; if (a_count >= 254) flush_char(); } /* * Flush the packet to disk, and reset the accumulator */ static void flush_char() { if (a_count > 0) { fputc(a_count, g_outfile); fwrite(accum, 1, a_count, g_outfile); a_count = 0; } } /* The End */ /* ** ** Gif decompression ** */ #define true 1 #define false 0 #define ReadOK(file,buffer,len) (fread(buffer, len, 1, file) == 1) static int16_t ZeroDataBlock = false; int16_t GetDataBlock(FILE * fd, unsigned char *buf) { unsigned char count; if (!ReadOK(fd, &count, 1)) { return -1; } ZeroDataBlock = (count == 0) ? true : false; if ((count != 0) && (!ReadOK(fd, buf, count))) { return -1; } return (int16_t) (count & 0xFF); } int16_t GetCode(FILE * fd, int16_t code_size, int16_t flag) { static unsigned char buf[280]; static int16_t curbit, lastbit, done, last_byte; int16_t i, j, ret; unsigned char count; if (flag) { curbit = 0; lastbit = 0; done = false; return 0; } if ((curbit + code_size) >= lastbit) { if (done) { return -1; } buf[0] = buf[last_byte - 2]; buf[1] = buf[last_byte - 1]; if ((count = (unsigned char) GetDataBlock(fd, buf + (2))) == 0) done = true; last_byte = (int16_t) (2 + count); curbit = (int16_t) ((curbit - lastbit) + 16); lastbit = (int16_t) ((2 + count) * 8); } ret = 0; for (i = curbit, j = 0; j < code_size; ++i, ++j) ret |= ((buf[i / 8] & (1 << (i % 8))) != 0) << j; curbit += code_size; return ret; } int16_t LWZReadByte(FILE * fd, int16_t flag, int16_t input_code_size) { static int16_t fresh = false; int16_t code, incode; register int16_t i; static int16_t code_size, set_code_size; static int16_t max_code, max_code_size; static int16_t firstcode, oldcode; static int16_t clear_code, end_code; static int16_t *sp; if (flag) { set_code_size = input_code_size; code_size = (int16_t) (set_code_size + 1); clear_code = (int16_t) (1 << set_code_size); end_code = (int16_t) (clear_code + 1); max_code_size = (int16_t) (2 * clear_code); max_code = (int16_t) (clear_code + 2); GetCode(fd, 0, true); fresh = true; for (i = 0; i < clear_code; ++i) { table[0][i] = 0; table[1][i] = i; } for (; i < (1 << BITS); ++i) table[0][i] = table[1][0] = 0; sp = stack; return 0; } else if (fresh) { fresh = false; do { firstcode = oldcode = GetCode(fd, code_size, false); } while (firstcode == clear_code); return firstcode; } if (sp > stack) return *--sp; while ((code = GetCode(fd, code_size, false)) >= 0) { if (code == clear_code) { for (i = 0; i < clear_code; ++i) { table[0][i] = 0; table[1][i] = i; } for (; i < (1 << BITS); ++i) table[0][i] = table[1][i] = 0; code_size = (int16_t) (set_code_size + 1); max_code_size = (int16_t) (2 * clear_code); max_code = (int16_t) (clear_code + 2); sp = stack; firstcode = oldcode = GetCode(fd, code_size, false); return firstcode; } else if (code == end_code) { int16_t count; unsigned char buf[260]; if (ZeroDataBlock) return -2; while ((count = GetDataBlock(fd, buf)) > 0); return -2; } incode = code; if (code >= max_code) { *sp++ = firstcode; code = oldcode; } while (code >= clear_code) { *sp++ = table[1][code]; if (code == table[0][code]) { return -1; } code = table[0][code]; } *sp++ = firstcode = table[1][code]; if ((code = max_code) < (1 << BITS)) { table[0][code] = oldcode; table[1][code] = firstcode; ++max_code; if ((max_code >= max_code_size) && (max_code_size < (1 << BITS))) { max_code_size *= 2; ++code_size; } } oldcode = incode; if (sp > stack) return *--sp; } return code; } deutex-5.2.3/src/merge.c000066400000000000000000000450641475516661100150760ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ #include "deutex.h" #include #include "tools.h" #include "endianm.h" #include "mkwad.h" #include "texture.h" #include "ident.h" #include "lists.h" #include "merge.h" #include /* ** old references ** */ static const int32_t HDRdirSz = 5 * sizeof(struct WADDIR); static struct WADDIR HDRdir[6]; /* ** Take some entries from a WAD */ static void HDRplunderWad(struct WADINFO *rwad, struct WADINFO *ewad) { char *data; int32_t wsize, sz = 0; int32_t ostart, osize; int16_t n; int32_t oldfilesz; /* ** calculate required size */ oldfilesz = WADRposition(rwad); /*old file size */ /* ** copy entries from WAD */ Phase("ME10", "Copying entries from wad (please wait)"); data = (char *) Malloc(MEMORYCACHE); for (n = 0; n < (rwad->ntry); n++) { ostart = rwad->dir[n].start; osize = rwad->dir[n].size; /*detect external entries */ if (ostart & EXTERNAL) { /*update new directory */ WADRalign4(rwad); rwad->dir[n].start = WADRposition(rwad); /*get entry size */ if (osize > 0) { /*copy old entry */ WADRseek(ewad, ostart & (~EXTERNAL)); for (wsize = 0; wsize < osize; wsize += sz) { sz = (osize - wsize > MEMORYCACHE) ? MEMORYCACHE : osize - wsize; WADRreadBytes(ewad, data, sz); if (WADRwriteBytes2(rwad, data, sz) < 0) { WADRchsize(rwad, oldfilesz); ProgError("ME13", "Not enough disk space"); break; } } } } } free(data); } /* ** Copy a WAD, and link to it's entries */ static int32_t HDRinsertWad(struct WADINFO *rwad, struct WADINFO *ewad, int32_t * pesize) { char *data; int32_t wsize, sz = 0; int32_t estart, esize; int16_t n; int32_t oldfilesz; /* ** calculate required size */ oldfilesz = WADRposition(rwad); /*old file size */ WADRalign4(rwad); estart = WADRposition(rwad); WADRseek(ewad, 0); esize = ewad->maxpos; Phase("ME16", "Inserting wad file into wad"); data = (char *) Malloc(MEMORYCACHE); for (wsize = 0; wsize < esize; wsize += sz) { sz = (esize - wsize > MEMORYCACHE) ? MEMORYCACHE : esize - wsize; WADRreadBytes(ewad, data, sz); if (WADRwriteBytes2(rwad, data, sz) < 0) { WADRchsize(rwad, oldfilesz); ProgError("ME19", "Not enough disk space"); break; } /*if((wsize&0xF000)==0) Phase("."); FIXME need /dev/tty output */ } free(data); for (n = 0; n < (rwad->ntry); n++) { /*detect external entries */ if ((rwad->dir[n].start) & EXTERNAL) { /*update new directory */ rwad->dir[n].start &= (~EXTERNAL); rwad->dir[n].start += estart; } } /* Phase("\n"); FIXME need /dev/tty output */ *pesize = esize; return estart; } void HDRrestoreWAD(const char *wadres) { struct WADINFO rwad; int32_t dirpos, ntry, n; int32_t rwadstart = 0, rwadsize = 0; int32_t ewadstart = 0, ewadsize = 0; static char ewadname[8]; static char ewadfile[40]; char *data; int32_t size = 0, wsize = 0, sz = 0; int32_t time; FILE *fp; bool Fail; Phase("ME22", "Attempting to restore wad %s", fname(wadres)); /*open DOOM.WAD */ rwad.ok = 0; WADRopenR(&rwad, wadres); /*get position of fake directory entry, reference to old dir */ dirpos = rwad.dirpos - HDRdirSz; WADRseek(&rwad, dirpos); WADRreadBytes(&rwad, (char *) HDRdir, HDRdirSz); Fail = false; if (peek_i32_le(&HDRdir[0].start) != 0x24061968L) Fail = true; if (peek_i32_le(&HDRdir[0].size) != 666L) Fail = true; if (strncmp(HDRdir[0].name, "IZNOGOOD", 8) != 0) Fail = true; if (Fail) { if ((n = WADRfindEntry(&rwad, "_DEUTEX_")) >= 0) if (rwad.dir[n].size >= HDRdirSz) { dirpos = rwad.dir[n].start; WADRseek(&rwad, dirpos); WADRreadBytes(&rwad, (char *) HDRdir, HDRdirSz); Fail = false; if (peek_i32_le(&HDRdir[0].start) != 0x24061968L) Fail = true; if (peek_i32_le(&HDRdir[0].size) != 666L) Fail = true; if (strncmp(HDRdir[0].name, "IZNOGOOD", 8) != 0) Fail = true; } } if (Fail) ProgError("ME25", "Not a modified WAD"); Phase("ME28", "Restoration infos seem correct"); dirpos = peek_i32_le(&HDRdir[1].start); ntry = peek_i32_le(&HDRdir[1].size); rwadstart = peek_i32_le(&HDRdir[2].start); rwadsize = peek_i32_le(&HDRdir[2].size); ewadstart = peek_i32_le(&HDRdir[3].start); ewadsize = peek_i32_le(&HDRdir[3].size); Normalise(ewadname, HDRdir[3].name); /*name of WAD inside */ /*original file time */ time = peek_i32_le(&HDRdir[4].size); if (peek_i32_le(&HDRdir[4].start)) { /*extract the PWAD */ sprintf(ewadfile, "%.8s.WAD", ewadname); ToLowerCase(ewadfile); fp = fopen(ewadfile, FOPEN_RB); if (fp != NULL) { fclose(fp); Info("ME31", "%s already exists, internal WAD discarded", fname(ewadfile)); } else { Phase("ME34", "Restoring internal wad %s", fname(ewadfile)); if ((fp = fopen(ewadfile, FOPEN_WB)) != NULL) { data = (char *) Malloc(MEMORYCACHE); size = ewadsize; WADRseek(&rwad, ewadstart); fseek(fp, 0, SEEK_SET); for (wsize = 0; wsize < size; wsize += sz) { sz = (size - wsize > MEMORYCACHE) ? MEMORYCACHE : size - wsize; WADRreadBytes(&rwad, data, sz); errno = 0; if (fwrite(data, (size_t) sz, 1, fp) != 1) { Warning("ME37", "%s: %s", fnameofs(ewadfile, (long) ewadstart), errno == 0 ? "write error" : strerror(errno)); break; } } free(data); fclose(fp); } else Warning("ME40", "%s: %s", fname(ewadfile), strerror(errno)); } } WADRopenA(&rwad, wadres); /*correct the directory reference of DOOM.WAD */ WADRsetDirRef(&rwad, ntry, dirpos); /*restore original size */ WADRchsize(&rwad, rwadstart + rwadsize); WADRclose(&rwad); Set_File_Time(wadres, time); Output("Restoration of %s should be successful\n", fname(wadres)); } static void HDRsetDir(struct WADINFO *rwad, bool IsIwad, bool Restore, int32_t time, int32_t dirpos, int32_t ntry, int32_t rsize, int32_t estart, int32_t esize, const char *wadext) { static char name[8]; int32_t pos; /*Set the old references */ Phase("ME43", "Writing new wad directory"); write_i32_le(&HDRdir[0].start, 0x24061968L); write_i32_le(&HDRdir[0].size, 666L); Normalise(HDRdir[0].name, "IZNOGOOD"); /*Set original WAD DIRECTORY */ write_i32_le(&HDRdir[1].start, dirpos); write_i32_le(&HDRdir[1].size, ntry); Normalise(HDRdir[1].name, IsIwad ? "DOOM_DIR" : "PWAD_DIR"); /*Store original WAD size and start */ write_i32_le(&HDRdir[2].start, 0); write_i32_le(&HDRdir[2].size, rsize); Normalise(HDRdir[2].name, "ORIGINAL"); /*Store external WAD size and start */ write_i32_le(&HDRdir[3].start, estart); write_i32_le(&HDRdir[3].size, esize); GetNameOfWAD(name, wadext); Normalise(HDRdir[3].name, name); /*old file time */ write_i32_le(&HDRdir[4].size, time); write_i32_le(&HDRdir[4].start, Restore); Normalise(HDRdir[4].name, "TIME"); /*save position of old ref if no previous _DEUTEX_ */ WADRalign4(rwad); pos = (int32_t) WADRfindEntry(rwad, "_DEUTEX_"); if (pos < 0) { pos = WADRposition(rwad); /*BC++ 4.5 bug */ WADRdirAddEntry(rwad, pos, HDRdirSz, "_DEUTEX_"); } /*write old refs */ WADRwriteBytes(rwad, (char *) HDRdir, HDRdirSz); /*write the directory */ rwad->dirpos = WADRposition(rwad); WADRwriteDir(rwad, 1); } /* ** merge WAD ** */ void PSTmergeWAD(const char *doomwad, const char *wadin, NTRYB select) { static struct WADINFO iwad; static struct WADINFO pwad; ENTRY *iiden; /*identify entry in IWAD */ ENTRY *piden; /*identify entry in PWAD */ int16_t pnm; char *Pnam; int32_t Pnamsz = 0; int32_t dirpos, ntry, isize, pstart, psize, time; struct WADDIR *NewDir; int32_t NewNtry; Phase("ME46", "Attempting to merge iwad %s and pwad %s", fname(doomwad), wadin); /*open iwad,get iwad directory */ iwad.ok = 0; WADRopenR(&iwad, doomwad); /*find PNAMES */ pnm = WADRfindEntry(&iwad, "PNAMES"); if (pnm < 0) ProgError("ME49", "Can't find PNAMES in iwad"); Pnam = WADRreadEntry(&iwad, pnm, &Pnamsz); /* identify iwad */ iiden = IDENTentriesIWAD(&iwad, Pnam, Pnamsz, true); /* get pwad directory, and identify */ pwad.ok = 0; WADRopenR(&pwad, wadin); piden = IDENTentriesPWAD(&pwad, Pnam, Pnamsz); free(Pnam); /*where to put pwad? at pwadstart */ /* merge the two directories */ NewDir = LISmergeDir(&NewNtry, false, true, select, &iwad, iiden, 0, &pwad, piden, EXTERNAL); /* prepare for append */ time = WADRprepareAppend(doomwad, &iwad, NewDir, NewNtry, &dirpos, &ntry, &isize); /* complete IWAD with PWAD, restorable */ pstart = HDRinsertWad(&iwad, &pwad, &psize); /* set directory */ HDRsetDir(&iwad, true, true, time, dirpos, ntry, isize, pstart, psize, wadin); /* close */ WADRclose(&pwad); WADRclose(&iwad); } /* ** -app complete a PWAD with DOOM entries ** */ void ADDappendSpriteFloor(const char *doomwad, const char *wadres, NTRYB select) { struct WADINFO iwad; struct WADINFO pwad; ENTRY *iiden; /*identify entry in IWAD */ ENTRY *piden; /*identify entry in PWAD */ int16_t pnm; char *Pnam; int32_t Pnamsz; int32_t dirpos, ntry, psize, time; struct WADDIR *NewDir; int32_t NewNtry; Phase("ME52", "Appending sprites and/or flats"); Phase("ME52", " from iwad %s", fname(doomwad)); Phase("ME52", " to pwad %s", fname(wadres)); /* get iwad directory, and identify */ iwad.ok = 0; WADRopenR(&iwad, doomwad); /*find PNAMES */ pnm = WADRfindEntry(&iwad, "PNAMES"); if (pnm < 0) ProgError("ME61", "Can't find PNAMES in iwad"); Pnam = WADRreadEntry(&iwad, pnm, &Pnamsz); /* identify iwad */ iiden = IDENTentriesIWAD(&iwad, Pnam, Pnamsz, true); /* get pwad directory, and identify */ pwad.ok = 0; WADRopenR(&pwad, wadres); piden = IDENTentriesPWAD(&pwad, Pnam, Pnamsz); free(Pnam); /* merge the two directories */ NewDir = LISmergeDir(&NewNtry, true, true, select, &iwad, iiden, EXTERNAL, &pwad, piden, 0); /* prepare for append */ time = WADRprepareAppend(wadres, &pwad, NewDir, NewNtry, &dirpos, &ntry, &psize); /* append DOOM sprites to PWAD */ HDRplunderWad(&pwad, &iwad); /* set dir */ HDRsetDir(&pwad, false, false, time, dirpos, ntry, psize, -1, -1, doomwad); /* close */ WADRclose(&iwad); WADRclose(&pwad); } /* ** join: complete a PWAD with another PWAD entries ** */ void ADDjoinWads(const char *doomwad, const char *wadres, const char *wadext, NTRYB select) { struct WADINFO iwad; /*IWAD*/ struct WADINFO ewad; /*external Wad */ struct WADINFO rwad; ENTRY *eiden; /*identify entry in IWAD */ ENTRY *riden; /*identify entry in PWAD */ int16_t entry; char *Entry; int32_t EntrySz; int16_t pnm; char *Patch; int32_t PatchSz; int32_t start, size; int16_t etexu, rtexu; struct WADDIR *NewDir; int32_t NewNtry; bool TexuMrg = false; int32_t dirpos, ntry, rsize, estart, esize, time; Phase("ME64", "Merging pwad %s", fname(wadext)); Phase("ME64", " into pwad %s", fname(wadres)); /* get iwad directory, and identify */ iwad.ok = 0; WADRopenR(&iwad, doomwad); /*find PNAMES */ entry = WADRfindEntry(&iwad, "PNAMES"); if (entry < 0) ProgError("ME70", "Can't find PNAMES in iwad"); Entry = WADRreadEntry(&iwad, entry, &EntrySz); /* get ewad directory, and identify */ ewad.ok = 0; WADRopenR(&ewad, wadext); eiden = IDENTentriesPWAD(&ewad, Entry, EntrySz); /* get rwad directory, and identify */ rwad.ok = 0; WADRopenR(&rwad, wadres); riden = IDENTentriesPWAD(&rwad, Entry, EntrySz); free(Entry); /*merge texture1 if needed */ etexu = WADRfindEntry(&ewad, "TEXTURE1"); rtexu = WADRfindEntry(&rwad, "TEXTURE1"); if ((etexu >= 0) && (rtexu >= 0)) { TexuMrg = true; iwad.ok = 0; WADRopenR(&iwad, doomwad); /*find PNAMES in IWAD and init */ pnm = WADRfindEntry(&iwad, "PNAMES"); if (pnm < 0) Bug("ME73", "JnPnm"); Entry = WADRreadEntry(&iwad, entry, &EntrySz); PNMinit(Entry, EntrySz); free(Entry); /*declare TEXTURE1 from IWAD */ TXUinit(); } WADRclose(&iwad); if (TexuMrg) { /*add TEXTURE1 from rwad */ Phase("ME76", " With TEXTURE1 from %s", fname(wadres)); PatchSz = 0; Patch = NULL; pnm = WADRfindEntry(&rwad, "PNAMES"); if (pnm >= 0) { Phase("ME79", " Declaring patches from %s", fname(wadres)); riden[pnm] = EVOID; Patch = WADRreadEntry(&rwad, pnm, &PatchSz); } Entry = WADRreadEntry(&rwad, rtexu, &EntrySz); TXUreadTEXTURE(rwad.dir[pnm].name, Entry, EntrySz, Patch, PatchSz, true); if (PatchSz != 0) free(Patch); free(Entry); riden[rtexu] = EVOID; /* forget r texu */ /*TEXTURE1 from ewad */ Phase("ME82", " And TEXTURE1 from %s", fname(wadext)); PatchSz = 0; Patch = NULL; pnm = WADRfindEntry(&ewad, "PNAMES"); if (pnm >= 0) { Phase("ME85", " Declaring Patches from %s", fname(wadext)); eiden[pnm] = EVOID; Patch = WADRreadEntry(&ewad, pnm, &PatchSz); } Entry = WADRreadEntry(&ewad, etexu, &EntrySz); TXUreadTEXTURE(ewad.dir[pnm].name, Entry, EntrySz, Patch, PatchSz, false); if (PatchSz != 0) free(Patch); free(Entry); eiden[etexu] = EVOID; /* forget e texu */ } /* merge the two directories, all entries */ NewDir = LISmergeDir(&NewNtry, false, false, select, &rwad, riden, 0, &ewad, eiden, EXTERNAL); /* prepare for append */ time = WADRprepareAppend(wadres, &rwad, NewDir, NewNtry, &dirpos, &ntry, &rsize); /* append PWAD into PWAD, restorable */ estart = HDRinsertWad(&rwad, &ewad, &esize); /* append texu/pname */ if (TexuMrg) { WADRalign4(&rwad); start = WADRposition(&rwad); size = TXUwriteTEXTUREtoWAD(&rwad); WADRdirAddEntry(&rwad, start, size, "TEXTURE1"); TXUfree(); WADRalign4(&rwad); start = WADRposition(&rwad); size = PNMwritePNAMEtoWAD(&rwad); WADRdirAddEntry(&rwad, start, size, "PNAMES"); PNMfree(); } /* set directory */ HDRsetDir(&rwad, false, true, time, dirpos, ntry, rsize, estart, esize, wadext); /*end */ WADRclose(&rwad); WADRclose(&ewad); } /* ** Add sprites and Floors ** ** must delete void entries (old DMADDS files) ** must select SPRITES of FLOORS ** */ void ADDallSpriteFloor(const char *wadout, const char *doomwad, const char *wadres, NTRYB select) { struct WADINFO iwad; struct WADINFO pwad; struct WADINFO rwad; int16_t n; ENTRY *iiden; /*identify entry in IWAD */ ENTRY *piden; /*identify entry in PWAD */ int32_t start, size, ostart, osize; int16_t pnm; char *Pnam; int32_t Pnamsz; struct WADDIR *NewDir; int32_t NewNtry; Phase("ME88", "Copying sprites and/or flats"); Phase("ME88", " from iwad %s", fname(doomwad)); Phase("ME88", " and pwad %s", fname(wadres)); Phase("ME88", " into pwad %s", fname(wadout)); /* get iwad directory, and identify */ iwad.ok = 0; WADRopenR(&iwad, doomwad); /*find PNAMES */ pnm = WADRfindEntry(&iwad, "PNAMES"); if (pnm < 0) ProgError("ME91", "Can't find PNAMES in main WAD"); Pnam = WADRreadEntry(&iwad, pnm, &Pnamsz); /* identify iwad */ iiden = IDENTentriesIWAD(&iwad, Pnam, Pnamsz, true); /* get pwad directory, and identify */ pwad.ok = 0; WADRopenR(&pwad, wadres); piden = IDENTentriesPWAD(&pwad, Pnam, Pnamsz); free(Pnam); /*where to put pwad? at pwadstart */ if ((iwad.maxpos | pwad.maxpos) & EXTERNAL) Bug("ME94", "AddExt"); /* merge the two directories */ NewDir = LISmergeDir(&NewNtry, true, true, select, &iwad, iiden, EXTERNAL, &pwad, piden, 0); /* create a new PWAD */ rwad.ok = 0; WADRopenW(&rwad, wadout, PWAD, 1); for (n = 0; n < NewNtry; n++) { ostart = NewDir[n].start; osize = NewDir[n].size; WADRalign4(&rwad); start = WADRposition(&rwad); if (ostart & EXTERNAL) size = WADRwriteWADbytes(&rwad, &iwad, ostart & (~EXTERNAL), osize); else size = WADRwriteWADbytes(&rwad, &pwad, ostart, osize); WADRdirAddEntry(&rwad, start, size, NewDir[n].name); } free(NewDir); /*close files memory */ WADRclose(&iwad); WADRclose(&pwad); WADRwriteDir(&rwad, 1); WADRclose(&rwad); Phase("ME98", "Addition of sprites and floors is complete"); } deutex-5.2.3/src/merge.h000066400000000000000000000016331475516661100150750ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ /*merge a WAD into an IWAD*/ void PSTmergeWAD(const char *doomwad, const char *wadin, NTRYB select); /*put all sprites in another WAD, like DMADDS*/ void ADDallSpriteFloor(const char *wadout, const char *doomwad, const char *wadin, NTRYB select); /*append all sprites*/ void ADDappendSpriteFloor(const char *doomwad, const char *wadin, NTRYB select); /*join two WADs, including textures and pnames*/ void ADDjoinWads(const char *doomwad, const char *wadres, const char *wadext, NTRYB select); /*restore a modified WAD*/ void HDRrestoreWAD(const char *wadres); deutex-5.2.3/src/mkwad.c000066400000000000000000000620221475516661100150730ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ #include "deutex.h" #include #include #include #include #include #include "tools.h" #include "mkwad.h" #include "ident.h" #include "wadio.h" /* ** Open PWAD file. *pcount=global byte counter ** leaves number of entries and directory pointer NULL */ const int WADR_READ = 1; const int WADR_WRITE = 2; const int WADR_RDWR = 3; const int WADR_PIPO = 8; void WADRopenPipo(struct WADINFO *info, int32_t ntry) { /*directory */ if ((info->ok & WADR_RDWR)) Bug("WR80", "WadPpk"); info->ok = WADR_PIPO; if (ntry <= 0) Bug("WR81", "WadPpo"); info->maxdir = ntry; info->dir = (struct WADDIR *) Malloc((info->maxdir) * sizeof(struct WADDIR)); info->maxpos = ntry * sizeof(struct WADDIR); info->ntry = 0; info->wposit = info->maxpos; } struct WADDIR *WADRclosePipo(struct WADINFO *info, int32_t * ntry) { if ((info->ok != WADR_PIPO)) Bug("WR84", "WadPpc"); info->ok = false; if (info->ntry < 0) info->ntry = 0; info->dir = (struct WADDIR *) Realloc(info->dir, (info->ntry) * sizeof(struct WADDIR)); *ntry = info->ntry; return info->dir; } int32_t WADRdirAddPipo(struct WADINFO * info, int32_t start, int32_t size, const char *entry) { int16_t n; if (info->ok != WADR_PIPO) Bug("WR82", "WadDaP"); n = (int16_t) info->ntry; /*position of new entry */ if (n < 0) Bug("WR83", "WadDa2"); if (n < info->maxdir) { /*can add to the dir */ info->ntry++; /*new dir size */ info->dir[n].size = size; info->dir[n].start = start; Normalise(info->dir[n].name, entry); } return n; /* nb entries */ } void WADRopenR(struct WADINFO *info, const char *wadin) { /*directory */ int32_t ntry, dirpos; long filesize = 0x10000000L; int16_t n; struct WADDIR dir; if ((info->ok & WADR_RDWR)) Bug("WR01", "%s: WadOpr", fname(wadin)); info->fd = fopen(wadin, FOPEN_RB); if ((info->fd) == NULL) ProgError("WR03", "%s: %s", fname(wadin), strerror(errno)); info->filename = Malloc(strlen(wadin) + 1); strcpy(info->filename, wadin); info->ok = WADR_READ; if (fseek(info->fd, 0, SEEK_END) == 0) { filesize = ftell(info->fd); fseek(info->fd, 0, SEEK_SET); } /*signature */ switch (WADRreadShort(info)) { case PWAD: case IWAD: break; default: ProgError("WR05", "%s: invalid wad magic", fname(wadin)); } if (WADRreadShort(info) != WADMAGIC) ProgError("WR07", "%s: read error in header", fname(wadin)); /*start of directory */ ntry = WADRreadLong(info); if (ntry <= 0) ProgError("WR09", "%s: zero entries", fname(wadin)); if (ntry > 0xfce) Warning("WR11", "%s has more than 4046 lumps: vanilla-incompatible", fname(wadin)); info->dirpos = dirpos = WADRreadLong(info); if ((dirpos < 0) || (dirpos >= filesize)) ProgError("WR13", "%s: invalid directory offset %08lX", fname(wadin), (unsigned long) dirpos); /*allocate directory */ info->maxdir = ntry; info->dir = (struct WADDIR *) Malloc((info->maxdir) * sizeof(struct WADDIR)); /*read directory, calculate further byte in wad */ info->maxpos = dirpos + (ntry * sizeof(struct WADDIR)); WADRseek(info, dirpos); info->ntry = 0; for (n = 0; n < ntry; n++) { if (wad_read_i32(info->fd, &dir.start) || wad_read_i32(info->fd, &dir.size) || wad_read_name(info->fd, dir.name)) ProgError("WR15", "%s: read error in directory", fname(wadin)); WADRdirAddEntry(info, dir.start, dir.size, dir.name); } if (info->ntry != ntry) Bug("WR17", "WadOrN %ld %ld", (long) ntry, (long) info->ntry); info->wposit = info->maxpos; Phase("WR19", "Reading WAD %s:\t(%ld entries)", fname(wadin), (long) ntry); } void WADRopenW(struct WADINFO *info, const char *wadout, WADTYPE type, int verbose) { if (verbose) Phase("WW01", "Creating %s %s", (type == IWAD) ? "iwad" : "pwad", fname(wadout)); if ((info->ok & WADR_RDWR)) Bug("WW02", "%s: WadOpW", fname(wadout)); /*check file */ if (clobber == CLOBBER_NO) { info->fd = fopen(wadout, FOPEN_RB); if (info->fd != NULL) ProgError("WW03", "Won't overwrite existing file %s", fname(wadout)); } /*open file */ info->fd = fopen(wadout, FOPEN_WB); if (info->fd == NULL) ProgError("WW04", "%s: %s", fname(wadout), strerror(errno)); info->filename = Malloc(strlen(wadout) + 1); strcpy(info->filename, wadout); info->ok = WADR_WRITE; info->wposit = 0; info->ntry = 0; info->maxdir = MAXPWADDIR; info->dir = (struct WADDIR *) Malloc((info->maxdir) * sizeof(struct WADDIR)); WADRwriteShort(info, type); /* WAD type: PW or IW */ WADRwriteShort(info, WADMAGIC); /* WAD type: AD */ /* will be fixed when closing the WAD */ WADRwriteLong(info, -1); /* no counter of dir entries */ WADRwriteLong(info, -1); /* no dir pointer */ WADRalign4(info); } /* ** Assumes file already opened write ** if not, open it first ** OPEN READ-WRITE,not APPEND ** because APPEND can't FSEEK to start of file */ void WADRopenA(struct WADINFO *info, const char *wadinout) { Phase("WW10", "Modifying wad %s", fname(wadinout)); if ((info->ok & WADR_WRITE)) Bug("WW11", "%s: WadOpA", fname(wadinout)); if (!(info->ok & WADR_READ)) { WADRopenR(info, wadinout); } /*reopen for append */ fclose(info->fd); info->fd = fopen(wadinout, FOPEN_RBP); /*rb+ = read/write binary */ if (info->fd == NULL) ProgError("WW12", "%s: %s", fname(wadinout), strerror(errno)); info->filename = Malloc(strlen(wadinout) + 1); strcpy(info->filename, wadinout); info->ok = WADR_RDWR; WADRseek(info, info->wposit); } /* ** Add a new entry in the directory ** increase ntry, redim dir ** update maxdir and maxpos ** returns entry ref */ int32_t WADRdirAddEntry(struct WADINFO *info, int32_t start, int32_t size, const char *entry) { int16_t n; int32_t sz; if (!(info->ok & (WADR_RDWR))) Bug("WW40", "%s: WadDAE", fname(info->filename)); n = (int16_t) info->ntry; /*position of new entry */ if (n >= info->maxdir) { /*shall we move the dir? */ info->maxdir += MAXPWADDIR; info->dir = (struct WADDIR *) Realloc((char *) info->dir, (info->maxdir) * sizeof(struct WADDIR)); } info->ntry++; /*new dir size */ info->dir[n].size = size; info->dir[n].start = start; Normalise(info->dir[n].name, entry); sz = start + size; if (sz > info->maxpos) info->maxpos = sz; return n; /* nb entries */ } /* ** write the directory (names, counts, lengths) ** then update the number of entries and dir pointer */ void WADRwriteDir(struct WADINFO *info, int verbose) { int16_t n; if (!(info->ok & WADR_WRITE)) Bug("WW20", "%s: WadWD", fname(info->filename)); WADRalign4(info); /*align entry on int32_t word */ info->dirpos = info->wposit; /*current position */ /* write the new WAD directory */ for (n = 0; n < info->ntry; n++) { if (wad_write_i32(info->fd, info->dir[n].start) || wad_write_i32(info->fd, info->dir[n].size) || wad_write_name(info->fd, info->dir[n].name)) ProgError("WW22", "%s: write error in directory", fname(info->filename)); } /* fix up the directory start information */ WADRsetDirRef(info, info->ntry, info->dirpos); n = (int16_t) (info->dirpos + (sizeof(struct WADDIR) * info->ntry)); if (n > info->maxpos) info->maxpos = n; if (verbose) Phase("WW28", "%s: wad is complete, %ld entries, %ld bytes", fname(info->filename), (long) info->ntry, (long) info->wposit); } void WADRsetDirRef(struct WADINFO *info, int32_t ntry, int32_t dirpos) { if (!(info->ok & WADR_WRITE)) Bug("WW24", "%s: WadSDR", fname(info->filename)); WADRseek(info, 4); if (wad_write_i32(info->fd, ntry) || wad_write_i32(info->fd, dirpos)) ProgError("WW26", "%s: write error while fixing up the header", fname(info->filename)); WADRseek(info, info->wposit); info->ntry = ntry; info->dirpos = dirpos; } void WADRchsize(struct WADINFO *info, int32_t fsize) { if (!(info->ok & WADR_WRITE)) Bug("WW93", "WadcSz"); if (ftruncate(fileno(info->fd), fsize) != 0) ProgError("WW95", "%s: %s", fname(info->filename), strerror(errno)); /*@ AEO while trying the change the size of the wad */ info->maxpos = fsize; info->wposit = fsize; } void WADRseek(struct WADINFO *info, int32_t position) { long ofs; if (!(info->ok & WADR_RDWR)) Bug("WR31", "WadSk"); ofs = ftell(info->fd); if (position > info->maxpos) Bug("WR33", "WadSk>"); if (fseek(info->fd, position, SEEK_SET)) ProgError("WR35", "%s: Can't seek to %06lXh (%s)", fnameofs(info->filename, ofs), (unsigned long) position, strerror(errno)); } /* * WADRreadBytes2 * Attempt to read up to bytes from wad . * Return the actual number of bytes read. */ iolen_t WADRreadBytes2(struct WADINFO *info, char *buffer, iolen_t nbytes) { size_t attempt = MEMORYCACHE; iolen_t bytes_read = 0; if (!(info->ok & WADR_READ)) Bug("WR41", "WadRdB"); while (nbytes > 0) { size_t result; if (attempt > nbytes) attempt = nbytes; result = fread(buffer, 1, attempt, info->fd); bytes_read += result; if (result == 0) /* Hit EOF */ break; buffer += result; nbytes -= result; } return bytes_read; } /* * WADRreadBytes * Attempt to read bytes from wad . If EOF * is hit too soon, trigger a fatal error. Else, return * . */ iolen_t WADRreadBytes(struct WADINFO * info, char *buffer, iolen_t nbytes) { long ofs = ftell(info->fd); iolen_t result = WADRreadBytes2(info, buffer, nbytes); if (result != nbytes) { if (ferror(info->fd)) { ProgError("WR43", "%s: read error (got %lu/%lu bytes)", fnameofs(info->filename, ofs), (unsigned long) result, (unsigned long) nbytes); } else { ProgError("WR45", "%s: unexpected EOF (got %lu/%lu bytes)", fnameofs(info->filename, ofs), (unsigned long) result, (unsigned long) nbytes); } } return nbytes; } int16_t WADRreadShort(struct WADINFO * info) { int16_t res; long ofs = ftell(info->fd); if (!(info->ok & WADR_READ)) Bug("WR51", "WadRdS"); if (wad_read_i16(info->fd, &res)) { if (ferror(info->fd)) { ProgError("WR53", "%s: read error", fnameofs(info->filename, ofs)); } else { ProgError("WR55", "%s: unexpected EOF", fnameofs(info->filename, ofs)); } } return res; } int32_t WADRreadLong(struct WADINFO * info) { int32_t res; long ofs = ftell(info->fd); if (!(info->ok & WADR_READ)) Bug("WR61", "WadRdL"); if (wad_read_i32(info->fd, &res)) { if (ferror(info->fd)) { ProgError("WR63", "%s: read error", fnameofs(info->filename, ofs)); } else { ProgError("WR65", "%s: unexpected EOF", fnameofs(info->filename, ofs)); } } return res; } void WADRclose(struct WADINFO *info) { if (!(info->ok & WADR_RDWR)) Bug("WR97", "WadClo"); info->ok = false; free(info->filename); free(info->dir); fclose(info->fd); } int16_t WADRfindEntry(struct WADINFO *info, const char *entry) { int16_t i; static char name[8]; struct WADDIR *dir; if (!(info->ok & WADR_RDWR)) Bug("WR91", "WadFE"); for (i = 0, dir = info->dir; i < info->ntry; i++, dir += 1) { Normalise(name, dir->name); if (strncmp(name, entry, 8) == 0) { return i; } } return -1; } /* ** load data in buffer */ char *WADRreadEntry(struct WADINFO *info, int16_t n, int32_t * psize) { char *buffer; int32_t start, size; if (!(info->ok & WADR_READ)) Bug("WR71", "WadRE"); if (n >= (info->ntry)) Bug("WR73", "WadRE>"); start = info->dir[n].start; size = info->dir[n].size; buffer = (char *) Malloc(size); WADRseek(info, start); WADRreadBytes(info, buffer, size); *psize = size; return buffer; } /* * WADRreadEntry2 * Read at most the *psize first bytes of a lump. * If the lump is shorter than *psize, it's _not_ an error. * Return the actual number of bytes read in *psize. */ char *WADRreadEntry2(struct WADINFO *info, int16_t n, int32_t * psize) { char *buffer; long start; iolen_t size; iolen_t actual_size; if (!(info->ok & WADR_READ)) Bug("WR75", "WadRE"); if (n >= info->ntry) Bug("WR76", "WadRE>"); start = info->dir[n].start; size = *psize < info->dir[n].size ? *psize : info->dir[n].size; buffer = Malloc(size); WADRseek(info, start); actual_size = WADRreadBytes2(info, buffer, size); if (actual_size < size) { if (ferror(info->fd)) { ProgError("WR78", "%s: Lump %s: read error at byte %ld", fnameofs(info->filename, start + actual_size), lump_name(info->dir[n].name), (long) actual_size); } else { ProgError("WR79", "%s: Lump %s: unexpected EOF at byte %ld", fnameofs(info->filename, start + actual_size), lump_name(info->dir[n].name), (long) actual_size); } } *psize = actual_size; return buffer; } /* ** copy data from WAD to file */ void WADRsaveEntry(struct WADINFO *info, int16_t n, const char *file) { int32_t wsize, sz = 0; char *buffer; int32_t start, size; FILE *fd; if (!(info->ok & WADR_READ)) Bug("WR86", "WadSE"); if (n >= (info->ntry)) Bug("WR87", "WadSE>"); start = info->dir[n].start; size = info->dir[n].size; fd = fopen(file, FOPEN_WB); if (fd == NULL) ProgError("WR88", "%s: Can't open for writing (%s)", fname(file), strerror(errno)); buffer = (char *) Malloc(MEMORYCACHE); WADRseek(info, start); for (wsize = 0; wsize < size; wsize += sz) { sz = (size - wsize > MEMORYCACHE) ? MEMORYCACHE : size - wsize; WADRreadBytes(info, buffer, sz); if (fwrite(buffer, (size_t) sz, 1, fd) != 1) { free(buffer); ProgError("WR89", "%s: write error", fname(file)); } } /*declare in WAD directory */ free(buffer); if (fclose(fd)) ProgError("WR90", "%s: %s", fname(file), strerror(errno)); } void WADRsetLong(struct WADINFO *info, int32_t pos, int32_t val) { if (!(info->ok & WADR_WRITE)) Bug("WW56", "WadStL"); if (pos > (info->maxpos)) Bug("WW57", "WadSL>"); if (fseek(info->fd, pos, SEEK_SET)) ProgError("WW58", "%s: Can't seek to %06lXh (%s)", fname(info->filename), (unsigned long) pos, strerror(errno)); if (wad_write_i32(info->fd, val)) ProgError("WW59", "%s: write error", fnameofs(info->filename, pos)); } void WADRsetShort(struct WADINFO *info, int32_t pos, int16_t val) { if (!(info->ok & WADR_WRITE)) Bug("WW51", "WadStS"); if (pos > (info->maxpos)) Bug("WW52", "WadSS>"); if (fseek(info->fd, pos, SEEK_SET)) ProgError("WW53", "%s: Can't seek to %06lXh (%s)", fname(info->filename), (unsigned long) pos, strerror(errno)); if (wad_write_i16(info->fd, val)) ProgError("WW54", "%s: write error", fnameofs(info->filename, pos)); } /* ** internal functions ** */ static void WADRcheckWritePos(struct WADINFO *info) { if (!(info->ok & WADR_WRITE)) Bug("WW61", "WadCkW"); if (fseek(info->fd, info->wposit, SEEK_SET)) ProgError("WW63", "%s: Can't seek to %06lXh (%s)", fnameofs(info->filename, ftell(info->fd)), info->wposit, strerror(errno)); } static int32_t WADRwriteBlock(struct WADINFO *info, char *data, int32_t sz) { if (fwrite(data, (size_t) sz, 1, info->fd) != 1) ProgError("WW65", "%s: write error", fname(info->filename)); info->wposit += sz; if (info->maxpos < info->wposit) info->maxpos = info->wposit; return sz; } /* ** align, give position */ void WADRalign4(struct WADINFO *info) { int16_t remain; static char buffer[] = { 0, 0x24, 0x6, 0x68 }; WADRcheckWritePos(info); remain = (int16_t) (info->wposit & 0x03); /*0 to 3 */ if (remain > 0) WADRwriteBytes(info, buffer, 4 - remain); } /*must be equal to ftell*/ int32_t WADRposition(struct WADINFO *info) { WADRcheckWritePos(info); return info->wposit; } /* ** write */ int32_t WADRwriteLong(struct WADINFO * info, int32_t val) { WADRcheckWritePos(info); if (wad_write_i32(info->fd, val)) ProgError("WW67", "%s: write error", fname(info->filename)); info->wposit += sizeof val; if (info->maxpos < info->wposit) info->maxpos = info->wposit; return sizeof val; } int32_t WADRwriteShort(struct WADINFO *info, int16_t val) { WADRcheckWritePos(info); if (wad_write_i16(info->fd, val)) ProgError("WW69", "%s: write error", fname(info->filename)); info->wposit += sizeof val; if (info->maxpos < info->wposit) info->maxpos = info->wposit; return sizeof val; } int32_t WADRwriteBytes(struct WADINFO *info, char *data, int32_t size) { int32_t wsize, sz = 0; WADRcheckWritePos(info); if (size <= 0) Bug("WW71", "WadWb<"); for (wsize = 0; wsize < size;) { sz = (size - wsize > MEMORYCACHE) ? MEMORYCACHE : size - wsize; wsize += WADRwriteBlock(info, &data[wsize], sz); } return wsize; } static int32_t WADRwriteBlock2(struct WADINFO *info, char *data, int32_t sz) { if (fwrite(data, (size_t) sz, 1, info->fd) != 1) return -1; info->wposit += sz; if (info->maxpos < info->wposit) info->maxpos = info->wposit; return sz; } int32_t WADRwriteBytes2(struct WADINFO * info, char *data, int32_t size) { int32_t wsize, sz = 0; WADRcheckWritePos(info); if (size <= 0) Bug("WW73", "WadWb<"); for (wsize = 0; wsize < size;) { sz = (size - wsize > MEMORYCACHE) ? MEMORYCACHE : size - wsize; sz = WADRwriteBlock2(info, &data[wsize], sz); if (sz < 0) return -1; wsize += sz; } return wsize; } /* ** copy data from SOURCE WAD to WAD */ int32_t WADRwriteWADbytes(struct WADINFO * info, struct WADINFO * src, int32_t start, int32_t size) { int32_t wsize, sz = 0; char *data; data = (char *) Malloc(MEMORYCACHE); WADRseek(src, start); WADRcheckWritePos(info); for (wsize = 0; wsize < size;) { sz = (size - wsize > MEMORYCACHE) ? MEMORYCACHE : size - wsize; WADRreadBytes(src, data, sz); wsize += WADRwriteBlock(info, data, sz); } /*declare in WAD directory */ free(data); return wsize; } /* ** copy lump from file into WAD ** returns size */ int32_t WADRwriteLump(struct WADINFO * info, const char *file) { int32_t size, sz = 0; FILE *fd; char *data; WADRcheckWritePos(info); /*Look for entry in master directory */ fd = fopen(file, FOPEN_RB); if (fd == NULL) ProgError("WW75", "%s: %s", fname(file), strerror(errno)); data = (char *) Malloc(MEMORYCACHE); for (size = 0;;) { sz = fread(data, 1, (size_t) MEMORYCACHE, fd); if (sz <= 0) break; size += WADRwriteBlock(info, data, sz); } free(data); fclose(fd); return size; } int32_t WADRwriteWADentry(struct WADINFO * info, struct WADINFO * src, int16_t n) { if (n > (src->ntry)) Bug("WW77", "WadWW>"); return WADRwriteWADbytes(info, src, src->dir[n].start, src->dir[n].size); } /* ** copy level parts */ void WADRwriteWADlevelParts(struct WADINFO *info, struct WADINFO *src, int16_t N, size_t nlumps) { int32_t start, size; int16_t n; for (n = N + 1; n < src->ntry && n < N + nlumps; n++) { WADRalign4(info); start = WADRposition(info); size = WADRwriteWADentry(info, src, n); WADRdirAddEntry(info, start, size, src->dir[n].name); } } /* ** copy level from WAD ** try to match level name (multi-level) ** if level name not found, then take the first level... */ void WADRwriteWADlevel(struct WADINFO *info, const char *file, const char *level) { int16_t N, l; int32_t pos; /*char Level[8]; */ struct WADINFO src; if (IDENTlevel(level) < 0) ProgError("WW79", "Bad level name %s", level); src.ok = 0; WADRopenR(&src, file); /*search for entry in level */ N = WADRfindEntry(&src, level); if (N < 0) { /* no? then search first level */ for (N = 0;; N++) { l = IDENTlevel(src.dir[N].name); if (l >= 0) break; if (N >= src.ntry) ProgError("WW81", "No level in WAD %s", file); } } /*set level name */ WADRalign4(info); pos = WADRposition(info); /*BC++ 4.5 bug! */ WADRdirAddEntry(info, pos, 0L, level); /* 9999 is a way of saying "copy all the lumps". The rationale is "let's assume the user knows what he/she is doing. If he/she wants us to include a 100-lump level, let's do it". On the other hand, this stance is in contradiction with using WADRfindEntry() (see above). This needs to be fixed. There are two choices : - make this function discriminating and prevent experimentation - make it dumb but allow one to put multi-level wads in levels/. My real motivation for doing things the way I did was that I didn't want to copy-paste from IDENTdirLevels() into WADRwriteWADlevelParts() (these things should be at a single place). */ WADRwriteWADlevelParts(info, &src, N, 9999); WADRclose(&src); } /* ** replace dir of rwad by dir of newwad ** prepare to write at the end of rwad ** open for append */ int32_t WADRprepareAppend(const char *wadres, struct WADINFO *rwad, struct WADDIR *NewDir, int32_t NewNtry, int32_t * dirpos, int32_t * ntry, int32_t * size) { int32_t ewadstart; int32_t rwadsize; int32_t time; time = Get_File_Time(wadres); /* append to the Result WAD */ WADRopenA(rwad, wadres); /*get original size */ rwadsize = rwad->maxpos; /*last warning */ Output ("The WAD file %s will be modified, but it can be restored with:\n", wadres); Output("%s -res %s\n", PACKAGE, wadres); Output ("Restoration may fail if you modified the WAD with another tool.\n"); Output("In case of failure, you can salvage your WAD by:\n"); /* Assuming wad is little endian... */ Output("- setting bytes 4-7 to \t%02Xh %02Xh %02Xh %02Xh\n", (unsigned short) (rwad->ntry & 0xff), (unsigned short) ((rwad->ntry >> 8) & 0xff), (unsigned short) ((rwad->ntry >> 16) & 0xff), (unsigned short) ((rwad->ntry >> 24) & 0xff)); Output("- and setting bytes 8-11 to \t%02Xh %02Xh %02Xh %02Xh\n", (unsigned short) (rwad->dirpos & 0xff), (unsigned short) ((rwad->dirpos >> 8) & 0xff), (unsigned short) ((rwad->dirpos >> 16) & 0xff), (unsigned short) ((rwad->dirpos >> 24) & 0xff)); Output("If possible, set the file size to %ld bytes.\n", rwadsize); /*align */ ewadstart = ((rwadsize + 0xF) & (~0xFL)); if ((ewadstart | rwadsize) & EXTERNAL) ProgError("WW91", "Too big WADs"); *dirpos = rwad->dirpos; *ntry = rwad->ntry; *size = rwadsize; /*Change size */ WADRchsize(rwad, ewadstart); /*Write will start at file end */ rwad->maxpos = ewadstart; rwad->wposit = ewadstart; WADRseek(rwad, ewadstart); /*Change to New directory */ free(rwad->dir); rwad->dir = NewDir; rwad->ntry = NewNtry; rwad->maxdir = NewNtry; rwad->dirpos = -1; return time; } deutex-5.2.3/src/mkwad.h000066400000000000000000000072401475516661100151010ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ /*for merging directories*/ void WADRopenPipo(struct WADINFO *info, int32_t ntry); struct WADDIR *WADRclosePipo(struct WADINFO *info, int32_t * ntry); int32_t WADRdirAddPipo(struct WADINFO *info, int32_t start, int32_t size, const char *entry); /*Open a WAD file for read*/ void WADRopenR(struct WADINFO *info, const char *wadin); /*Open a WAD file for write*/ void WADRopenW(struct WADINFO *info, const char *wadout, WADTYPE type, int verbose); /*Open a WAD file for append*/ void WADRopenA(struct WADINFO *info, const char *wadinout); /*Close a WAD file*/ void WADRclose(struct WADINFO *info); /*WAD file structure*/ /*set position of internal WAD directory*/ void WADRsetDirRef(struct WADINFO *info, int32_t ntry, int32_t dirpos); /*change size of a WAD*/ void WADRchsize(struct WADINFO *info, int32_t fsize); /*increase size of WAD, do not update position*/ bool WADRchsize2(struct WADINFO *info, int32_t fsize); /*composition of internal WAD directory*/ /*add an entry to the directory*/ int32_t WADRdirAddEntry(struct WADINFO *info, int32_t start, int32_t size, const char *name); /*write the directory (and set it's position)*/ void WADRwriteDir(struct WADINFO *info, int verbose); /*find an entry in the directory*/ int16_t WADRfindEntry(struct WADINFO *info, const char *entry); /*-1 or index of entry in directory*/ /*set data in a WAD (write position doesn't change)*/ void WADRsetLong(struct WADINFO *info, int32_t pos, int32_t val); void WADRsetShort(struct WADINFO *info, int32_t pos, int16_t val); /*align on long*/ void WADRalign4(struct WADINFO *info); /*align on long word, for next entry */ /*tell position of pointer*/ int32_t WADRposition(struct WADINFO *info); /*current position */ /*write date (write position increase)*/ int32_t WADRwriteLong(struct WADINFO *info, int32_t val); int32_t WADRwriteShort(struct WADINFO *info, int16_t val); int32_t WADRwriteBytes(struct WADINFO *info, char *data, int32_t size); int32_t WADRwriteBytes2(struct WADINFO *info, char *data, int32_t size); int32_t WADRwriteLump(struct WADINFO *info, const char *file); int32_t WADRwriteWADbytes(struct WADINFO *info, struct WADINFO *src, int32_t start, int32_t size); int32_t WADRwriteWADentry(struct WADINFO *info, struct WADINFO *src, int16_t n); void WADRwriteWADlevelParts(struct WADINFO *info, struct WADINFO *src, int16_t n, size_t nlumps); void WADRwriteWADlevel(struct WADINFO *info, const char *file, const char *level); /*read data*/ void WADRseek(struct WADINFO *info, int32_t position); iolen_t WADRreadBytes(struct WADINFO *info, char *buffer, iolen_t nbytes); iolen_t WADRreadBytes(struct WADINFO *info, char *buffer, iolen_t nbytes); int16_t WADRreadShort(struct WADINFO *info); int32_t WADRreadLong(struct WADINFO *info); char *WADRreadEntry(struct WADINFO *info, int16_t N, int32_t * psize); char *WADRreadEntry2(struct WADINFO *info, int16_t n, int32_t * psize); void WADRsaveEntry(struct WADINFO *info, int16_t N, const char *file); /*make some preparations before appending data to an existing WAD*/ /*so that it can be restored later*/ int32_t WADRprepareAppend(const char *wadres, struct WADINFO *rwad, struct WADDIR *NewDir, int32_t NewNtry, int32_t * dirpos, int32_t * ntry, int32_t * size); deutex-5.2.3/src/picture.c000066400000000000000000001502231475516661100154440ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ #include "deutex.h" #include #include #ifdef HAVE_LIBPNG #include #include "png_tools.h" #endif #include "tools.h" #include "endianio.h" #include "endianm.h" #include "mkwad.h" #include "picture.h" #include "ident.h" #include "color.h" #include "usedidx.h" /* * parse_pic_header * * buf Data for the entire lump containing the * hypothethical image. * bufsz Size of buf in bytes. * h Pointer on struct through which * parse_pic_header() returns the characteristic * of the picture header. * message Pointer on char[81]. If an error occurs (non-zero * return value) and is not NULL, a * string of at most 80 characters is written * there. * * Return 0 on success, non-zero on failure. */ #define FAIL0(s) do{if (message) strcpy (message, s); return 1;}while(0) #define FAIL1(f,a) do{if (message) sprintf (message, f, a); return 1;}while(0) int parse_pic_header(const char *buf, long bufsz, pic_head_t * h, char *message) { /* Current byte of pic buffer */ const unsigned char *p = (const unsigned char *) buf; /* Last byte of pic buffer */ const unsigned char *buf_end = ((const unsigned char *) buf) + bufsz - 1; /* Details of picture format in wad */ h->dummy_bytes = picture_format == PF_NORMAL; h->colofs_size = (picture_format == PF_NORMAL) ? 4 : 2; /* Read the picture header */ if (picture_format == PF_ALPHA) { if (p + 4 - 1 > buf_end) FAIL0("header too short"); h->width = *p++; h->height = *p++; h->xofs = *p++; h->yofs = *p++; } else { if (p + 8 - 1 > buf_end) FAIL0("header too short"); read_i16_le(p, &h->width); p += 2; read_i16_le(p, &h->height); p += 2; read_i16_le(p, &h->xofs); p += 2; read_i16_le(p, &h->yofs); p += 2; } /* Sanity checks on picture size and offsets */ if (h->width < 1) FAIL1("width < 1 (%d)", (int) h->width); if (h->height < 1) FAIL1("height < 1 (%d)", (int) h->height); /* Array of column offsets. Column data. */ h->colofs = p; p += (long) h->colofs_size * h->width; if (p > buf_end) FAIL0("offsets table too short"); h->data = p; return 0; /* Success */ } /* BMP,GIF,DoomPIC conversion ** intermediary format: RAW (FLAT= RAW 64x64 or RAW 64x65) ** int16_t Xsz int16_t Ysz ** char idx[Xsz*Ysz] ** colors= those of DOOM palette ** position (x,y) = idx[x+xsz*y] ** insertion point not set */ /* ** */ static char *PICtoRAW(int16_t * prawX, int16_t * prawY, int16_t * pXinsr, int16_t * pYinsr, const char *pic, int32_t picsz, char transparent, const char *name, cusage_t * cusage); static char *RAWtoPIC(int32_t * ppicsz, char *raw, int16_t rawX, int16_t rawY, int16_t Xinsr, int16_t Yinsr, char transparent); static char *snea_to_raw(int16_t * prawX, int16_t * prawY, int16_t * pXinsr, int16_t * pYinsr, const char *snea, int32_t sneasz, const char *name, cusage_t * cusage); static void RAWtoBMP(char *file, char *raw, int16_t rawX, int16_t rawY, struct PIXEL *doompal); static char *BMPtoRAW(int16_t * prawX, int16_t * prawY, char *file); static void RAWtoPPM(char *file, char *raw, int16_t rawX, int16_t rawY, struct PIXEL *doompal); static char *PPMtoRAW(int16_t * prawX, int16_t * prawY, char *file); static char *GIFtoRAW(int16_t * rawX, int16_t * rawY, char *file); static void RAWtoGIF(char *file, char *raw, int16_t rawX, int16_t rawY, struct PIXEL *doompal); #ifdef HAVE_LIBPNG static void RAWtoPNG(char *file, char *raw, int16_t rawX, int16_t rawY, struct PIXEL *doompal, int16_t Xinsr, int16_t Yinsr); static char *PNGtoRAW(int16_t * prawX, int16_t * prawY, char *file, int16_t * altXinsr, int16_t * altYinsr); #endif /* ** this is only a test example ** GIF->BMP */ void PicDebug(char *file, const char *bmpdir, const char *name) { char *raw; int16_t rawX = 0, rawY = 0; struct PIXEL *doompal; Phase("DB10", "GIF->RAW"); MakeFileName(file, bmpdir, "", "", name, "GIF"); raw = GIFtoRAW(&rawX, &rawY, file); doompal = COLdoomPalet(); Phase("DB20", "RAW->BMP"); MakeFileName(file, bmpdir, "", "", name, "BMP"); RAWtoBMP(file, raw, rawX, rawY, doompal); free(raw); } /* ** end of examples ** */ /* ** BMP, GIF or PPM ** */ bool PICsaveInFile(char *file, PICTYPE type, char *pic, int32_t picsz, int16_t * pXinsr, int16_t * pYinsr, IMGTYPE Picture, const char *name, cusage_t * cusage) { char *raw = NULL; int16_t rawX = 0; /* Initialise to placate GCC */ int16_t rawY = 0; /* Initialise to placate GCC */ struct PIXEL *doompal; char transparent; /*Warning ("XX99", "PICsaveInFile: %s", fname (file)); */ transparent = (char) COLinvisible(); *pXinsr = INVALIDINT; /*default insertion point X */ *pYinsr = INVALIDINT; /*default insertion point Y */ switch (type) { case PGRAPH: case PWEAPN: case PSPRIT: case PPATCH: /*Warning ("XX99", "PICtoRAW: %s", fname (file)); */ raw = PICtoRAW(&rawX, &rawY, pXinsr, pYinsr, pic, picsz, transparent, name, cusage); if (raw == NULL) return false; /*was not a valid DoomPic */ break; case PFLAT: if (picsz == 0x1000L) { rawX = 64; rawY = 64; } else if (picsz == 0x1040) { rawX = 64; rawY = 65; } /* Heretic scrolling flat */ else if (picsz == 0x2000) { rawX = 64; rawY = 128; } /* Hexen scrolling flat */ else return false; /*Wrong size for FLAT. F_SKY1 */ raw = pic; /*flat IS raw already */ if (cusage != NULL) usedidx_rectangle(pic, picsz, name, cusage); break; case PSNEAP: case PSNEAT: raw = snea_to_raw(&rawX, &rawY, pXinsr, pYinsr, pic, picsz, name, cusage); if (raw == NULL) return false; break; case PLUMP: /* Heretic, Hexen and Strife store some large bitmaps this way. */ if (picsz == 64000L) { rawX = 320; rawY = 200; } else return false; /*Wrong size for LUMP. F_SKY1 */ raw = pic; /*flat IS raw already */ if (cusage != NULL) usedidx_rectangle(pic, picsz, name, cusage); break; default: Bug("GW90", "Invalid type %d", (int) type); } if (cusage == NULL) { /* ** load game palette (PLAYPAL or TITLEPAL) */ if (type == PSNEAT) doompal = COLaltPalet(); else doompal = COLdoomPalet(); /* ** convert to BMP/GIF/PPM/PNG */ switch (Picture) { #ifdef HAVE_LIBPNG case PICPNG: RAWtoPNG(file, raw, rawX, rawY, doompal, *pXinsr, *pYinsr); break; #endif case PICGIF: RAWtoGIF(file, raw, rawX, rawY, doompal); break; case PICBMP: RAWtoBMP(file, raw, rawX, rawY, doompal); break; case PICPPM: RAWtoPPM(file, raw, rawX, rawY, doompal); break; default: Bug("GW91", "Invalid picture format %d", (int) Picture); } } switch (type) { case PGRAPH: case PWEAPN: case PSPRIT: case PPATCH: case PSNEAP: case PSNEAT: free(raw); break; case PFLAT: /*don't free pic! */ case PLUMP: break; } return true; } int32_t PICsaveInWAD(struct WADINFO * info, char *file, PICTYPE type, int16_t Xinsr, int16_t Yinsr, IMGTYPE Picture) { char *raw = NULL; int16_t rawX = 0, rawY = 0; char *pic = NULL; int32_t picsz = 0; /* Initialise to placate GCC */ char transparent; int16_t altXinsr = 0, altYinsr = 0; /* ** convert BMP to RAW */ transparent = COLinvisible(); switch (Picture) { #ifdef HAVE_LIBPNG case PICPNG: raw = PNGtoRAW(&rawX, &rawY, file, &altXinsr, &altYinsr); /* if the option to use png_offsets is set, use them */ if (use_png_offsets) { if (altXinsr != INVALIDINT && altYinsr != INVALIDINT) { Xinsr = altXinsr; Yinsr = altYinsr; } } break; #endif case PICGIF: raw = GIFtoRAW(&rawX, &rawY, file); break; case PICBMP: raw = BMPtoRAW(&rawX, &rawY, file); break; case PICPPM: raw = PPMtoRAW(&rawX, &rawY, file); break; default: Bug("GB02", "Invalid picture conversion %d", (int) Picture); } if (rawX < 1) ProgError("GB10", "%s: picture width < 1", fname(file)); if (rawY < 1) ProgError("GB11", "%s: picture height < 1", fname(file)); /*max tallpic is 2048 */ if (rawY > 2048) ProgError("GB13", "%s: picture height > 2048", fname(file)); switch (type) { case PGRAPH: case PWEAPN: case PSPRIT: case PPATCH: break; case PFLAT: /*flats */ if (rawX != 64) Warning("FB10", "%s: weird width for a flat (not 64)", fname(file)); if (rawY != 64 && rawY != 65 && rawY != 128) Warning("FB11", "%s: weird height for a flat (not 64, 65 or 128)", fname(file)); break; case PLUMP: /*special heretic lumps */ if (rawX != 320) Warning("LB10", "%s: weird width for a lump (not 320)", fname(file)); if (rawY != 200) Warning("LB11", "%s: weird height for a lump (not 200)", fname(file)); break; default: Bug("GB91", "Invalid type %d", (int) type); } /* ** calculate insertion point */ Xinsr = IDENTinsrX(type, Xinsr, rawX); Yinsr = IDENTinsrY(type, Yinsr, rawY); /* ** convert RAW to DoomPic */ switch (type) { case PGRAPH: case PWEAPN: case PSPRIT: case PPATCH: pic = RAWtoPIC(&picsz, raw, rawX, rawY, Xinsr, Yinsr, transparent); free(raw); WADRwriteBytes(info, pic, picsz); free(pic); break; case PLUMP: /*LUMP is RAW */ case PFLAT: /*FLAT is RAW */ picsz = ((int32_t) rawX) * ((int32_t) rawY); WADRwriteBytes(info, raw, picsz); free(raw); break; } /* ** write DoomPic in WAD */ return picsz; } /******************* DoomPic module ************************/ /* ** doom pic ** */ struct PICHEAD { int16_t Xsz; /*nb of columns */ int16_t Ysz; /*nb of rows */ int16_t Xinsr; /*insertion point */ int16_t Yinsr; /*insertion point */ }; /* ** RAW to DoomPIC conversion ** ** in: int16_t Xinsr; int16_t Yinsr; ** int16_t rawX; int16_t rawY; char transparent; ** char raw[Xsize*Ysize] ** out: int32_t picsz; size of DoomPIC ** char pic[picsz]; buffer for DoomPIC */ static char *RAWtoPIC(int32_t * ppicsz, char *raw, int16_t rawX, int16_t rawY, int16_t Xinsr, int16_t Yinsr, char transparent) { int16_t x, y; int16_t rowpos; int16_t number_of_pix_index = 1; int16_t first_pix_index = 3; bool is_tall_pic_post_header; bool is_first_254; char pix, lastpix; /*pixels */ /*Doom PIC */ char *pic; /*picture */ int32_t picsz, rawpos; struct PICHEAD *pichead; /*header */ int32_t *ColOfs; /*position of column */ /*columns composed of sets */ char *Set = NULL; int32_t colnbase, colnpos, setpos; int16_t setcount = 0; /*offset of first column */ colnbase = sizeof(struct PICHEAD) + ((int32_t) rawX) * sizeof(int32_t); /* worst expansion when converting from PIXEL column to ** list of sets: (5*Ysize/2)+(Y/254*8) + 5, corresponding to a dotted vertical ** transparent line with tallpic posts. */ int32_t worst_case = (int32_t) (5 * ((rawY + 1) / 2) + ((rawY / 254) * 8) + 5); picsz = colnbase + ((int32_t) rawX) * worst_case; pic = (char *) Malloc(picsz); ColOfs = (int32_t *) & (pic[sizeof(struct PICHEAD)]); pichead = (struct PICHEAD *) pic; /* ** convert raw (doom colors) to PIC */ write_i16_le(&pichead->Xsz, rawX); write_i16_le(&pichead->Ysz, rawY); write_i16_le(&pichead->Xinsr, Xinsr); write_i16_le(&pichead->Yinsr, Yinsr); colnpos = colnbase; for (x = 0; x < rawX; x++) { write_i32_le(ColOfs + x, colnpos); setpos = 0; lastpix = transparent; /* So this is how posts work: 0 [y-offset] 1 [n# of pixels in post] 2 [dummy pixel] 3 [pixel 0] 4 [pixel 1] ... 3+n-1 [pixel n-1] 4+n-1 [dummy pixel] It makes a new one every tranparent pixel. If y % 254 == 0 and the height of the image is >= 256, 509, etc, then a new tallpic post is started like this: 0 [254] 1 [0] 2 [0] 3 [0] 4 [no. of trans pixels between pixel 254 and the next non transparent pixel] 5 [n# of pixels in post] 6 [dummy pixel] 7 [pixel 0] 8 [pixel 1] ... 7+n-1 [pixel n-1] 8+n-1 [dummy pixel] THEN, every subsequent post after the tallpic post goes like this: 0 [row offset from first non-transparent pixel of last post] 1 [n# of pixels] 2 [dummy] etc. */ is_tall_pic_post_header = false; is_first_254 = true; number_of_pix_index = 1; first_pix_index = 3; rowpos = 0; for (y = 0; y < rawY; y++) { /*get column pixel */ rawpos = ((int32_t) x) + ((int32_t) rawX) * ((int32_t) y); pix = raw[rawpos]; /*if this is a picture that has a height of >=256, make tallpic post. */ if (((y == 254) || (y > 254 && setcount == 254) || (y > 254 && rowpos >= 254 && lastpix == transparent)) && rawY > 255) { is_tall_pic_post_header = true; is_first_254 = false; /*finish the current set, if any */ if (lastpix != transparent) { Set[number_of_pix_index] = setcount; Set[first_pix_index + setcount] = lastpix; setpos += first_pix_index + setcount + 1; /*1pos,1cnt,1dmy,setcount pixels,1dmy */ } Set = (char *) &(pic[colnpos + setpos]); rowpos = 0; //reset row position for subsequent posts after this one. setcount = 0; number_of_pix_index = 5; first_pix_index = 7; /*start new tallpic post */ Set[0] = 254; Set[1] = 0; Set[2] = 0; Set[3] = 0; Set[4] = 0; /*number of transparent pixels between this post and next */ Set[5] = 0; /*count (updated later) */ if (pix != transparent) Set[6] = pix; //dummy else Set[6] = 0; //dummy } /* Start new post ? */ if (pix != transparent) { if (is_tall_pic_post_header) { //We are done making the tallpic post header. is_tall_pic_post_header = false; lastpix = pix; //we DO NOT want to start a new post. } /* End current post and start new one if more than 128 consecutive non-transparent pixels AND picture is less than 256 pixels tall */ if (setcount == 128 && rawY < 256 && lastpix != transparent) { Set[1] = setcount; Set[3 + setcount] = lastpix; //dummy setpos += 3 + setcount + 1; lastpix = transparent; //start a new post } if (lastpix == transparent) { /* begining of post */ Set = (char *) &(pic[colnpos + setpos]); setcount = 0; number_of_pix_index = 1; first_pix_index = 3; Set[0] = rowpos; /* y position */ Set[1] = 0; /*count (updated later) */ Set[2] = pix; /*unused */ if (!is_first_254) { //reset rowpos if we are in relative offset mode rowpos = 0; } } Set[first_pix_index + setcount] = pix; /*non transparent pixel */ setcount++; /*update count of pixel in set */ } else { /*pix is transparent */ if (is_tall_pic_post_header) { Set[4] += 1; //increase transparent pixel count in tallpic post header rowpos = -1; //keep resetting rowpos until we get a non-transparent pixel } else if (lastpix != transparent) { /*finish the current set */ Set[number_of_pix_index] = setcount; Set[first_pix_index + setcount] = lastpix; //dummy setpos += first_pix_index + setcount + 1; /*1pos,1cnt,1dmy,setcount pixels,1dmy */ } /*else: not in set but in transparent area */ } lastpix = pix; rowpos++; } if (lastpix != transparent) { /*finish current set, if any */ Set[number_of_pix_index] = setcount; Set[first_pix_index + setcount] = lastpix; //dummy setpos += first_pix_index + setcount + 1; /*1pos,1cnt,1dmy,setcount pixels,1dmy */ } /*If we've reached the end of the column and we're still in the tallpic header, end the post at offset 4. */ if (is_tall_pic_post_header) { Set[4] = (char) 0xFF; setpos += 4; } else { //otherwise, end it normally. pic[colnpos + setpos] = (char) 0xFF; /*end of all sets */ } colnpos += (int32_t) (setpos + 1); /*position of next column */ } /*picsz was an overestimated size for PIC */ pic = (char *) Realloc(pic, colnpos); *ppicsz = colnpos; /*real size of PIC */ return pic; } /* ** DoomPIC to RAW ** ** in: int32_t picsz; transparent; ** char pic[picsz]; ** out: int16_t rawX; int16_t rawY; ** char raw[rawX*rawY]; ** NULL if it's not a valid pic ** ** The parameter is normally NULL. If -usedidx is given, ** it points to an object of type cusage_t. ** That block is for colour usage statistics and we update it. */ static char *PICtoRAW(int16_t * prawX, int16_t * prawY, int16_t * pXinsr, int16_t * pYinsr, const char *pic, int32_t picsz, char transparent, const char *name, cusage_t * cusage) { int16_t x, y; /*pixels */ const void *offsets; int realY; bool is_first_254; /* Last byte of pic buffer */ const unsigned char *pic_end = ((const unsigned char *) pic) + picsz - 1; /*raw picture */ char col, notransp; char *raw; int32_t rawpos, rawsz; pic_head_t h; int nw = 5; /* Number of warnings left */ if (cusage != NULL) usedidx_begin_lump(cusage, name); notransp = 0; /* this is to avoid trouble when the transparent index is used in a picture */ /* Read the header of the picture */ { char message_buf[81]; if (parse_pic_header(pic, picsz, &h, message_buf)) { Warning("PI10", "Picture %s: %s, skipping picture", lump_name(name), message_buf); return NULL; } } offsets = h.colofs; /* Read all columns, post by post */ /* allocate raw. (care: free it if error, before exit) */ /*Warning ("XX99", "%s: %d x %d", lump_name (name), (int) h.width, (int) h.height); */ rawsz = (long) h.width * h.height; raw = (char *) Malloc(rawsz); Memset(raw, transparent, rawsz); for (x = 0; x < h.width; x++) { const unsigned char *post = NULL; // Initialised to avoid a warning if (h.colofs_size == 4) { int32_t ofs; read_i32_le(((const int32_t *) offsets) + x, &ofs); post = (const unsigned char *) (pic + ofs); } else if (h.colofs_size == 2) { /* In principle, the offset is signed. However, considering it unsigned helps extracting patches larger than 32 kB, like W18_1 (alpha) or SKY* (PR). Interestingly, Doom alpha and Doom PR treat the offset as signed, which is why some textures appear with tutti-frutti on the right. -- AYM 1999-09-18 */ uint16_t ofs; read_i16_le(((const int16_t *) offsets) + x, (int16_t *) & ofs); post = (const unsigned char *) (pic + ofs); } else { Bug("PI90", "Invalid colofs_size %d", (int) h.colofs_size); /* Can't happen */ } if (post < (const unsigned char *) pic) { LimitedWarn(&nw, "PI11", "Picture %s(%d): column has bad offset %ld, skipping column", lump_name(name), (int) x, (long) ((const char *) pic - (const char *) post)); continue; } if (post < h.data) { LimitedWarn(&nw, "PI12", "Picture %s(%d): column has suspicious offset %ld", lump_name(name), (int) x, (long) ((const char *) pic - (const char *) post)); } /* Read an entire post */ realY = 0; is_first_254 = true; for (;;) { int post_length; const unsigned char *post_end; if (post > pic_end) { LimitedWarn(&nw, "PI13", "Picture %s(%d): post starts past EOL, skipping rest of column", lump_name(name), (int) x); goto done_with_column; } /* Read the header of the post */ if (*post == 0xff) break; /* Last post */ else if (*post == 0xfe) { //If this is a tallpic post is_first_254 = false; y = *post; y += realY; post = &post[4]; if (*post == 0xff) //last post break; y += *post++; //read transparent pixel count, go to count post_length = *post++; realY = y; } else { y = *post++; post_length = *post++; if (!is_first_254) { //if we are in relative offset mode y += realY; realY = y; } } if (h.dummy_bytes) post++; post_end = post + post_length; if (post_end > pic_end) { LimitedWarn(&nw, "PI14", "Picture %s(%d): post spans EOL, skipping rest of column", lump_name(name), (int) x); goto done_with_column; } /* Read the middle of the post */ for (; post < post_end; y++) { if (y >= h.height) { LimitedWarn(&nw, "PI15", "Picture %s(%d): post Y-offset too high, skipping rest of column", lump_name(name), (int) x); goto done_with_column; } col = *post++; if (cusage != NULL) usedidx_pixel(cusage, (unsigned char) col); if (col == transparent) { col = notransp; } rawpos = x + (long) h.width * y; raw[rawpos] = col; } /* Read the trailer of the post */ if (h.dummy_bytes) post++; } done_with_column: ; } /*return */ LimitedEpilog(&nw, "PI16", "Picture %s: ", lump_name(name)); if (cusage != NULL) usedidx_end_lump(cusage); *pXinsr = h.xofs; *pYinsr = h.yofs; *prawX = h.width; *prawY = h.height; return raw; } static char *snea_to_raw(int16_t * prawX, int16_t * prawY, int16_t * pXinsr, int16_t * pYinsr, const char *sneabuf, int32_t sneasz, const char *name, cusage_t * cusage) { const unsigned char *snea = (const unsigned char *) sneabuf; int width = snea[0] * 4; int height = snea[1]; unsigned long pixels = (long) width * height; unsigned char *raw = Malloc(pixels); const unsigned char *s = snea + 2; const unsigned char *end_of_snea = snea + sneasz; unsigned char *r = raw; unsigned char *end_of_raw = raw + pixels; if (pixels + 2 != sneasz) { Warning("SN10", "Snea %s: mismatch between size and geometry, skipping snea", lump_name(name)); free(raw); return NULL; } while (s < end_of_snea) { *r = *s++; r += 4; if (r >= end_of_raw) r -= pixels - 1; } if (cusage != NULL) { usedidx_rectangle(raw, pixels, name, cusage); } *prawX = width; *prawY = height; return raw; } /* color index convertion bmp->doom */ static uint8_t Idx2Doom[256]; struct BMPPALET { uint8_t B; uint8_t G; uint8_t R; uint8_t Zero; }; struct BMPPIXEL { uint8_t B; uint8_t G; uint8_t R; }; /* ** bitmap conversion */ struct BMPHEAD { int32_t bmplen; /*02 total file length Size */ int32_t reserved; /*06 void Reserved1 Reserved2 */ int32_t startpix; /*0A start of pixels OffBits */ /*bitmap core header */ int32_t headsz; /*0E Size =nb of bits in bc header */ int32_t szx; /*12 X size = width int16_t width */ int32_t szy; /*16 Y size = height int16_t height */ int32_t planebits; /*1A equal to 1 word planes */ /*1C nb of bits word bitcount 1,4,8,24 */ int32_t compress; /*1E int32_t compression = BI_RGB = 0 */ int32_t pixlen; /*22 int32_t SizeImage size of array necessary for pixels */ int32_t XpixRes; /*26 XPelsPerMeter X resolution no one cares */ int32_t YpixRes; /*2A YPelPerMeter Y resolution but code1a=code1b */ int32_t ColorUsed; /*2C ClrUsed nb of colors in palette */ int32_t ColorImp; /*32 ClrImportant nb of important colors in palette */ /*palette pos: ((uint8_t *)&headsz) + headsz */ /*palette size = 4*nb of colors. order is Blue Green Red (Black? always0) */ /*bmp line size is xsize*bytes_per_pixel aligned on int32_t */ /*pixlen = ysize * line size */ }; /* ** BMP to RAW conversion ** ** in: uint8_t *bmp; int32_t bmpsize; uint8_t COLindex(R,G,B); ** ** out: uint8_t *raw; int16_t rawX; int16_t rawY; */ static char *BMPtoRAW(int16_t * prawX, int16_t * prawY, char *file) { struct BMPHEAD *head; /*bmp header */ struct BMPPALET *palet; /*palet. 8 bit */ int32_t paletsz; uint8_t *line; /*line of BMP */ int32_t startpix, linesz = 0; struct BMPPIXEL *pixs; /*pixels. 24 bit */ uint8_t *bmpidxs; /*color indexs in BMP 8bit */ int16_t szx, szy, x, y, p, nbits; int32_t ncols; int32_t bmpxy; char *raw; /*point to pixs. 8 bit */ int32_t rawpos; uint8_t col = '\0'; FILE *fd; char sig[2]; /* ** read BMP header for size */ fd = fopen(file, FOPEN_RB); if (fd == NULL) ProgError("BR10", "%s: %s", fname(file), strerror(errno)); if (fread(sig, 2, 1, fd) != 1) ProgError("BR11", "%s: read error in header", fname(file)); if (strncmp(sig, "BM", 2) != 0) ProgError("BR12", "%s: bad BMP magic", fname(file)); head = (struct BMPHEAD *) Malloc(sizeof(struct BMPHEAD)); if (fread(head, sizeof(struct BMPHEAD), 1, fd) != 1) ProgError("BR13", "%s: read error in header", fname(file)); /* ** check the BMP header */ if (peek_i32_le(&head->compress) != 0) ProgError("BR14", "%s: not an RGB BMP", fname(file)); read_i32_le(&head->startpix, &startpix); szx = (int16_t) peek_i32_le(&head->szx); szy = (int16_t) peek_i32_le(&head->szy); if (szx < 1) ProgError("BR15", "%s: bad width", fname(file)); if (szy < 1) ProgError("BR16", "%s: bad height", fname(file)); ncols = peek_i32_le(&head->ColorUsed); /* ** Allocate memory for raw bytes */ raw = (char *) Malloc(((int32_t) szx) * ((int32_t) szy)); /* ** Determine line size and palet (if needed) */ nbits = (int16_t) ((peek_i32_le(&head->planebits) >> 16) & 0xFFFFL); switch (nbits) { case 24: /*RGB, aligned mod 4 */ linesz = ((((int32_t) szx) * sizeof(struct BMPPIXEL)) + 3L) & ~3L; break; case 8: linesz = (((int32_t) szx) + 3L) & ~3L; /*Idx, aligned mod 4 */ if (ncols > 256) ProgError("BR17", "%s: palette should have 256 entries", fname(file)); if (ncols <= 0) ncols = 256; /*Bug of PaintBrush */ paletsz = (ncols) * sizeof(struct BMPPALET); /*set position to palette (a bit hacked) */ if (fseek(fd, 0xEL + peek_i32_le(&head->headsz), SEEK_SET)) ProgError("BR18", "%s: can't seek to BMP palette (%s)", fname(file), strerror(errno)); /* FIXME mention the destination offset */ /*load palette */ palet = (struct BMPPALET *) Malloc(paletsz); if (fread(palet, (size_t) paletsz, 1, fd) != 1) ProgError("BR19", "%s: can't read palette", fname(file)); for (p = 0; p < ncols; p++) { Idx2Doom[p] = COLindex(palet[p].R, palet[p].G, palet[p].B, (uint8_t) p); } free(palet); break; default: ProgError("BR20", "%s: unsupported BMP type (%d bits)", fname(file), nbits); } bmpxy = ((int32_t) linesz) * ((int32_t) szy); /*Most windows application bug on one of the other */ /*picture publisher: bmplen incorrect */ /*Micrographix: bmplen over estimated */ /*PaintBrush: pixlen is null */ if (head->pixlen < bmpxy) if (peek_i32_le(&head->bmplen) < (startpix + bmpxy)) ProgError("BR21", "%s: size of pixel area incorrect", fname(file)); free(head); /* seek start of pixels */ if (fseek(fd, startpix, SEEK_SET)) ProgError("BR22", "%s: can't seek to pixel data (%s)", fname(file), strerror(errno)); /* FIXME mention the destination offset */ /* read lines */ line = (uint8_t *) Malloc(linesz); bmpidxs = (uint8_t *) line; pixs = (struct BMPPIXEL *) line; /*convert bmp pixels/bmp indexs into doom indexs */ for (y = szy - 1; y >= 0; y--) { if (fread(line, (size_t) linesz, 1, fd) != 1) ProgError("BR23", "%s: read error in pixel data", fname(file)); for (x = 0; x < szx; x++) { switch (nbits) { case 24: col = COLindex(pixs[x].R, pixs[x].G, pixs[x].B, 0); break; case 8: col = Idx2Doom[((int16_t) bmpidxs[x]) & 0xFF]; break; } rawpos = ((int32_t) x) + ((int32_t) szx) * ((int32_t) y); raw[rawpos] = col; } } free(line); fclose(fd); *prawX = szx; *prawY = szy; return raw; } /* ** Raw to Bmp 8bit ** ** in: int16_t rawX; int16_t rawY; struct BMPPALET doompal[256] ** uint8_t *raw ** out: int32_t bmpsz; ** uint8_t bmp[bmpsz]; */ static void RAWtoBMP(char *file, char *raw, int16_t rawX, int16_t rawY, struct PIXEL *doompal) { struct BMPHEAD *head; /*bmp header */ struct BMPPALET *palet; /*palet. 8 bit */ uint8_t *bmpidxs; /*color indexs in BMP 8bit */ int16_t linesz; /*size of line in Bmp */ int32_t startpix, pixlen, bmplen, paletsz; int16_t x, y, ncol; int32_t rawpos; FILE *fd; char sig[2]; /*BMP 8 bits avec DOOM Palette */ fd = fopen(file, FOPEN_WB); if (fd == NULL) ProgError("BW10", "%s: %s", fname(file), strerror(errno)); ncol = 256; paletsz = ncol * sizeof(struct BMPPALET); linesz = (rawX + 3) & (~3); /*aligned mod 4 */ pixlen = ((int32_t) linesz) * ((int32_t) rawY); startpix = 2 + sizeof(struct BMPHEAD) + paletsz; bmplen = startpix + pixlen; strncpy(sig, "BM", 2); if (fwrite(sig, 2, 1, fd) != 1) ProgError("BW11", "%s: write error", fname(file)); /*Header */ head = (struct BMPHEAD *) Malloc(sizeof(struct BMPHEAD)); write_i32_le(&head->bmplen, bmplen); write_i32_le(&head->reserved, 0); write_i32_le(&head->startpix, startpix); write_i32_le(&head->headsz, 0x28); write_i32_le(&head->szx, rawX); write_i32_le(&head->szy, rawY); write_i32_le(&head->planebits, 0x80001); /*1 plane 8bits BMP */ write_i32_le(&head->compress, 0); /* RGB */ write_i32_le(&head->pixlen, pixlen); write_i32_le(&head->XpixRes, 0); write_i32_le(&head->YpixRes, 0); write_i32_le(&head->ColorUsed, ncol); write_i32_le(&head->ColorImp, ncol); if (fwrite(head, sizeof(struct BMPHEAD), 1, fd) != 1) ProgError("BW12", "%s: write error", fname(file)); free(head); /* ** set palette ** */ palet = (struct BMPPALET *) Malloc(paletsz); for (x = 0; x < ncol; x++) { palet[x].R = doompal[x].R; palet[x].G = doompal[x].G; palet[x].B = doompal[x].B; palet[x].Zero = 0; } if (fwrite(palet, (size_t) paletsz, 1, fd) != 1) ProgError("BW13", "%s: write error", fname(file)); free(palet); /* ** set data ** */ bmpidxs = (uint8_t *) Malloc(linesz); for (y = rawY - 1; y >= 0; y--) { for (x = 0; x < rawX; x++) { rawpos = ((int32_t) x) + ((int32_t) rawX) * ((int32_t) y); bmpidxs[((int32_t) x)] = raw[rawpos]; } for (; x < linesz; x++) { bmpidxs[((int32_t) x)] = 0; } if (fwrite(bmpidxs, 1, linesz, fd) != linesz) ProgError("BW14", "%s: write error", fname(file)); } free(bmpidxs); /*done */ if (fclose(fd)) ProgError("BW15", "%s: %s", fname(file), strerror(errno)); } static void RAWtoPPM(char *file, char *raw, int16_t rawX, int16_t rawY, struct PIXEL *doompal) { FILE *fp; fp = fopen(file, FOPEN_WB); if (fp == NULL) ProgError("PW10", "%s: %s", fname(file), strerror(errno)); /* header */ fprintf(fp, "P6\n%d %d\n255\n", rawX, rawY); /* data */ { const unsigned char *p = (const unsigned char *) raw; const unsigned char *pmax = p + (long) rawX * rawY; for (p = raw; p < pmax; p++) fwrite(doompal + *p, sizeof *doompal, 1, fp); } if (fclose(fp)) ProgError("PW11", "%s: %s", fname(file), strerror(errno)); } static char *PPMtoRAW(int16_t * prawX, int16_t * prawY, char *file) { int32_t rawpos, rawSz; int16_t rawX, rawY; char *raw; /*point to pixs. 8 bit */ struct PIXEL pix; char buff[20]; uint8_t c; int16_t n; FILE *fd; /*BMP 8 bits avec DOOM Palette */ fd = fopen(file, FOPEN_RB); if (fd == NULL) ProgError("PR10", "%s: %s", fname(file), strerror(errno)); if (getc(fd) != 'P' || getc(fd) != '6') { fclose(fd); ProgError("PR11", "%s: not a rawbits PPM (P6) file", fname(file)); } c = getc(fd); while (isspace(c)) c = getc(fd); while (c == '#') { while (c != '\n') { c = getc(fd); } c = getc(fd); }; while (isspace(c)) c = getc(fd); for (n = 0; n < 10; n++) { if (!isdigit(c)) break; buff[n] = c; c = getc(fd); } buff[n] = '\0'; rawX = atoi(buff); while (isspace(c)) c = getc(fd); while (c == '#') { while (c != '\n') { c = getc(fd); } c = getc(fd); }; while (isspace(c)) c = getc(fd); for (n = 0; n < 10; n++) { if (!isdigit(c)) break; buff[n] = c; c = getc(fd); } buff[n] = '\0'; rawY = atoi(buff); while (isspace(c)) c = getc(fd); while (c == '#') { while (c != '\n') { c = getc(fd); } c = getc(fd); }; while (isspace(c)) c = getc(fd); for (n = 0; n < 10; n++) { if (!isdigit(c)) break; buff[n] = c; c = getc(fd); } buff[n] = '\0'; /* data */ if (rawX < 1) { fclose(fd); ProgError("PR12", "%s: width < 1 (%d)", fname(file), (int) rawX); } if (rawY < 1) { fclose(fd); ProgError("PR13", "%s: height < 1 (%d)", fname(file), (int) rawY); } rawSz = ((int32_t) rawX) * ((int32_t) rawY); raw = (char *) Malloc(rawSz); for (rawpos = 0; rawpos < rawSz; rawpos++) { if (fread(&pix, sizeof(struct PIXEL), 1, fd) != 1) { fclose(fd); ProgError("PR15", "%s: read error", fname(file)); } raw[rawpos] = COLindex(pix.R, pix.G, pix.B, 0); } fclose(fd); *prawX = rawX; *prawY = rawY; return raw; } #ifdef HAVE_LIBPNG static char *PNGtoRAW(int16_t * rawX, int16_t * rawY, char *file, int16_t * altXinsr, int16_t * altYinsr) { char *raw; int i; png_image image; memset(&image, 0, (sizeof image)); image.version = PNG_IMAGE_VERSION; read_grAb(file, altXinsr, altYinsr); if (png_image_begin_read_from_file(&image, file)) { png_bytep buffer; image.format = PNG_FORMAT_RGBA; buffer = malloc(PNG_IMAGE_SIZE(image)); if (buffer != NULL && png_image_finish_read(&image, NULL /*background */ , buffer, 0 /*row_stride */ , NULL)) { *rawX = (int16_t) image.width; *rawY = (int16_t) image.height; raw = (char *) malloc(((int32_t) image.height) * ((int32_t) image.width)); /* Convert 32-bit RGBA raw to 8-bit palletted raw with transparency color. */ for (i = 0; i < image.width * image.height * 4; i += 4) { /* If alpha channel is transparent, change color to the transparent color. */ if (buffer[i + 3] == 0x00) { raw[i / 4] = COLinvisible(); } else { raw[i / 4] = COLindex(buffer[i], buffer[i + 1], buffer[i + 2], 0); } } free(buffer); return raw; } else { ProgError("GR33", "libPNG decoding error"); return NULL; } } } static void RAWtoPNG(char *file, char *raw, int16_t rawX, int16_t rawY, struct PIXEL *doompal, int16_t Xinsr, int16_t Yinsr) { int i; png_image image; png_bytep colormap; memset(&image, 0, (sizeof image)); image.version = PNG_IMAGE_VERSION; image.opaque = NULL; image.width = (png_uint_32) rawX; image.height = (png_uint_32) rawY; image.format = PNG_FORMAT_RGBA_COLORMAP; image.colormap_entries = 256; colormap = malloc(PNG_IMAGE_COLORMAP_SIZE(image)); /* Convert the palette into a proper RGBA colormap */ for (i = 0; i < 256; i++) { /* If color is invisible, set Alpha to transparent */ if (i == COLinvisible()) { colormap[i * 4] = (png_byte) 0x00; colormap[i * 4 + 1] = (png_byte) 0x00; colormap[i * 4 + 2] = (png_byte) 0x00; colormap[i * 4 + 3] = (png_byte) 0x00; } else { colormap[i * 4] = (png_byte) doompal[i].R; colormap[i * 4 + 1] = (png_byte) doompal[i].G; colormap[i * 4 + 2] = (png_byte) doompal[i].B; colormap[i * 4 + 3] = (png_byte) 0xFF; } } if (!png_image_write_to_file(&image, file, 0, raw, 0, colormap)) { ProgError("GR34", "libPNG encoding error"); } else { if (Xinsr != INVALIDINT && Yinsr != INVALIDINT && Xinsr != 0 && Yinsr != 0) write_grAb(file, Xinsr, Yinsr); } free(colormap); } #endif static struct { uint16_t Transparent; uint16_t DelayTime; uint16_t InputFlag; uint16_t Disposal; } Gif89 = { -1, -1, -1, 0}; static struct { int16_t Width; int16_t Height; int16_t BitPixel; int16_t ColorRes; int16_t Backgnd; int16_t AspRatio; } GifScreen; static char GifIdent[6]; const int GIFHEADsz = 2 + 2 + 1 + 1 + 1; static struct GIFHEAD { /*size =7 */ int16_t xsize; int16_t ysize; uint8_t info; /*b7=colmap b6-4=colresol-1 b2-1=bitperpix-1 */ uint8_t backgnd; /*Backg color */ uint8_t aspratio; /*Aspect ratio */ } GifHead; struct PIXEL GifColor[256]; /*color map */ const int GIFIMAGEsz = 2 + 2 + 2 + 2 + 1; static struct GIFIMAGE { /*size =9 */ int16_t ofsx; /*0,1 left offset */ int16_t ofsy; /*2,3 top offset */ int16_t xsize; /*4,5 width */ int16_t ysize; /*6,7 heigth */ char info; /*8 b7=colmap b6=interlace b2-1=bitperpix-1 */ } GifImage; #define INTERLACE 0x40 #define COLORMAP 0x80 /* ** extern LZW routines */ extern void decompressInit(void); extern void decompressFree(void); extern int16_t LWZReadByte(FILE * fd, int16_t flag, int16_t input_code_size); extern void compressInit(void); extern void compressFree(void); static char *GIFreadPix(FILE * fd, int16_t Xsz, int16_t Ysz); static void GIFextens(FILE * fd); static char *GIFintlace(char *org, int16_t Xsz, int16_t Ysz); /* ** Read a Gif file */ static char *GIFtoRAW(int16_t * rawX, int16_t * rawY, char *file) { int16_t Xsz = 0, Ysz = 0; static bool IntLace = false; int16_t bitPixel; int32_t rawSz, rawpos; int16_t c; int chr; FILE *fd; char *raw = NULL; fd = fopen(file, FOPEN_RB); if (fd == NULL) ProgError("GR10", "%s: %s", fname(file), strerror(errno)); decompressInit(); /* ** screen descriptor */ if (fread(GifIdent, 6, 1, fd) != 1) { fclose(fd); ProgError("GR11", "%s: read error in GIF magic", fname(file)); } if (strncmp(GifIdent, "GIF87a", 6) && strncmp(GifIdent, "GIF89a", 6)) { fclose(fd); ProgError("GR12", "%s: not a 87a or 89a GIF", fname(file)); } if (fread_i16_le(fd, &GifHead.xsize) || fread_i16_le(fd, &GifHead.ysize) || (chr = fgetc(fd), GifHead.info = chr, chr == EOF) // Training || (chr = fgetc(fd), GifHead.backgnd = chr, chr == EOF) // for the || (chr = fgetc(fd), GifHead.aspratio = chr, chr == EOF)) { // IOCCC fclose(fd); ProgError("GR13", "%s: read error in GIF header", fname(file)); } bitPixel = 1 << ((GifHead.info & 0x07) + 1); GifScreen.BitPixel = bitPixel; GifScreen.ColorRes = (((GifHead.info >> 3) & 0xE) + 1); GifScreen.Backgnd = GifHead.backgnd; GifScreen.AspRatio = GifHead.aspratio; Memset(GifColor, 0, 256 * sizeof(struct PIXEL)); /* read Global Color Map */ if ((GifHead.info) & COLORMAP) { if (fread(GifColor, sizeof(struct PIXEL), bitPixel, fd) != bitPixel) { fclose(fd); ProgError("GR14", "%s: read error in GIF data", fname(file)); } } /* ** Read extension, images, etc... */ while ((c = getc(fd)) != EOF) { if (c == ';') break; /* GIF terminator */ /*no need to test imagecount */ if (c == '!') /* Extension */ GIFextens(fd); else if (c == ',') { /*valid image start */ if (raw != NULL) { /* only keep first image */ Warning("GR15", "%s: other GIF images discarded", fname(file)); break; } if (fread_i16_le(fd, &GifImage.ofsx) || fread_i16_le(fd, &GifImage.ofsy) || fread_i16_le(fd, &GifImage.xsize) || fread_i16_le(fd, &GifImage.ysize) || (chr = fgetc(fd), GifHead.info = chr, chr == EOF)) { fclose(fd); ProgError("GR16", "%s: read error", fname(file)); } /* GifImage.ofsx,ofsy X,Y offset ignored */ bitPixel = 1 << ((GifImage.info & 0x07) + 1); IntLace = (GifImage.info & INTERLACE) ? true : false; Xsz = GifImage.xsize; Ysz = GifImage.ysize; if ((Xsz < 1) || (Ysz < 1)) { fclose(fd); ProgError("GR17", "%s: bad size", fname(file)); } if (GifImage.info & COLORMAP) { if (fread(GifColor, sizeof(struct PIXEL), bitPixel, fd) != bitPixel) { fclose(fd); ProgError("GR18", "%s: read error", fname(file)); } } /*read the GIF. if many pictures, only the last one is kept. */ raw = GIFreadPix(fd, Xsz, Ysz); } /*else, not a valid start character, skip to next */ } fclose(fd); if (raw == NULL) ProgError("GR19", "%s: no picture found", fname(file)); /*convert colors */ for (c = 0; c < 256; c++) { Idx2Doom[c] = (uint8_t) COLindex((uint8_t) GifColor[c].R, (uint8_t) GifColor[c].G, (uint8_t) GifColor[c].B, (uint8_t) c); } rawSz = ((int32_t) Xsz) * ((int32_t) Ysz); for (rawpos = 0; rawpos < rawSz; rawpos++) { raw[rawpos] = Idx2Doom[((int16_t) raw[rawpos]) & 0xFF]; } /*unInterlace */ if (IntLace) { raw = GIFintlace(raw, Xsz, Ysz); } *rawX = Xsz; *rawY = Ysz; decompressFree(); return raw; } /* ** process the GIF extensions */ int16_t GIFreadBlock(FILE * fd, char buff[256]) { int16_t data, count, c; if ((data = fgetc(fd)) == EOF) return -1; /*no data block */ count = data & 0xFF; for (c = 0; c < count; c++) { if ((data = fgetc(fd)) == EOF) return -1; buff[c] = data & 0xFF; } return count; } static void GIFextens(FILE * fd) { char Buf[256]; int16_t label; if ((label = fgetc(fd)) == EOF) return; switch (label & 0xFF) { case 0x01: /* Plain Text Extension */ GIFreadBlock(fd, Buf); break; case 0xff: /* Application Extension */ break; case 0xfe: /* Comment Extension */ break; case 0xf9: /* Graphic Control Extension */ GIFreadBlock(fd, Buf); Gif89.Disposal = (Buf[0] >> 2) & 0x7; Gif89.InputFlag = (Buf[0] >> 1) & 0x1; Gif89.DelayTime = ((Buf[2] << 8) & 0xFF00) + Buf[1]; if (Buf[0] & 0x1) Gif89.Transparent = Buf[3]; break; default: /*Unknown GIF extension */ break; } while (GIFreadBlock(fd, Buf) > 0); } /* ** Read Gif Indexes */ static char *GIFreadPix(FILE * fd, int16_t Xsz, int16_t Ysz) { char *raw = NULL; int32_t rawSz; int16_t v; int32_t rawpos; unsigned char c = 0; /* ** get some space */ rawSz = ((int32_t) Xsz) * ((int32_t) Ysz); raw = (char *) Malloc(rawSz); /* Initialize the Compression routines */ if (fread(&c, 1, 1, fd) != 1) ProgError("GR50", "GIF: read error"); if (LWZReadByte(fd, true, c) < 0) ProgError("GR51", "GIF: bad code in image"); /* read the file */ for (rawpos = 0; rawpos < rawSz; rawpos++) { if ((v = LWZReadByte(fd, false, c)) < 0) ProgError("GR52", "GIF: too short"); raw[rawpos] = (v & 0xFF); } while (LWZReadByte(fd, false, c) >= 0); /* ignore extra data */ return raw; } /* ** Un-Interlace a GIF */ static char *GIFintlace(char *org, int16_t Xsz, int16_t Ysz) { int32_t rawpos, orgpos; int16_t pass, Ys = 0, Y0 = 0, y; char *raw; rawpos = ((int32_t) Xsz) * ((int32_t) Ysz); raw = (char *) Malloc(rawpos); orgpos = 0; for (pass = 0; pass < 4; pass++) { switch (pass) { case 0: Y0 = 0; Ys = 8; break; case 1: Y0 = 4; Ys = 8; break; case 2: Y0 = 2; Ys = 4; break; case 3: Y0 = 1; Ys = 2; break; } rawpos = (int32_t) Y0 *(int32_t) Xsz; for (y = Y0; y < Ysz; y += Ys) { Memcpy(&raw[rawpos], &org[orgpos], (size_t) Xsz); rawpos += (int32_t) Ys *(int32_t) Xsz; orgpos += Xsz; } } free(org); return raw; } /* ** write GIF */ typedef int16_t code_int; extern void compress(int16_t init_bits, FILE * outfile, code_int(*ReadValue) (void)); static char *Raw; static int32_t CountTop = 0; static int32_t CountCur = 0; static code_int NextPixel(void) { char c; if (CountCur >= CountTop) return EOF; c = Raw[CountCur]; CountCur++; return ((code_int) c & 0xFF); } static void RAWtoGIF(char *file, char *raw, int16_t rawX, int16_t rawY, struct PIXEL *doompal) { FILE *fd; int32_t rawSz; fd = fopen(file, FOPEN_WB); if (fd == NULL) ProgError("GW10", "%s: %s", fname(file), strerror(errno)); rawSz = (int32_t) rawX *(int32_t) rawY; /* screen header */ strncpy(GifIdent, "GIF87a", 6); fwrite(GifIdent, 1, 6, fd); /*header */ fwrite_u16_le(fd, rawX); /* xsize */ fwrite_u16_le(fd, rawY); /* ysize */ fputc(COLORMAP | ((8 - 1) << 4) | (8 - 1), fd); /* info */ /* global colormap, 256 colors, 7 bit per pixel */ fputc(0, fd); /* backgnd */ fputc(0, fd); /* aspratio */ fwrite(doompal, sizeof(struct PIXEL), 256, fd); /*color map */ fputc(',', fd); /*Image separator */ /* image header */ fwrite_u16_le(fd, 0); /* ofsx */ fwrite_u16_le(fd, 0); /* ofsy */ fwrite_u16_le(fd, rawX); /* xsize */ fwrite_u16_le(fd, rawY); /* ysize */ fputc(0, fd); /* info */ /* image data */ fputc(8, fd); /* Write out the initial code size */ Raw = raw; /* init */ CountTop = rawSz; CountCur = 0; compressInit(); compress(8 + 1, fd, NextPixel); /* write picture, InitCodeSize=8 */ compressFree(); /* termination */ fputc(0, fd); /*0 length packet to end */ fputc(';', fd); /*GIF file terminator */ if (fclose(fd)) ProgError("GW11", "%s: %s", fname(file), strerror(errno)); } deutex-5.2.3/src/picture.h000066400000000000000000000030671475516661100154540ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ /* first call COLinit();*/ bool PICsaveInFile(char *file, PICTYPE type, char *pic, int32_t picsz, int16_t * pXinsr, int16_t * pYinsr, IMGTYPE Picture, const char *name, cusage_t * cusage); int32_t PICsaveInWAD(struct WADINFO *info, char *file, PICTYPE type, int16_t Xinsr, int16_t Yinsr, IMGTYPE Picture); /* last call COLfree();*/ /*picture.c: only for test*/ void GIFtoBMP(char *file, char *bmpdir, char *name); void BMPtoGIF(char *file, char *bmpdir, char *name); /* * pic_head_t * parse_pic_header() returns header information * through this structure. */ typedef struct { int16_t width; /* Width of picture */ int16_t height; /* Height of picture */ int16_t xofs; /* X-offset of picture */ int16_t yofs; /* Y-offset of picture */ const void *colofs; /* Pointer on array of column offsets */ const unsigned char *data; /* Pointer on column data */ size_t colofs_size; /* Size of a column offset in bytes (2 or 4) */ int dummy_bytes; /* Are there dummy bytes around post data ? */ } pic_head_t; int parse_pic_header(const char *buf, long size, pic_head_t * h, char *message); deutex-5.2.3/src/png_tools.c000066400000000000000000000102371475516661100157750ustar00rootroot00000000000000/* Copyright © contributors to the DeuTex project. SPDX-License-Identifier: GPL-2.0-or-later */ /* This is a quick-and-dirty hack to read/write the grAb chunk from a png file. A grAb chunk is a custom PNG chunk that zdoom and SLADE uses to store the offset of the image. */ #include "deutex.h" #include "tools.h" #include "png_tools.h" #include "endianm.h" uint32_t gen_grAb_crc(unsigned char *buf) { uint32_t crc = 0xffffffff; uint32_t crc_table[256]; uint32_t c; int32_t n, k; for (n = 0; n < 256; n++) { c = (uint32_t) n; for (k = 0; k < 8; k++) { if (c & 1) c = ((uint32_t) 0xedb88320) ^ (c >> 1); else c = c >> 1; } crc_table[n] = c; } for (n = 0; n < 12; n++) crc = crc_table[(crc ^ buf[n]) & 0xff] ^ (crc >> 8); return crc ^ ((uint32_t) 0xffffffff); } unsigned char *read_whole_image(char *file, long *sz) { FILE *fd; unsigned char *buffer; size_t result; fd = fopen(file, FOPEN_RB); if (fd == NULL) { ProgError("XX16", "PNG image read error"); return NULL; } fseek(fd, 0L, SEEK_END); *sz = ftell(fd); rewind(fd); buffer = malloc(sizeof(unsigned char) * (*sz)); result = fread(buffer, 1, *sz, fd); if (result != *sz) { ProgError("XX12", "PNG image read error"); *sz = 0; return NULL; } fclose(fd); return buffer; } bool read_grAb_chunk(unsigned char *buffer, long sz, int32_t * xofs, int32_t * yofs, long *grabpos) { long i; bool is_grab = false; if (buffer == NULL) return false; for (i = 4; i < sz; i++) { if (buffer[i] == 'I' && buffer[i + 1] == 'D' && buffer[i + 2] == 'A' && buffer[i + 3] == 'T') { break; } if (buffer[i] == 'g' && buffer[i + 1] == 'r' && buffer[i + 2] == 'A' && buffer[i + 3] == 'b') { i += 4; if (i < sz) { read_i32_be(buffer + i, xofs); i += 4; if (i < sz) { read_i32_be(buffer + i, yofs); *grabpos = i - 8; is_grab = true; } else { ProgError("XX23", "read_grAb_chunk error"); } } else { ProgError("XX22", "read_grAb_chunk error"); } } } return is_grab; } void read_grAb(char *file, int16_t * Xinsr, int16_t * Yinsr) { long sz = 0; long IDATpos = 0; unsigned char *buffer = read_whole_image(file, &sz); if (buffer == NULL) return; long grabpos = 0; int32_t xofs = 0; int32_t yofs = 0; if (read_grAb_chunk(buffer, sz, &xofs, &yofs, &grabpos)) { *Xinsr = (int16_t) xofs; *Yinsr = (int16_t) yofs; } else { *Xinsr = INVALIDINT; *Yinsr = INVALIDINT; } free(buffer); } //To be called after the PNG is already written. void write_grAb(char *file, int16_t Xinsr, int16_t Yinsr) { long sz = 0; FILE *fd; long grabpos = 33; long index = 0; long IDATpos = 0; int32_t grabsz = 0; int32_t xofs = 0; int32_t yofs = 0; int32_t crc = 0; unsigned char grab_chunk[20]; bool grab_exists; unsigned char *buffer = read_whole_image(file, &sz); //create grAb Chunk //size of chunk write_i32_be(grab_chunk, 8); //name of chunk strncpy((char *) grab_chunk + 4, "grAb", 4); //xoffset write_i32_be(grab_chunk + 8, (int32_t) Xinsr); //yoffset write_i32_be(grab_chunk + 12, (int32_t) Yinsr); crc = gen_grAb_crc(grab_chunk + 4); //crc write_i32_be(grab_chunk + 16, crc); if (buffer == NULL) return; grab_exists = read_grAb_chunk(buffer, sz, &xofs, &yofs, &grabpos); fd = fopen(file, FOPEN_WB); if (fd == NULL) { ProgError("XX75", "write_grAb error"); } fwrite(buffer, sizeof(unsigned char), grabpos, fd); fwrite(grab_chunk, sizeof(char), 20, fd); if (!grab_exists) index = grabpos; else index = grabpos + 20; fwrite(buffer + index, sizeof(unsigned char), sz - index, fd); fclose(fd); free(buffer); } deutex-5.2.3/src/png_tools.h000066400000000000000000000010021475516661100157700ustar00rootroot00000000000000/* Copyright © contributors to the DeuTex project. SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef PNG_TOOLS_H #define PNG_TOOLS_H #include "deutex.h" uint32_t gen_grAb_crc(unsigned char *buf); unsigned char *read_whole_image(char *file, long *sz); bool read_grAb_chunk(unsigned char *buffer, long sz, int32_t * xofs, int32_t * yofs, long *grabpos); void read_grAb(char *file, int16_t * Xinsr, int16_t * Yinsr); void write_grAb(char *file, int16_t Xinsr, int16_t Yinsr); #endif deutex-5.2.3/src/sound.c000066400000000000000000000251711475516661100151240ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ #include "deutex.h" #include #include "tools.h" #include "endianm.h" #include "mkwad.h" #include "sound.h" #include "text.h" static struct RIFFHEAD { char riff[4]; int32_t length; char wave[4]; } headr; static struct CHUNK { char name[4]; int32_t size; } headc; static struct WAVEFMT { /*format */ char fmt[4]; /* "fmt " */ int32_t fmtsize; /*0x10 */ int16_t tag; /*format tag. 1=PCM */ int16_t channel; /*1 */ int32_t smplrate; int32_t bytescnd; /*average bytes per second */ int16_t align; /*block alignment, in bytes */ int16_t nbits; /*specific to PCM format */ } headf; static struct WAVEDATA { /*data */ char data[4]; /* "data" */ int32_t datasize; } headw; static void SNDsaveWave(char *file, char *buffer, int32_t size, int32_t speed) { FILE *fp; int32_t wsize, sz = 0; fp = fopen(file, FOPEN_WB); if (fp == NULL) { ProgError("RW10", "%s: %s", fname(file), strerror(errno)); } /*header */ strncpy(headr.riff, "RIFF", 4); write_i32_le(&headr.length, 4 + sizeof(struct WAVEFMT) + sizeof(struct WAVEDATA) + size); strncpy(headr.wave, "WAVE", 4); fwrite(&headr, sizeof(struct RIFFHEAD), 1, fp); strncpy(headf.fmt, "fmt ", 4); write_i32_le(&headf.fmtsize, sizeof(struct WAVEFMT) - 8); write_i16_le(&headf.tag, 1); write_i16_le(&headf.channel, 1); write_i32_le(&headf.smplrate, speed); write_i32_le(&headf.bytescnd, speed); write_i16_le(&headf.align, 1); write_i16_le(&headf.nbits, 8); fwrite(&headf, sizeof(struct WAVEFMT), 1, fp); strncpy(headw.data, "data", 4); write_i32_le(&headw.datasize, size); fwrite(&headw, sizeof(struct WAVEDATA), 1, fp); for (wsize = 0; wsize < size; wsize += sz) { sz = (size - wsize > MEMORYCACHE) ? MEMORYCACHE : (size - wsize); if (fwrite((buffer + (wsize)), (size_t) sz, 1, fp) != 1) ProgError("RW11", "%s: write error", fname(file)); } fclose(fp); } char *SNDloadWaveFile(char *file, int32_t * psize, int32_t * pspeed) { FILE *fp; int32_t wsize, sz = 0, smplrate, datasize; int32_t chunk; char *data; fp = fopen(file, FOPEN_RB); if (fp == NULL) ProgError("RR10", "%s: %s", fname(file), strerror(errno)); /*read RIFF HEADER */ if (fread(&headr, sizeof(struct RIFFHEAD), 1, fp) != 1) ProgError("RR11", "%s: read error in header", fname(file)); /*check RIFF header */ if (strncmp(headr.riff, "RIFF", 4) != 0) ProgError("RR12", "%s: bad RIFF magic in header (%s)", fname(file), short_dump(headr.riff, 4)); if (strncmp(headr.wave, "WAVE", 4) != 0) ProgError("RR13", "%s: bad WAVE magic in header (%s)", fname(file), short_dump(headr.wave, 4)); chunk = sizeof(struct RIFFHEAD); for (sz = 0;; sz++) { if (sz > 256) ProgError("RR14", "%s: no fmt", fname(file)); fseek(fp, chunk, SEEK_SET); if (fread(&headc, sizeof(struct CHUNK), 1, fp) != 1) ProgError("RR15", "%s: no fmt", fname(file)); if (strncmp(headc.name, "fmt ", 4) == 0) break; /* There used to be a bug here; sizeof (struct CHUNK) had its bytes swapped too. Reading .wav files on big endian machines must have been broken. -- AYM 1999-07-04 */ chunk += sizeof(struct CHUNK) + peek_i32_le(&headc.size); } fseek(fp, chunk, SEEK_SET); if (fread(&headf, sizeof(struct WAVEFMT), 1, fp) == 1) { Detail("RR01", "%s is a WAVE file", fname(file)); } else { ProgError("RR02", "%s is not a WAVE file", fname(file)); } if (peek_i16_le(&headf.tag) != 1) ProgError("RR16", "%s: not raw data", fname(file)); if (peek_i16_le(&headf.channel) != 1) ProgError("RR17", "%s: not one channel", fname(file)); smplrate = peek_i32_le(&headf.smplrate); for (sz = 0;; sz++) { if (sz > 256) ProgError("RR18", "%s: no data", fname(file)); fseek(fp, chunk, SEEK_SET); if (fread(&headc, sizeof(struct CHUNK), 1, fp) != 1) ProgError("RR19", "%s: no data", fname(file)); if (strncmp(headc.name, "data", 4) == 0) break; /* Same endianness bug as above. */ chunk += sizeof(struct CHUNK) + peek_i32_le(&headc.size); } fseek(fp, chunk, SEEK_SET); if (fread(&headw, sizeof(struct WAVEDATA), 1, fp) != 1) ProgError("RR20", "%s: no data", fname(file)); datasize = peek_i32_le(&headw.datasize); /*check WAVE header */ if (datasize > 0x1000000L) /* AYM 2000-04-22 */ ProgError("RR21", "%s: sample too long (%ld)", fname(file), (long) datasize); /*read data */ data = (char *) Malloc(datasize); for (wsize = 0; wsize < datasize; wsize += sz) { sz = (datasize - wsize > MEMORYCACHE) ? MEMORYCACHE : (datasize - wsize); if (fread((data + (wsize)), (size_t) sz, 1, fp) != 1) ProgError("RR22", "%s: read error in data", fname(file)); } fclose(fp); *psize = datasize; *pspeed = smplrate & 0xFFFFL; return data; } void SNDsaveSound(char *file, char *buffer, int32_t size, SNDTYPE format, const char *name) { char *data; int32_t datasize; int32_t phys_size; int16_t type; int16_t headsize; uint16_t rate; headsize = sizeof(int16_t) + sizeof(int16_t) + sizeof(int32_t); if (size < headsize) { Warning("SD10", "Sound %s: lump has no header, skipping", lump_name(name)); return; } type = peek_i16_le(buffer); rate = peek_u16_le(buffer + 2); datasize = peek_i32_le(buffer + 4); data = buffer + headsize; if (type != 3) Warning("SD11", "Sound %s: weird type %d, extracting anyway", lump_name(name), type); phys_size = size - headsize; if (datasize > phys_size) { Warning("SD12", "Sound %s: declared length %lu > lump size %lu, truncating", lump_name(name), (unsigned long) datasize, (unsigned long) phys_size); datasize = phys_size; } else if (datasize < phys_size) { datasize = phys_size; } switch (format) { case SNDWAV: SNDsaveWave(file, data, datasize, rate); break; default: Bug("SD14", "sndsv %d", (int) format); } } int32_t SNDcopyInWAD(struct WADINFO *info, char *file, SNDTYPE format) { int32_t size = 0; int32_t datasize; int32_t rate; char *data = NULL; long wadrate; switch (format) { case SNDWAV: data = SNDloadWaveFile(file, &datasize, &rate); break; default: Bug("SC10", "sndcw %d", (int) format); } wadrate = rate; switch (rate_policy) { case RP_REJECT: if (rate != 11025) ProgError("SC11", "%s: sample rate not 11025 Hz", fname(file)); break; case RP_FORCE: if (rate > 11025) { Warning("SC12", "%s: resampling down from %ld Hz to 11025 Hz", fname(file), rate); wadrate = 11025; { double ratio = 11025.0 / rate; long s; datasize = (int32_t) (ratio * datasize) + 1; for (s = 0; s < datasize; s++) data[s] = data[(size_t) (s / ratio + 0.5)]; } data = (char *) Realloc(data, datasize); } else if (rate < 11025) { Warning("SC13", "%s: resampling up from %ld Hz to 11025 Hz", fname(file), rate); wadrate = 11025; { double ratio = 11025.0 / rate; long s; datasize = (int32_t) (ratio * datasize) + 1; data = (char *) Realloc(data, datasize); for (s = datasize - 1; s >= 0; s--) data[s] = data[(size_t) (s / ratio + 0.5)]; } } break; case RP_WARN: if (rate != 11025) Warning("SC14", "%s: sample rate != 11025 Hz, won't work on Doom < 1.4", fname(file)); break; case RP_ACCEPT: break; default: Bug("SC15", "SNDcopyInWAD: rate_policy %d", (int) rate_policy); } if (datasize > 0) { size = WADRwriteShort(info, 3); size += WADRwriteShort(info, wadrate); size += WADRwriteLong(info, datasize); size += WADRwriteBytes(info, data, datasize); } free(data); return size; } void SNDsavePCSound(const char *lumpname, const char *file, const char *buffer, int32_t size) { FILE *fp; const char *data; int16_t datasize, type, headsize; int16_t i; headsize = sizeof(int16_t) + sizeof(int16_t); if (size < headsize) ProgError("KW10", "FIXME: wrong size", fname(file)); type = peek_i16_le(buffer); datasize = peek_i16_le(buffer + 2); data = buffer + (sizeof(int16_t) + sizeof(int16_t)); if (type != 0) Bug("KW11", "FIXME: not a PC sound", fname(file)); if (size < datasize + headsize) ProgError("KW12", "FIXME: wrong size", fname(file)); fp = fopen(file, FOPEN_WT); /*text file */ if (fp == NULL) ProgError("KW13", "%s: %s", fname(file), strerror(errno)); for (i = 0; i < datasize; i++) { fprintf(fp, "%d\n", ((int) data[i]) & 0xFF); } if (fclose(fp) != 0) ProgError("KW14", "%s: %s", fname(file), strerror(errno)); } int32_t SNDcopyPCSoundInWAD(struct WADINFO *info, char *file) { struct TXTFILE *Txt; int32_t size, datasizepos; int16_t datasize, s; char c; Txt = TXTopenR(file, 1); if (Txt == NULL) ProgError("KR10", "%s: %s", fname(file), strerror(errno)); size = WADRwriteShort(info, 0); datasizepos = WADRposition(info); size += WADRwriteShort(info, -1); datasize = 0; while (TXTskipComment(Txt)) { s = TXTreadShort(Txt); if ((s < 0) || (s > 255)) ProgError("KR11", "%s: number out of bounds [0-255]", fname(file)); datasize += sizeof(char); c = (char) (s & 0xFF); size += WADRwriteBytes(info, &c, sizeof(c)); } WADRsetShort(info, datasizepos, datasize); TXTcloseR(Txt); return size; } deutex-5.2.3/src/sound.h000066400000000000000000000012141475516661100151210ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ void SNDsaveSound(char *file, char *buffer, int32_t size, SNDTYPE Sound, const char *name); int32_t SNDcopyInWAD(struct WADINFO *info, char *file, SNDTYPE Sound); void SNDsavePCSound(const char *name, const char *file, const char *buffer, int32_t size); int32_t SNDcopyPCSoundInWAD(struct WADINFO *info, char *file); deutex-5.2.3/src/sscript.c000066400000000000000000000125011475516661100154540ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ #include "deutex.h" #include #include #include "mkwad.h" #include "tools.h" #include "endianm.h" #include "sscript.h" #define PAGESZ 1516 #define OPTIONSZ 228 enum field_type_t { FT_I32, FT_NAME, FT_S16, FT_S32, FT_S80, FT_S320 }; static void dump_field(FILE * fp, const unsigned char *buf, unsigned long *ofs, enum field_type_t type, const char *desc); static int mem_is_zero(const char *buf, size_t buf_size); /* * sscript_save - save a script lump to file */ int sscript_save(struct WADINFO *wad, int16_t n, const char *file) { FILE *fp = NULL; unsigned char *data = NULL; int32_t size = 0; const char *lumpname = wad->dir[n].name; data = (unsigned char *) WADRreadEntry(wad, n, &size); if (size % PAGESZ) { Warning("SS10", "script %s: weird size %ld", lump_name(lumpname), (long) size); size = PAGESZ * (size / PAGESZ); } fp = fopen(file, "w"); if (fp == NULL) { nf_err("SS15", "%s: %s", file, strerror(errno)); return 1; } fprintf(fp, "# DeuTex Strife script source version 1\n"); /* Save all pages */ { int p; for (p = 0; p < size / PAGESZ; p++) { unsigned long ofs = PAGESZ * p; int o; fputc('\n', fp); fprintf(fp, "page %d {\n", p); dump_field(fp, data, &ofs, FT_I32, " unknown0 "); dump_field(fp, data, &ofs, FT_I32, " unknown1 "); dump_field(fp, data, &ofs, FT_I32, " unknown2 "); dump_field(fp, data, &ofs, FT_I32, " unknown3 "); dump_field(fp, data, &ofs, FT_I32, " unknown4 "); dump_field(fp, data, &ofs, FT_I32, " unknown5 "); dump_field(fp, data, &ofs, FT_S16, " character "); dump_field(fp, data, &ofs, FT_NAME, " voice "); dump_field(fp, data, &ofs, FT_NAME, " background "); dump_field(fp, data, &ofs, FT_S320, " statement "); for (o = 0; o < 5; o++) { /* If the option is zeroed out, omit it */ if (mem_is_zero((char *) (data + ofs), OPTIONSZ)) { ofs += OPTIONSZ; continue; } /* Else, decode it */ fputc('\n', fp); fprintf(fp, " option %d {\n", o); dump_field(fp, data, &ofs, FT_I32, " whata "); dump_field(fp, data, &ofs, FT_I32, " whatb "); dump_field(fp, data, &ofs, FT_I32, " whatc "); dump_field(fp, data, &ofs, FT_I32, " whatd "); dump_field(fp, data, &ofs, FT_I32, " whate "); dump_field(fp, data, &ofs, FT_I32, " price "); dump_field(fp, data, &ofs, FT_I32, " whatg "); dump_field(fp, data, &ofs, FT_S32, " option "); dump_field(fp, data, &ofs, FT_S80, " success "); dump_field(fp, data, &ofs, FT_I32, " whath "); dump_field(fp, data, &ofs, FT_I32, " whati "); dump_field(fp, data, &ofs, FT_S80, " failure "); fputs(" }\n", fp); } fputs("}\n", fp); /* Sanity check */ if (ofs % PAGESZ) Bug("SS20", "script %s: page %d: bad script offset %d", lump_name(lumpname), p, (int) ofs); } } free(data); if (fclose(fp)) { nf_err("SS45", "%s: %s", file, strerror(errno)); return 1; } return 0; } /* * sscript_load */ int sscript_load(void) { printf("Oops! sscript_load not written yet!\n"); return 1; } /* * dump_field - write a field of a page to file */ static void dump_field(FILE * fp, const unsigned char *buf, unsigned long *ofs, enum field_type_t type, const char *desc) { fprintf(fp, "%-10s ", desc); if (type == FT_I32) { fprintf(fp, "%ld", (long) peek_i32_le(buf + *ofs)); *ofs += 4; } else if (type == FT_NAME) { int n; putc('"', fp); for (n = 0; n < 8 && buf[*ofs + n] != '\0'; n++) putc(tolower(buf[*ofs + n]), fp); putc('"', fp); *ofs += 8; } else if (type == FT_S16) { fprintf(fp, "\"%.16s\"", buf + *ofs); *ofs += 16; } else if (type == FT_S32) { fprintf(fp, "\"%.32s\"", buf + *ofs); *ofs += 32; } else if (type == FT_S80) { fprintf(fp, "\"%.80s\"", buf + *ofs); *ofs += 80; } else if (type == FT_S320) { fprintf(fp, "\"%.320s\"", buf + *ofs); *ofs += 320; } else { Bug("SS25", "bad field type %d", (int) type); } fputs(";\n", fp); } static int mem_is_zero(const char *buf, size_t buf_size) { int n; for (n = 0; n < buf_size; n++) { if (buf[n] != '\0') { return 0; } } return 1; } deutex-5.2.3/src/sscript.h000066400000000000000000000006051475516661100154630ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ int sscript_save(struct WADINFO *wad, int16_t n, const char *file); int sscript_load(void); deutex-5.2.3/src/text.c000066400000000000000000000405731475516661100147630ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ /* ** This code should contain all the tricky O/S related ** functions. If you're porting DeuTex, look here! */ #include "deutex.h" #include #include "tools.h" #include "text.h" #include /* A special instance of struct TXTFILE that is treated by the TXT*() output functions as the equivalent of /dev/null. Writing into it is a no-op. This convention is _not_ supported by the input functions ! */ struct TXTFILE TXTdummy; /*****************************************************/ const int16_t SPACE = 0x2; const int16_t NEWLINE = 0x4; const int16_t COMMENT = 0x8; const int16_t SECTION = 0x10; /*ok for SECTION header */ const int16_t NAME = 0x20; /*valid as a name identifier */ const int16_t NUMBER = 0x40; /*valid for a number */ const int16_t STPATCH = 0x80; /*start of patch? */ const int16_t EXESTRNG = 0x100; /*valid in #string# */ const int16_t BOUNDARY = 0x200; /*# */ const int16_t STEQUAL = 0x400; /*=*/ static int16_t TXTval[256]; static bool TXTok = false; void TXTinit(void) { int16_t n, val; for (n = 0; n < 256; n++) { val = 0; switch (n) { case '#': /*comment,string boundary */ val |= BOUNDARY + COMMENT; break; case ';': /*comment */ val |= COMMENT + EXESTRNG; break; case '\0': case '\n': /*newline */ val |= NEWLINE; break; case '_': /*in name */ case '\\': /* deal with VILE strange name */ val |= NAME + EXESTRNG; break; case '[': /* deal with VILE strange name */ case ']': /* deal with VILE strange name */ val |= SECTION + NAME + EXESTRNG; break; case '-': case '+': /*in integer number */ val |= NUMBER + EXESTRNG; break; case '*': /*start of patch wall */ val |= EXESTRNG + STPATCH; break; case '=': val |= STEQUAL + EXESTRNG; break; case '?': case '!': case '.': case ',': case '\'': case '&': case '(': case ')': case '$': case '%': case '@': case '/': case '<': case '>': case ' ': case '^': case '\"': case ':': val |= EXESTRNG; break; default: break; } if (isdigit(n)) val |= NUMBER + EXESTRNG; if (isalpha(n)) val |= SECTION + NAME + EXESTRNG; if (isspace(n)) val |= SPACE; if (n == '%') // Deal with Strife's "INVFONG%" and "INVFONY%" val |= NAME; TXTval[n] = val; } TXTok = true; } void TXTcloseR(struct TXTFILE *TXT) { if (!TXTok) Bug("TR91", "TxtClo"); fclose(TXT->fp); free(TXT); } struct TXTFILE *TXTopenR(const char *file, int silent) { struct TXTFILE *TXT; size_t pathname_len; /*characters */ if (!TXTok) TXTinit(); pathname_len = strlen(file); TXT = (struct TXTFILE *) Malloc(sizeof(struct TXTFILE) + pathname_len); /*some inits */ strcpy(TXT->pathname, file); TXT->Lines = 1; /*start in line 1 */ TXT->SectionStart = 0; TXT->SectionEnd = 0; TXT->fp = fopen(file, FOPEN_RT); if (TXT->fp == NULL) { if (silent) { free(TXT); return NULL; } ProgError("TR03", "%s: %s", fname(file), strerror(errno)); } return TXT; } static bool TXTgetc(struct TXTFILE *TXT, int16_t * c, int16_t * val) { int16_t cc = (int16_t) getc(TXT->fp); TXT->LastChar = cc; if (cc == EOF) return false; *c = cc = (cc & 0xFF); *val = TXTval[cc]; if (TXTval[cc] & NEWLINE) TXT->Lines++; return true; } static void TXTungetc(struct TXTFILE *TXT) { int16_t cc = TXT->LastChar; cc = (cc & 0xFF); ungetc(cc, TXT->fp); if (TXTval[cc] & NEWLINE) TXT->Lines--; } /*skip lines beginning with # or ; */ bool TXTskipComment(struct TXTFILE *TXT) { int16_t c = 0, val = 0; bool comment; for (comment = false;;) { if (!TXTgetc(TXT, &c, &val)) return false; if (val & NEWLINE) { /*eat newlines */ comment = false; continue; } if (val & COMMENT) { /*eat commentaries */ comment = true; continue; } if (val & SPACE) { /*eat space */ continue; } if (!comment) { TXTungetc(TXT); return true; } } } /* find '*' */ static bool TXTcheckStartPatch(struct TXTFILE *TXT) { int16_t c = 0, val = 0; if (!TXTgetc(TXT, &c, &val)) return false; if (val & STPATCH) return true; TXTungetc(TXT); return false; } /*read string, skip space before, stop space/\n*/ static bool TXTread(struct TXTFILE *TXT, char name[8], int16_t valid) { int16_t c = 0, val = 0, n = 0; while (1) { if (!TXTgetc(TXT, &c, &val)) return false; if (val & NEWLINE) continue; if (val & SPACE) continue; if (val & valid) break; ProgError("TR11", "%s(%ld): illegal char %s", TXT->pathname, (long) TXT->Lines, quotechar(c)); } name[0] = (char) c; for (n = 1; n < 256; n++) { if (!TXTgetc(TXT, &c, &val)) break; if (val & SPACE) { TXTungetc(TXT); break; } if (!(val & valid)) ProgError("TR13", "%s(%ld): illegal char %s in word", TXT->pathname, (long) TXT->Lines, quotechar(c)); if (n < 8) name[n] = (char) c; } if (n < 8) name[n] = '\0'; return true; } int16_t TXTreadShort(struct TXTFILE * TXT) { static char buffer[9]; TXTread(TXT, buffer, NUMBER); buffer[8] = '\0'; return (int16_t) atoi(buffer); } static bool TXTboundSection(struct TXTFILE *TXT); static bool TXTreadIdent(struct TXTFILE *TXT, char name[8]) { if (!TXTok) Bug("TR21", "%s: TxtRid", fname(TXT->pathname)); if (!TXTskipComment(TXT)) return false; /*check end of section */ if ((TXT->Lines) > (TXT->SectionEnd)) { if (!TXTboundSection(TXT)) return false; /*no other section */ } if (!TXTread(TXT, name, NAME | NUMBER)) ProgError("TR23", "%s(%ld): expected identifier or \"END:\"", TXT->pathname, (long) TXT->Lines); Normalise(name, name); return true; } /* ** STPATCH is also used to indicate repetition */ static bool TXTreadOptionalRepeat(struct TXTFILE *TXT) { int16_t c = 0, val = 0; while (1) { if (!TXTgetc(TXT, &c, &val)) return false; if (!(val & NEWLINE)) { if (val & STPATCH) break; /*look for STPATCH */ if (val & SPACE) continue; /*skip space */ } TXTungetc(TXT); return false; } return true; /*found */ } /* ** STEQUAL is used to indicate alternate name */ static void TXTreadOptionalName(struct TXTFILE *TXT, char name[8]) { int16_t c = 0, val = 0; while (1) { if (!TXTgetc(TXT, &c, &val)) return; if (!(val & NEWLINE)) { if (val & STEQUAL) continue; /*skip '=' */ if (val & SPACE) continue; /*skip space */ if (val & (NAME & (~NUMBER))) break; } TXTungetc(TXT); return; /*name is NOT modified */ } TXTungetc(TXT); if (!TXTread(TXT, name, NAME | NUMBER)) { ProgError("TR32", "%s(%ld): invalid optional name", TXT->pathname, (long) TXT->Lines); } } /* ** Read integer if exist before NEWLINE, ** but don't eat NEWLINE */ static int16_t TXTreadOptionalShort(struct TXTFILE *TXT) { static char name[9]; int16_t n, c = 0, val = 0; while (1) { if (!TXTgetc(TXT, &c, &val)) return INVALIDINT; if (!(val & NEWLINE)) { if (val & SPACE) continue; /*skip space */ if (val & STEQUAL) continue; /*skip '=' */ if (val & NUMBER) break; /*look for number */ } TXTungetc(TXT); return INVALIDINT; /*not a number. abort */ } name[0] = (char) c; for (n = 1; n < 256; n++) { if (!TXTgetc(TXT, &c, &val)) break; if (val & NEWLINE) { TXTungetc(TXT); break; } if (val & SPACE) break; if (!(val & NUMBER)) ProgError("TR42", "%s(%ld): illegal char %s in number", fname(TXT->pathname), (long) TXT->Lines, quotechar(c)); if (n < 8) name[n] = (char) c; } if (n < 8) name[n] = '\0'; name[8] = '\0'; return (int16_t) atoi(name); } /* read Blocks of the form ** [BLOCKID] ** identifier ... anything ... ** identifier ... anything ... */ static bool TXTfindSection(struct TXTFILE *TXT, bool Match) { int16_t c = 0, val = 0, n; char buffer[8]; while (1) { if (!TXTskipComment(TXT)) return false; if (!TXTgetc(TXT, &c, &val)) return false; if (c == '[') { for (n = 0; n < 256; n++) { if (!TXTgetc(TXT, &c, &val)) return false; if (c == ']') { if (n < 8) buffer[n] = '\0'; if (!Match) return true; /*any section is ok */ Normalise(buffer, buffer); /*the right section? */ if (strncmp(buffer, TXT->Section, 8) == 0) return true; break; /*not the right section */ } if (!(val & (NAME | NUMBER))) break; /*not a section */ if (n < 8) buffer[n] = c; } } while (1) { /*look for end of line */ if (!TXTgetc(TXT, &c, &val)) return false; if (val & NEWLINE) break; } } } /* ** find the section boundaries, from current position in file */ static bool TXTboundSection(struct TXTFILE *TXT) { int16_t c = 0, val = 0; if (!TXTfindSection(TXT, true)) return false; TXT->SectionStart = TXT->Lines + 1; /*check that we don't read twice the same section */ if (TXT->SectionEnd > TXT->SectionStart) Bug("TR51", "TxtBdS"); if (TXTfindSection(TXT, false)) TXT->SectionEnd = TXT->Lines - 1; else TXT->SectionEnd = TXT->Lines; /* set pointer to first section line */ fseek(TXT->fp, 0, SEEK_SET); TXT->Lines = 1; /*start in line 1 */ while (TXT->Lines < TXT->SectionStart) { if (!TXTgetc(TXT, &c, &val)) return false; } return true; } bool TXTseekSection(struct TXTFILE * TXT, const char *section) { if (!TXTok) Bug("TR61", "TxtSks"); /*seek begin of file */ TXT->SectionStart = 0; TXT->SectionEnd = 0; Normalise(TXT->Section, section); fseek(TXT->fp, 0L, SEEK_SET); TXT->Lines = 1; /*start in line 1 */ /*skipping comments, look for a line with [section] */ return TXTboundSection(TXT); } /*read a texture definition*/ /*return false if read End*/ bool TXTreadTexDef(struct TXTFILE * TXT, char name[8], int16_t * szx, int16_t * szy) { if (!TXTok) Bug("TR71", "TxtTxd"); if (!TXTskipComment(TXT)) return false; /*End */ if (!TXTread(TXT, name, NAME | NUMBER)) ProgError("TR73", "%s(%ld): expecting identifier", fname(TXT->pathname), (long) TXT->Lines); Normalise(name, name); *szx = TXTreadShort(TXT); *szy = TXTreadShort(TXT); return true; } /*read a patch def. Return false if could not find '*' */ bool TXTreadPatchDef(struct TXTFILE * TXT, char name[8], int16_t * ofsx, int16_t * ofsy) { if (!TXTok) Bug("TR81", "TxtRpd"); if (!TXTskipComment(TXT)) return false; if (!TXTcheckStartPatch(TXT)) return false; /*not a patch line */ if (!TXTread(TXT, name, NAME | NUMBER)) ProgError("TR83", "%s(%ld): expecting identifier", fname(TXT->pathname), (long) TXT->Lines); Normalise(name, name); *ofsx = TXTreadShort(TXT); *ofsy = TXTreadShort(TXT); return true; } bool TXTentryParse(char *name, char *filenam, int16_t * x, int16_t * y, bool * repeat, struct TXTFILE * TXT, bool XY) { int16_t c = 0, val = 0; bool comment; int16_t xx = INVALIDINT, yy = INVALIDINT; if (!TXTreadIdent(TXT, name)) return false; /* skip the equal */ if (!TXTgetc(TXT, &c, &val)) return false; if (c != '=') TXTungetc(TXT); /* read integer */ if (XY) { xx = TXTreadOptionalShort(TXT); yy = TXTreadOptionalShort(TXT); } Normalise(filenam, name); TXTreadOptionalName(TXT, filenam); if (XY) { if (xx == INVALIDINT) xx = TXTreadOptionalShort(TXT); if (yy == INVALIDINT) yy = TXTreadOptionalShort(TXT); } *repeat = TXTreadOptionalRepeat(TXT); *x = xx; *y = yy; for (comment = false;;) { if (!TXTgetc(TXT, &c, &val)) break; if (val & NEWLINE) break; if (val & COMMENT) { /*eat commentaries */ comment = true; continue; } if (val & SPACE) { /*eat space */ continue; } if (!comment) ProgError("TR87", "%s(%ld): bad entry format", fname(TXT->pathname), (long) TXT->Lines); } return true; } /* ** For any Writing of text files */ struct TXTFILE *TXTopenW(const char *file) { /*open, and init if needed */ struct TXTFILE *TXT; size_t pathname_len; /*characters */ if (!TXTok) TXTinit(); pathname_len = strlen(file); TXT = (struct TXTFILE *) Malloc(sizeof(struct TXTFILE) + pathname_len); /*some inits */ strcpy(TXT->pathname, file); TXT->Lines = 1; /*start in line 1 */ TXT->SectionStart = 0; TXT->SectionEnd = 0; TXT->fp = fopen(file, FOPEN_RT); if (TXT->fp == NULL) { TXT->fp = fopen(file, FOPEN_WT); } else { fclose(TXT->fp); TXT->fp = fopen(file, FOPEN_AT); Warning("TW03", "%s: already exists, appending to it", fname(file)); } if (TXT->fp == NULL) ProgError("TW05", "%s: %s", fname(file), strerror(errno)); return TXT; } void TXTcloseW(struct TXTFILE *TXT) { if (TXT == &TXTdummy) return; if (!TXTok) Bug("TW91", "TxtClo"); fclose(TXT->fp); free(TXT); } /* ** To write entries */ void TXTaddSection(struct TXTFILE *TXT, const char *def) { if (TXT == &TXTdummy) return; if (!TXTok) Bug("TW11", "TxtAdS"); fprintf(TXT->fp, "[%.8s]\n", def); } void TXTaddEntry(struct TXTFILE *TXT, const char *name, const char *filenam, int16_t x, int16_t y, bool repeat, bool XY) { if (TXT == &TXTdummy) return; if (!TXTok) Bug("TW21", "TxtAdE"); fprintf(TXT->fp, "%.8s", name); if (filenam != NULL) fprintf(TXT->fp, "\t%.8s", filenam); if (XY) fprintf(TXT->fp, "\t%d\t%d", x, y); if (repeat) fprintf(TXT->fp, "\t*"); fprintf(TXT->fp, "\n"); } void TXTaddComment(struct TXTFILE *TXT, const char *text) { if (TXT == &TXTdummy) return; if (!TXTok) Bug("TW31", "TxtAdC"); fprintf(TXT->fp, "# %.256s\n", text); } void TXTaddEmptyLine(struct TXTFILE *TXT) { if (TXT == &TXTdummy) return; if (!TXTok) Bug("TW41", "TxtAdL"); putc('\n', TXT->fp); } deutex-5.2.3/src/text.h000066400000000000000000000036431475516661100147650ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ /* ** simplified TEXT parsing */ struct TXTFILE { FILE *fp; int16_t Lines; int16_t LastChar; int16_t SectionStart; int16_t SectionEnd; char Section[8]; char pathname[1]; }; /* A special instance of struct TXTFILE that is treated by the TXT*() output functions as the equivalent of /dev/null. Writing into it is a no-op. This convention is _not_ supported by the input functions ! */ extern struct TXTFILE TXTdummy; /* ** For any Reading of TEXT files */ void TXTinit(void); struct TXTFILE *TXTopenR(const char *file, int silent); void TXTcloseR(struct TXTFILE *TXT); /* ** To read entries */ bool TXTskipComment(struct TXTFILE *TXT); bool TXTseekSection(struct TXTFILE *TXT, const char *def); bool TXTentryParse(char *name, char *filenam, int16_t * x, int16_t * y, bool * repeat, struct TXTFILE *TXT, bool XY); /* ** To read textures */ bool TXTreadTexDef(struct TXTFILE *TXT, char name[8], int16_t * szx, int16_t * szy); bool TXTreadPatchDef(struct TXTFILE *TXT, char name[8], int16_t * ofsx, int16_t * ofsy); /* ** To read PC sounds */ int16_t TXTreadShort(struct TXTFILE *TXT); /* ** For any Writing of text files */ struct TXTFILE *TXTopenW(const char *file); /*open, and init if needed */ void TXTcloseW(struct TXTFILE *TXT); /* ** To write entries */ void TXTaddSection(struct TXTFILE *TXT, const char *def); void TXTaddEntry(struct TXTFILE *TXT, const char *name, const char *filenam, int16_t x, int16_t y, bool repeat, bool XY); void TXTaddComment(struct TXTFILE *TXT, const char *text); void TXTaddEmptyLine(struct TXTFILE *TXT); deutex-5.2.3/src/texture.c000066400000000000000000000602641475516661100154760ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ #include "deutex.h" #include #include "tools.h" #include "endianm.h" #include "text.h" #include "mkwad.h" #include "texture.h" #include "wadio.h" /* The wad format limits the maximum number of patches per texture. */ #define MAX_PATCHES 32767 /* add new patchs by cluster of 64 */ #define NEWPATCHS 0x40 /* add new textures by cluster of 64 */ #define NEWTEXTURES 0x40 /* more than 8000 textures is unreasonable */ #define MAXTEXTURES 0x2000 /* add new patch defs by cluster of 128*/ #define NEWPATCHESDEF 0x80 /* TEXU module that stores the textures definitions PNM modules that stores the patch names */ static struct PNMP { char name[8]; } *PNMpatchs; static int16_t PNMtop; static int16_t PNMmax; static int16_t PNMknown; static bool PNMok = false; void PNMinit(char *buffer, int32_t size) { int16_t n, i; char picname[8]; int32_t pnames; /*find the number of entries in PNAME */ pnames = 0; if (size > 4L) { pnames = peek_i32_le(buffer); if (pnames > 0x7FFFL) ProgError("PA01", "Too many patches"); if (size < (4L + 8L * pnames)) ProgError("PA02", "PNAMES has wrong size"); } /*initialise */ PNMmax = (int16_t) (pnames + NEWPATCHS); PNMpatchs = (struct PNMP *) Malloc(PNMmax * sizeof(struct PNMP)); PNMtop = (int16_t) pnames; PNMknown = 0; /*Read patches */ if (pnames <= 0) return; for (n = 0; n < PNMtop; n++) { for (i = 0; i < 8; i++) picname[i] = buffer[4L + 8L * n + i]; Normalise(PNMpatchs[n].name, picname); } PNMknown = PNMtop; PNMok = true; } /* ** check if PNAME exists (ident.c) */ int16_t PNMindexOfPatch(char *patch) { int16_t idx; char name[8]; Normalise(name, patch); if (!PNMok) return -1; /*check index already exists */ for (idx = 0; idx < PNMtop; idx++) if (strncmp(PNMpatchs[idx].name, name, 8) == 0) /*if equal */ return idx; return -1; } /* Try to locate a Patch, from name ** if it doesn't exist, add it to the list */ static int16_t PNMgetPatchIndex(char *patch) { int16_t idx; char name[8]; if (!PNMok) Bug("PA90", "PNMok"); Normalise(name, patch); idx = PNMindexOfPatch(patch); if (idx < 0) { /*No, it's a new patch, then */ idx = PNMtop; Normalise(PNMpatchs[idx].name, name); PNMtop++; /* Increase top of patches */ if (PNMtop >= PNMmax) { PNMmax += NEWPATCHS; PNMpatchs = (struct PNMP *) Realloc(PNMpatchs, PNMmax * sizeof(struct PNMP)); } } return idx; } /* ** get name from index */ void PNMgetPatchName(char name[8], int16_t index) { if (!PNMok) Bug("PA91", "PNMok"); if (index >= PNMtop) Bug("PA92", "PnmGP>"); Normalise(name, PNMpatchs[index].name); } /* ** Insert in directory all the entries which are not ** referenced in DOOM.WAD */ int16_t PNMgetNbOfPatch(void) { return PNMtop; } bool PNMisNew(int16_t idx) { if (!PNMok) Bug("PA93", "PNMok"); if (idx >= PNMtop) Bug("PA94", "PnmIN>"); /*check if patch was added after initial definition */ if (idx >= PNMknown) return true; return false; } void PNMfree(void) { PNMok = false; free(PNMpatchs); } int32_t PNMwritePNAMEtoWAD(struct WADINFO *info) { int16_t idx; int32_t size = 0; char buffer[8]; if (!PNMok) Bug("PA95", "PNMok"); /*Write the Number of entries */ size += WADRwriteLong(info, PNMtop); /*Then write the names , '\0' padded */ for (idx = 0; idx < PNMtop; idx++) { Normalise(buffer, PNMpatchs[idx].name); size += WADRwriteBytes(info, buffer, 8); } return size; } struct TEXTUR { char Name[8]; int16_t szX; int16_t szY; int16_t Npatches; }; struct PATCH { int16_t Pindex; int16_t ofsX; int16_t ofsY; }; /*Textures: name, size*/ struct TEXTUR *TXUtex; int16_t TXUtexCur; /*currently edited in TEXUtex */ int16_t TXUtexTop; /*top of in TEXUtex */ int16_t TXUtexMax; /*Textures: patches composing textures*/ struct PATCH *TXUpat; long TXUpatTop; /*top of TEXUpat */ long TXUpatMax; bool TXUok = false; void TXUinit(void) { TXUtexMax = NEWTEXTURES; TXUtexTop = 0; TXUtex = (struct TEXTUR *) Malloc(TXUtexMax * sizeof(struct TEXTUR)); TXUpatMax = NEWPATCHESDEF; TXUpatTop = 0; TXUpat = (struct PATCH *) Malloc(TXUpatMax * sizeof(struct PATCH)); TXUok = true; } static void TXUdefineCurTex(char name[8], int16_t X, int16_t Y, bool Redefn) { int t; if (!TXUok) Bug("TX89", "TXUok"); TXUtexCur = TXUtexTop; /*set current entry */ TXUtexTop += 1; /*find a free position */ if (TXUtexTop >= TXUtexMax) { TXUtexMax += NEWTEXTURES; TXUtex = (struct TEXTUR *) Realloc(TXUtex, TXUtexMax * sizeof(struct TEXTUR)); } Normalise(TXUtex[TXUtexCur].Name, name); /*declare texture */ TXUtex[TXUtexCur].szX = X; TXUtex[TXUtexCur].szY = Y; TXUtex[TXUtexCur].Npatches = 0; /*check if we redefine other textures, and overide them. */ for (t = 0; t < TXUtexCur; t++) { if (strncmp(TXUtex[t].Name, name, 8) == 0) { if (Redefn) { TXUtex[t].Name[0] = '\0'; Detail("TX90", "Warning: texture %s is redefined", lump_name(name)); } else { /*don't redefine textures */ TXUtex[TXUtexCur].Name[0] = '\0'; break; } } } } static void TXUaddPatchToCurTex(int16_t pindex, int16_t ofsX, int16_t ofsY) { char pname[8]; if (!TXUok) Bug("TX91", "TXUok"); if (TXUpatTop >= TXUpatMax) { TXUpatMax += NEWPATCHESDEF; TXUpat = (struct PATCH *) Realloc(TXUpat, TXUpatMax * sizeof(struct PATCH)); } if (TXUtexCur < 0) Bug("TX92", "TXUTxC"); PNMgetPatchName(pname, pindex); /*check if index correct */ TXUtex[TXUtexCur].Npatches += 1; /*increase texture's patch counter */ TXUpat[TXUpatTop].Pindex = pindex; /*declare patch */ TXUpat[TXUpatTop].ofsX = ofsX; TXUpat[TXUpatTop].ofsY = ofsY; TXUpatTop += 1; } void TXUfree(void) { if (!TXUok) Bug("TX93", "TXUok"); free(TXUpat); free(TXUtex); TXUok = false; } bool TXUexist(char *Name) { int t; if (!TXUok) Bug("TX94", "TXUok"); for (t = 0; t < TXUtexTop; t++) { if (strncmp(TXUtex[t].Name, Name, 8) == 0) return true; } return false; } /* ** find the number of real textures */ static int32_t TXUrealTexture(void) { int16_t t; int32_t NbOfTex = 0; /*real top of texs */ for (t = 0; t < TXUtexTop; t++) { if (TXUtex[t].Npatches < 1) { Warning("TX10", "Ignored empty texture %s", lump_name(TXUtex[t].Name)); TXUtex[t].Name[0] = '\0'; } if (TXUtex[t].Name[0] != '\0') NbOfTex++; } return NbOfTex; } int32_t TXUwriteTEXTUREtoWAD(struct WADINFO * info) { int16_t t, tt, p, pat; int32_t size, ofsTble; int32_t NbOfTex; if (!TXUok) Bug("TX95", "TXUok"); if (TXUtexTop < 1) Bug("TX96", "TxuNTx"); /*count real textures */ NbOfTex = TXUrealTexture(); size = WADRwriteLong(info, NbOfTex); /*number of entries */ ofsTble = WADRposition(info); for (tt = 0; tt < NbOfTex; tt++) { size += WADRwriteLong(info, -1); /*pointer, to be corrected later */ } for (pat = 0, tt = 0, t = 0; t < TXUtexTop; t++) { if (TXUtex[t].Name[0] != '\0') { /*set texture direct pointer */ if (tt >= NbOfTex) Bug("TX97", "TxuRT"); WADRsetLong(info, ofsTble + tt * 4, size); tt++; /*write the begining of texture definition */ fseek(info->fd, info->wposit, SEEK_SET); /* Ugly */ if (output_texture_format != TF_NAMELESS) { wad_write_name(info->fd, TXUtex[t].Name); size += 8; } wad_write_i16(info->fd, 0); size += 2; wad_write_i16(info->fd, 0); size += 2; wad_write_i16(info->fd, TXUtex[t].szX); size += 2; wad_write_i16(info->fd, TXUtex[t].szY); size += 2; if (output_texture_format != TF_STRIFE11) { wad_write_i16(info->fd, 0); size += 2; wad_write_i16(info->fd, 0); size += 2; } wad_write_i16(info->fd, TXUtex[t].Npatches); size += 2; for (p = 0; p < (TXUtex[t].Npatches); p++) { if (pat + p >= TXUpatTop) /*number of patches exceeds definitions */ Bug("TX98", "TxuP>D"); wad_write_i16(info->fd, TXUpat[pat + p].ofsX); size += 2; wad_write_i16(info->fd, TXUpat[pat + p].ofsY); size += 2; wad_write_i16(info->fd, TXUpat[pat + p].Pindex); size += 2; if (output_texture_format != TF_STRIFE11) { wad_write_i16(info->fd, 0); size += 2; wad_write_i16(info->fd, 0); size += 2; } } info->wposit = ftell(info->fd); /* Ugly */ } pat += TXUtex[t].Npatches; } return size; } /* ** ** convert raw data (as read from wad) into Textures ** Data= texture entry ** if PatchSz>0, Patch defines the patch list */ void TXUreadTEXTURE(const char *texture1_name, const char *Data, int32_t DataSz, const char *Patch, int32_t PatchSz, bool Redefn) { int32_t Pos, Numtex, Numpat, dummy; /* texture data */ int16_t t, p, i, Xsize, Ysize; /* size x and y */ /* nb of patches used to build it */ /* patch inside a texture */ int16_t Xofs, Yofs, Pindex; /* x,y coordinate in texture space */ /* patch name index in PNAMES table */ int32_t MaxPindex; static char tname[10]; /*texture name */ static char pname[8]; /*patch name */ size_t header_size = 0; size_t item_size = 0; int have_texture_name = 0; int have_header_dummies = 0; if (input_texture_format == TF_NAMELESS) { header_size = 14; item_size = 10; have_texture_name = 0; have_header_dummies = 1; } else if (input_texture_format == TF_NONE) { Warning("TX11", "No texture definitions to read"); return; /* FIXME is it OK to do that ? */ } else if (input_texture_format == TF_NORMAL) { header_size = 22; item_size = 10; have_texture_name = 1; have_header_dummies = 1; } else if (input_texture_format == TF_STRIFE11) { header_size = 18; item_size = 6; have_texture_name = 1; have_header_dummies = 0; } else { Bug("TX99", "invalid itf %d", (int) input_texture_format); return; } /*get number of patches */ if (PatchSz > 0) { MaxPindex = peek_i32_le(Patch); dummy = 4L + (MaxPindex * 8L); if (dummy > PatchSz) ProgError("PA03", "PNAMES is corrupt"); } else { MaxPindex = PNMgetNbOfPatch(); } if ((MaxPindex < 0) || (MaxPindex > 0x7FFF)) ProgError("PA04", "PNAMES: invalid patch count %ld", (long) MaxPindex); /*get number of textures */ Numtex = peek_i32_le(Data); if (Numtex < 0) ProgError("TX12", "%s: invalid texture count %ld", lump_name(texture1_name), (long) Numtex); if (Numtex > MAXTEXTURES) ProgError("TX13", "%s: too many textures (%ld/%ld)", lump_name(texture1_name), (long) Numtex, (long) MAXTEXTURES); /*read textures */ for (t = 0; t < Numtex; t++) { Pos = peek_i32_le(Data + 4L + t * 4L); if (Pos + header_size > DataSz) ProgError("TX14", "%s: unexpected EOL in entry %d", lump_name(texture1_name), (long) t); if (have_texture_name) { Normalise(tname, Data + Pos); Pos += 8; } else { /* No name. Make one up (TEXnnnn) */ if (t > 9999) { Warning("TX15", "%s: more than 10000 textures, ignoring excess", lump_name(texture1_name)); break; } sprintf(tname, "TEX%04d", (int) t); } Pos += 4; /* Skip 2 dummy unused fields */ Xsize = peek_i16_le(Data + Pos); Pos += 2; if ((Xsize < 0) || (Xsize > 4096)) ProgError("TX16", "%s: Texture %s: width out of bounds (%d)", lump_name(texture1_name), lump_name(tname), (int) Xsize); Ysize = peek_i16_le(Data + Pos); Pos += 2; if ((Ysize < 0) || (Ysize > 4096)) ProgError("TX17", "%s: Texture %s: height out of bounds (%d)", lump_name(texture1_name), lump_name(tname), (int) Ysize); if (have_header_dummies) Pos += 4; /* Skip 2 dummy unused fields */ Numpat = peek_i16_le(Data + Pos) & 0xFFFF; Pos += 2; if (Numpat < 0) { nf_err("TX18", "%s: Texture %s: negative patch count %d, skipping texture", lump_name(texture1_name), lump_name(tname), (int) Numpat); continue; } /* declare texture */ TXUdefineCurTex(tname, Xsize, Ysize, Redefn); if (Pos + (long) Numpat * item_size > DataSz) ProgError("TX19", "%s(%ld): Texture %s: unexpected EOL", lump_name(texture1_name), (long) t, lump_name(tname)); for (p = 0; p < Numpat; p++, Pos += item_size) { Xofs = peek_i16_le(Data + Pos); if ((Xofs < -4096) || (Xofs > 4096)) ProgError("TX20", "%s(%ld): Texture %s(%d/%d): bad patch X-offset %d", lump_name(texture1_name), (long) t, lump_name(tname), (int) p, (int) Numpat, (int) Xofs); Yofs = peek_i16_le(Data + Pos + 2); if ((Yofs < -4096) || (Yofs > 4096)) ProgError("TX21", "%s(%ld): Texture %s(%d/%d): bad patch Y-offset %d", lump_name(texture1_name), (long) t, lump_name(tname), (int) p, (int) Numpat, (int) Yofs); Pindex = peek_i16_le(Data + Pos + 4); if ((Pindex < 0) || (Pindex > MaxPindex)) ProgError("TX22", "%s(%ld): Texture %s(%d/%d): bad patch index %d", lump_name(texture1_name), (long) t, lump_name(tname), (int) p, (int) Numpat, (int) Pindex); /*if new patch list, recalculate Pindex */ if (PatchSz > 0) { for (dummy = (4L + (Pindex * 8L)), i = 0; i < 8; i++) pname[i] = Patch[dummy + i]; Pindex = PNMgetPatchIndex(pname); } /*declare patch */ TXUaddPatchToCurTex(Pindex, Xofs, Yofs); } } } bool TXUcheckTex(int16_t npatch, int16_t * PszX) { int16_t t, tt, p, pat, col, top, found; int16_t bit, C, b; /*bit test */ int16_t Meduza; bool Res = true; if (!TXUok) Bug("TX23", "TXUok"); Output("Checking textures\n"); if (TXUtexTop < 1) Bug("TX24", "TxuNTx"); /* FIXME assign a code to this message */ if (TXUtexTop < 100) Output ("Warning: Some textures could be missing! (less than 100 defined)\n"); for (pat = 0, t = 0; t < TXUtexTop; t++) { if (TXUtex[t].Npatches < 1) /* FIXME assign a code to this message */ { Output("Warning: Texture %s is empty\n", lump_name(TXUtex[t].Name)); Res = false; } top = pat + TXUtex[t].Npatches; if (top > TXUpatTop) Bug("TX25", "TxuP>D"); /* ** check width */ for (bit = 1, C = 0, b = 0; b < 16; b++, bit <<= 1) if ((TXUtex[t].szX) & bit) C++; if (C > 1) { /* FIXME assign a code to this message */ Output("Warning: Width of %s is not a power of 2\n", lump_name(TXUtex[t].Name)); Res = false; } /* ** check height */ if (TXUtex[t].szY > 128) { /* FIXME assign a code to this message */ Output("Warning: Height of %s is more than 128\n", lump_name(TXUtex[t].Name)); Res = false; } /* ** check patch for: ** - void columns (crashes the game at boot) ** - possible meduza effect (if the patch is used on 2S walls) */ Meduza = 0; for (col = 0; col < TXUtex[t].szX; col++) { if (Meduza < 2) Meduza = 0; /*no Meduza effect found yet */ found = false; for (p = 0; p < (TXUtex[t].Npatches); p++) { if (TXUpat[pat + p].Pindex >= npatch) Bug("TX26", "~TxuP>D"); if (col >= TXUpat[pat + p].ofsX) { top = PszX[TXUpat[pat + p].Pindex] + TXUpat[pat + p].ofsX; if (col < top) { found = true; if (Meduza >= 2) break; // two patches on same column. Meduza effect else Meduza++; /*keep looking for patches */ } } } if (!found) { /* FIXME assign a code to this message */ Output("Warning: Empty column %d in texture %s\n", col, lump_name(TXUtex[t].Name)); Res = false; } } if (Meduza >= 2) { /*there is a column with two patches */ /* FIXME assign a code to this message */ Output ("Warning: Texture %s should not be used on a two sided wall.\n", lump_name(TXUtex[t].Name)); } pat += TXUtex[t].Npatches; } /* ** check duplication */ for (t = 0; t < TXUtexTop; t++) { for (tt = t + 1; tt < TXUtexTop; tt++) if (strncmp(TXUtex[t].Name, TXUtex[tt].Name, 8) == 0) { /* FIXME assign a code to this message */ Output("Warning: texture %s is duplicated\n", lump_name(TXUtex[t].Name)); Res = false; } } return Res; } /* ** this function shall only be used to add fake ** textures in order to list textures in levels */ void TXUfakeTex(char Name[8]) { if (!TXUok) Bug("TX27", "TXUok"); /*if already exist */ if (TXUexist(Name)) return; TXUtexCur = TXUtexTop; /*set current entry */ TXUtexTop += 1; /*find a free position */ if (TXUtexTop >= TXUtexMax) { TXUtexMax += NEWTEXTURES; TXUtex = (struct TEXTUR *) Realloc(TXUtex, TXUtexMax * sizeof(struct TEXTUR)); } Normalise(TXUtex[TXUtexCur].Name, Name); /*declare texture */ TXUtex[TXUtexCur].szX = 0; TXUtex[TXUtexCur].szY = 0; TXUtex[TXUtexCur].Npatches = 0; } /* ** list the names of the textures defined */ void TXUlistTex(void) { int16_t t; if (!TXUok) Bug("TX28", "TXUok"); for (t = 0; t < TXUtexTop; t++) { if (TXUtex[t].Name[0] != '\0') Output("%s\n", lump_name(TXUtex[t].Name)); } } /* ** write texture as text file */ void TXUwriteTexFile(const char *file) { int16_t t, p, pat, top; char pname[8]; FILE *out; if (!TXUok) Bug("TX29", "TXUok"); if (TXUtexTop < 1) Bug("TX30", "TxunTx"); out = fopen(file, FOPEN_WT); if (out == NULL) ProgError("TX31", "%s: %s", file, strerror(errno)); TXUrealTexture(); fprintf(out, ";Format of textures:\n"); fprintf(out, ";TextureName\tWidth\tHeight\n"); fprintf(out, ";*\tPatchName\tXoffset\tYoffset\n"); for (pat = 0, t = 0; t < TXUtexTop; t++) { if (TXUtex[t].Name[0] != '\0') { /*if tex was not redefined */ fprintf(out, "%-8.8s ", TXUtex[t].Name); fprintf(out, "\t\t%d\t%d\n", TXUtex[t].szX, TXUtex[t].szY); for (p = 0; p < TXUtex[t].Npatches; p++) { top = pat + p; if (top >= TXUpatTop) Bug("TX32", "TxuP>D"); PNMgetPatchName(pname, TXUpat[pat + p].Pindex); fprintf(out, "*\t%-8.8s ", pname); fprintf(out, "\t%d\t%d\n", TXUpat[pat + p].ofsX, TXUpat[pat + p].ofsY); } } pat += TXUtex[t].Npatches; } fprintf(out, ";End\n"); if (fclose(out) != 0) ProgError("TX33", "%s: %s", file, strerror(errno)); } /* ** read texture as text file ** */ void TXUreadTexFile(const char *file, bool Redefn) { int16_t Pindex; int16_t xsize = 0, ysize = 0, ofsx = 0, ofsy = 0; char tname[8]; char pname[8]; int16_t t, bit, C, b; /*to check Xsize */ struct TXTFILE *TXT; TXT = TXTopenR(file, 0); for (t = 0; t < MAXTEXTURES; t++) { if (!TXTreadTexDef(TXT, tname, &xsize, &ysize)) break; /* check X size */ if (xsize < 0) ProgError("TX35", "Texture %s: width %d < 0", lump_name(tname), (int) xsize); if (xsize > 4096) Warning("TX36", "Texture %s: width %d > 4096, XDoom 20001001 may crash", lump_name(tname), (int) xsize); if (xsize > 512) Warning("TX37", "Texture %s: width %d > 512, Doom may freeze", lump_name(tname), (int) xsize); for (bit = 1, C = 0, b = 0; b < 16; b++, bit <<= 1) if (xsize & bit) C++; if (C > 1) Warning("TX38", "Texture %s: width %d not a power of 2", lump_name(tname), (int) xsize); /* check Y size */ if (ysize < 0) ProgError("TX39", "Texture %s: height %d < 0", lump_name(tname), (int) ysize); if (ysize > 128) Warning("TX40", "Texture %s: height %d > 128, won't be rendered properly", lump_name(tname), (int) ysize); /* declare texture */ TXUdefineCurTex(tname, xsize, ysize, Redefn); { long npat = 0; while (TXTreadPatchDef(TXT, pname, &ofsx, &ofsy)) { if (npat == MAX_PATCHES) Warning("TX42", "Texture %s: more than 32767 patches, ignoring excess", lump_name(tname), ofsx); else if (npat > MAX_PATCHES); /* Silently drop the rest */ else { /* declare a patch wall, getting index or declaring a new index */ Pindex = PNMgetPatchIndex(pname); TXUaddPatchToCurTex(Pindex, ofsx, ofsy); } if (npat <= MAX_PATCHES) npat++; } } } Phase("TX44", "Read %d textures from %s", t, file); TXTcloseR(TXT); } deutex-5.2.3/src/texture.h000066400000000000000000000032371475516661100155000ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ /** PNM module **/ void PNMinit(char *buffer, int32_t size); int16_t PNMindexOfPatch(char *patch); /* check if patch exists. for ident.c */ int32_t PNMwritePNAMEtoWAD(struct WADINFO *info); /* write PNAME entry in Wad */ void PNMfree(void); int16_t PNMgetNbOfPatch(void); /* compose.c get nb of patches */ void PNMgetPatchName(char name[8], int16_t index); /* compose.c returns name of patch, from index */ bool PNMisNew(int16_t idx); /* compose.c is the patch not in IWAD? */ /** TXU module **/ void TXUinit(void); /*requires the the patches are init */ void TXUfree(void); bool TXUexist(char *Name); /*does it exist? */ int32_t TXUwriteTEXTUREtoWAD(struct WADINFO *info); /*write the TEXTURE entry in Wad */ void TXUreadTEXTURE(const char *texture1_name, const char *Data, int32_t DataSz, const char *Patch, int32_t PatchSz, bool Redefn); /*read texture from raw data = TEXTURE entry */ void TXUwriteTexFile(const char *file); /*write the TEXTURE entry to a file */ void TXUreadTexFile(const char *file, bool Redefn); /*checkif the composition of textues in okay */ bool TXUcheckTex(int16_t npatch, int16_t * PszX); /*declare a fake texure. to list textures */ void TXUfakeTex(char Name[8]); /*list all defined textures */ void TXUlistTex(void); deutex-5.2.3/src/tools.c000066400000000000000000000311271475516661100151320ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ /* ** This code should contain all the tricky O/S related ** functions. If you're porting DeuTex, look here! */ #include "deutex.h" #include #include #include "tools.h" #include #include #include #include #include #include #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #endif static const char hex_digit[16] = "0123456789ABCDEF"; /* ** Get a file time stamp. (date of last modification) */ int32_t Get_File_Time(const char *path) { int32_t time; struct stat statbuf; stat(path, &statbuf); time = statbuf.st_ctime; return time; } /* ** Set a file time stamp. */ void Set_File_Time(const char *path, int32_t time) { struct utimbuf stime; stime.modtime = stime.actime = time; utime(path, &stime); } /* ** Copy memory */ void Memcpy(void *dest, const void *src, long n) { if (n < 0) Bug("MM21", "MovInf"); /*move inf to zero */ if (n == 0) return; memcpy((char *) dest, (char *) src, (size_t) n); } /* ** Set memory */ void Memset(void *dest, char car, long n) { if (n < 0) Bug("MM11", "MStInf"); /*set inf to zero */ if (n == 0) return; memset(dest, car, (size_t) n); } /* ** Allocate memory */ /* actually, this is (size - 1) */ void *Malloc(long size) { void *ret; if (size < 1) { Warning("MM02", "Attempt to allocate %ld bytes", size); size = 1; } if ((size_t) size != size) ProgError("MM03", "Tried to allocate %ld b but couldn't; use another compiler", size); ret = malloc((size_t) size); if (ret == NULL) ProgError("MM04", "Out of memory (needed %ld bytes)", size); return ret; } /* ** Reallocate memory */ void *Realloc(void *old, long size) { void *ret; if (size < 1) { Warning("MM05", "Attempt to allocate %ld bytes", size); size = 1; } if ((size_t) size != size) ProgError("MM06", "Tried to realloc %ld b but couldn't; use another compiler", size); ret = realloc(old, (size_t) size); if (ret == NULL) ProgError("MM07", "Out of memory (needed %ld bytes)", size); return ret; } /* ** Use only lower case file names */ void ToLowerCase(char *file) { int16_t i; for (i = 0; (i < 128) && (file[i] != '\0'); i++) file[i] = tolower((((int16_t) file[i]) & 0xFF)); } static void NameDir(char file[128], const char *path, const char *dir, const char *sdir) { file[0] = '.'; file[1] = '\0'; if (path != NULL) if (strlen(path) > 0) { strncpy(file, path, 80); } if (dir != NULL) if (strlen(dir) > 0) { strcat(file, "/"); strncat(file, dir, 12); } if (sdir != NULL) if (strlen(sdir) > 0) { strcat(file, "/"); strncat(file, sdir, 12); } ToLowerCase(file); } /* ** Create directory if it does not exists */ void MakeDir(char file[128], const char *path, const char *dir, const char *sdir) { NameDir(file, path, dir, sdir); #ifdef _WIN32 CreateDirectory(file, NULL); #else mkdir(file, (mode_t) 0777); #endif } /* ** Create a file name, by concatenation ** returns true if file exists false otherwise */ bool MakeFileName(char file[128], const char *path, const char *dir, const char *sdir, const char *name, const char *extens) { FILE *fp; char name2[8]; /* AYM 1999-01-13: keep checker happy */ /* deal with VILE strange name ** replace the VILE\ ** by VILE^ */ Normalise(name2, name); /* Replace backslash as it is an illegal character on Windows. */ switch (name2[4]) { case '\\': name2[4] = '^'; break; } switch (name2[6]) { case '\\': name2[6] = '^'; break; } NameDir(file, path, dir, sdir); /* ** file name */ strcat(file, "/"); strncat(file, name2, 8); strcat(file, "."); strncat(file, extens, 4); ToLowerCase(file); /* ** check if file exists */ fp = fopen(file, FOPEN_RB); if (fp != NULL) { fclose(fp); return true; } return false; } /* ** Get the root name of a WAD file */ void GetNameOfWAD(char name[8], const char *path) { int16_t n, nam, len; len = (int16_t) strlen(path); /*find end of DOS or Unix path */ for (nam = n = 0; n < len; n++) switch (path[n]) { #ifdef _WIN32 case '\\': #endif case '/': nam = n + 1; } /*find root name */ /* FIXME AYM 1999-06-09: Do we really have to truncate to 8 ? */ for (n = 0; n < 8; n++) { switch (path[nam + n]) { case '.': case '\0': case '\n': name[n] = '\0'; return; default: name[n] = toupper(path[nam + n]); break; } } return; } /*****************************************************/ /* convert 8 byte string to upper case, 0 padded*/ void Normalise(char dest[8], const char *src) { /*strupr */ int16_t n; bool pad = false; char c = 'A'; for (n = 0; n < 8; n++) { c = pad ? '\0' : src[n]; if (c == '\0') pad = true; else c = (isprint(c)) ? toupper(c) : '*'; dest[n] = c; } } /* ** Output auxilliary functions */ /* * fnameofs - return string containing file name and offset * * Not reentrant (returns pointer on static buffer). * FIXME: should encode non-printable characters. * FIXME: should have shortening heuristic (E.G. print only basename). */ char *fnameofs(const char *name, long ofs) { static char buf[81]; *buf = '\0'; strncat(buf, name, sizeof buf - 12); sprintf(buf + strlen(buf), "(%06lXh)", ofs); return buf; } /* * fname - return string containing file name * * Not reentrant (returns pointer on static buffer). * FIXME: should encode non-printable characters. * FIXME: should have shortening heuristic (E.G. print only basename). */ char *fname(const char *name) { static char buf[81]; *buf = '\0'; strncat(buf, name, sizeof buf - 1); return buf; } /* * lump_name - return string containing lump name * * Partially reentrant (returns pointer on one of two * static buffers). The string is guaranteed to have at * most 32 characters and to contain only graphic * characters. */ char *lump_name(const char *name) { static char buf1[9]; static char buf2[9]; static int buf_toggle = 0; const char *const name_end = name + 8; char *buf = buf_toggle ? buf2 : buf1; char *p = buf; buf_toggle = !buf_toggle; if (*name == '\0') strcpy(buf, "(empty)"); else { for (; *name != '\0' && name < name_end; name++) { if (isgraph((unsigned char) *name)) *p++ = toupper((unsigned char) *name); else { *p++ = '\\'; *p++ = 'x'; *p++ = ((unsigned char) *name) >> 4; *p++ = *name & 0x0f; } } *p = '\0'; } return buf; } /* * short_dump - return string containing hex dump of buffer * * Not reentrant (returns pointer on static buffer). Length * is silently limited to 16 bytes. */ char *short_dump(const char *data, size_t size) { #define MAX_BYTES 16 static char buf[3 * MAX_BYTES]; char *b = buf; size_t n; for (n = 0; n < size && n < MAX_BYTES; n++) { if (n > 0) *b++ = ' '; *b++ = hex_digit[((unsigned char) data[n]) >> 4]; *b++ = hex_digit[((unsigned char) data[n]) & 0x0f]; } *b++ = '\0'; return buf; } /* * quotechar - return the safe representation of a char * * Not reentrant (returns pointer on static buffer). The string is * guaranteed to be exactly three characters long and not contain * any control or non-ASCII characters. */ const char *quotechar(char c) { static char buf[4]; if (c >= 32 && c <= 126) { buf[0] = '"'; buf[1] = c; buf[2] = '"'; buf[3] = '\0'; } else { buf[0] = hex_digit[((unsigned char) c) >> 4]; buf[1] = hex_digit[((unsigned char) c) & 0x0f]; buf[2] = 'h'; buf[3] = '\0'; } return buf; } /* ** Output and Error handling */ static bool asFile = false; static int16_t Verbosity = 2; void PrintVerbosity(int16_t level) { Verbosity = (level < 0) ? 0 : (level > 4) ? 4 : level; } void PrintExit(void) { if (asFile) { fclose(stdout); fclose(stderr); } } void ActionDummy(void) { return; } static void (*Action) (void) = ActionDummy; void ProgErrorCancel(void) { Action = ActionDummy; } void ProgErrorAction(void (*action) (void)) { Action = action; } void ProgError(const char *code, const char *fmt, ...) { va_list args; fflush(stdout); fprintf(stderr, "E %s ", code); va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); va_start(args, fmt); va_end(args); fputc('\n', stderr); (*Action) (); /* execute error handler */ PrintExit(); exit(2); } /* * nf_err - non fatal error message */ void nf_err(const char *code, const char *fmt, ...) { va_list args; fflush(stdout); fprintf(stderr, "%c %s ", MSGCLASS_ERR, code); va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); va_start(args, fmt); va_end(args); fputc('\n', stderr); fflush(stderr); } void Bug(const char *code, const char *fmt, ...) { va_list args; fflush(stdout); fprintf(stderr, "%c %s ", MSGCLASS_BUG, code); va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); va_start(args, fmt); va_end(args); fputc('\n', stderr); fputs("Please report that bug\n", stderr); /* CloseWadFiles(); */ PrintExit(); exit(3); } void Warning(const char *code, const char *fmt, ...) { va_list args; fflush(stdout); fprintf(stderr, "%c %s ", MSGCLASS_WARN, code); va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); va_start(args, fmt); va_end(args); fputc('\n', stderr); } void LimitedWarn(int *left, const char *code, const char *fmt, ...) { if (left == NULL || (left != NULL && *left > 0)) { va_list args; fflush(stdout); fprintf(stderr, "%c %s ", MSGCLASS_WARN, code); va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); va_start(args, fmt); va_end(args); fputc('\n', stderr); } if (left != NULL) (*left)--; } void LimitedEpilog(int *left, const char *code, const char *fmt, ...) { if (left != NULL && *left < 0) { fflush(stdout); if (fmt != NULL) { va_list args; fprintf(stderr, "%c %s ", MSGCLASS_WARN, code); va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); va_start(args, fmt); va_end(args); } fprintf(stderr, "%d warnings omitted\n", -*left); } } void Output(const char *fmt, ...) { va_list args; va_start(args, fmt); vfprintf(stdout, fmt, args); va_end(args); va_start(args, fmt); va_end(args); } void Info(const char *code, const char *fmt, ...) { if (Verbosity >= 1) { va_list args; fprintf(stdout, "%c %s ", MSGCLASS_INFO, code); va_start(args, fmt); vfprintf(stdout, fmt, args); va_end(args); va_start(args, fmt); va_end(args); fputc('\n', stdout); } } void Phase(const char *code, const char *fmt, ...) { if (Verbosity >= 2) { va_list args; fprintf(stdout, "%c %s ", MSGCLASS_INFO, code); va_start(args, fmt); vfprintf(stdout, fmt, args); va_end(args); va_start(args, fmt); va_end(args); fputc('\n', stdout); } } void Detail(const char *code, const char *fmt, ...) { if (Verbosity >= 3) { va_list args; fprintf(stdout, "%c %s ", MSGCLASS_INFO, code); va_start(args, fmt); vfprintf(stdout, fmt, args); va_end(args); va_start(args, fmt); va_end(args); fputc('\n', stdout); } } deutex-5.2.3/src/tools.h000066400000000000000000000043441475516661100151400ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef TOOLS_H #define TOOLS_H #define MSGCLASS_INFO 'i' #define MSGCLASS_WARN 'w' #define MSGCLASS_ERR 'E' #define MSGCLASS_BUG 'B' void PrintCopyright(void); void print_version(void); void NoCommandGiven(void); char *fnameofs(const char *name, long ofs); char *fname(const char *name); char *lump_name(const char *name); char *short_dump(const char *data, size_t size); const char *quotechar(char c); void PrintVerbosity(int16_t level); void PrintExit(void); void ProgErrorCancel(void); void ProgErrorAction(void (*action) (void)); void ProgError(const char *code, const char *fmt, ...); /*fatal error. halt. */ void nf_err(const char *code, const char *fmt, ...); /* Non-fatal error */ void Bug(const char *code, const char *fmt, ...); /*fatal bug. halt */ void Warning(const char *code, const char *str, ...); /*I'm not happy */ void LimitedWarn(int *left, const char *code, const char *fmt, ...); void LimitedEpilog(int *left, const char *code, const char *fmt, ...); void Output(const char *fmt, ...); /*command text output */ void Info(const char *code, const char *fmt, ...); /*useful indications */ void Phase(const char *code, const char *fmt, ...); /*phase of executions */ void Detail(const char *code, const char *fmt, ...); /*technical details */ void ToLowerCase(char *file); void MakeDir(char file[128], const char *path, const char *dir, const char *sdir); bool MakeFileName(char file[128], const char *path, const char *dir, const char *sdir, const char *name, const char *extens); void GetNameOfWAD(char name[8], const char *path); int32_t Get_File_Time(const char *path); void Set_File_Time(const char *path, int32_t time); void Memcpy(void *dest, const void *src, long n); void Memset(void *dest, char car, long n); void *Malloc(long size); void *Realloc(void *old, long size); void Normalise(char dest[8], const char *src); void Progress(void); void ProgressEnds(void); #endif //TOOLS_H deutex-5.2.3/src/usedidx.c000066400000000000000000000052241475516661100154360ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ /* * usedidx.c * Palette index usage statistics (cf -usedidx) */ #include "deutex.h" #include "tools.h" #include "usedidx.h" /* * usedidx_begin_lump * Call this before the first call to usedidx_count for * a given lump. */ void usedidx_begin_lump(cusage_t * cusage, const char *name) { int i; if (cusage == NULL) Bug("UI10", "usedidx_begin_lump: null cusage"); cusage->lump_uses = Malloc(NCOLOURS * sizeof *cusage->lump_uses); for (i = 0; i < NCOLOURS; i++) cusage->lump_uses[i] = 0; memcpy(cusage->lump_name, name, sizeof cusage->lump_name); } /* * usedidx_pixel * Call this for each pixel for a given lump. */ void usedidx_pixel(cusage_t * cusage, unsigned char idx) { cusage->lump_uses[idx]++; } /* * usedidx_end_lump * Call this when you're done with a given lump. */ void usedidx_end_lump(cusage_t * cusage) { int i; if (cusage == NULL) Bug("UI20", "usedidx_end_lump: null cusage"); for (i = 0; i < NCOLOURS; i++) { if (cusage->lump_uses[i] > 0) { if (cusage->uses[i] == 0) memcpy(cusage->where_first[i], cusage->lump_name, sizeof cusage->where_first[i]); cusage->uses[i] += cusage->lump_uses[i]; cusage->nlumps[i]++; } } free(cusage->lump_uses); } /* * usedidx_rectangle * Call this to update the cusage_t structure for a whole * array of pixels at once. The array is supposed not to * contain transparent pixels so this is not for pictures; * it's for flats and rectangular graphic lumps (E.G. * 320x200 "TITLE" from Heretic or Hexen and 10x12 * "GNUM[0-9]" from Doom alpha.) */ void usedidx_rectangle(const char *buf, long buf_size, const char *name, cusage_t * cusage) { const unsigned char *p = (const unsigned char *) buf; const unsigned char *pmax = p + buf_size; unsigned long *uses = Malloc(NCOLOURS * sizeof *uses); int i; for (i = 0; i < NCOLOURS; i++) uses[i] = 0; for (; p < pmax; p++) uses[*p]++; for (i = 0; i < NCOLOURS; i++) { if (uses[i] > 0) { if (cusage->uses[i] == 0) memcpy(cusage->where_first[i], name, 8); cusage->uses[i] += uses[i]; cusage->nlumps[i]++; } } free(uses); } deutex-5.2.3/src/usedidx.h000066400000000000000000000020221475516661100154340ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ /* * usedidx.h * Palette index usage statistics (cf -usedidx) */ #ifndef DT_USEDIDX_H #define DT_USEDIDX_H /* This block is used in conjunction with -usedidx. Avoid messing with its fields directly. Use the API instead. */ struct cusage_s { /* Per-lump: */ char lump_name[8]; unsigned long *lump_uses; /* Totals: */ unsigned long uses[NCOLOURS]; unsigned long nlumps[NCOLOURS]; char where_first[NCOLOURS][8]; }; void usedidx_begin_lump(cusage_t * cusage, const char *name); void usedidx_pixel(cusage_t * cusage, unsigned char idx); void usedidx_end_lump(cusage_t * cusage); void usedidx_rectangle(const char *buf, long buf_size, const char *name, cusage_t * cusage); #endif deutex-5.2.3/src/wadio.c000066400000000000000000000044021475516661100150710ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ /* * wadio.c * Wad low level I/O routines, without error checking. * AYM 1999-03-06 */ #include "deutex.h" #include #include "endianio.h" #include "wadio.h" /* * wad_read_i16 * wad_read_i32 * wad_write_i16 * wad_write_i32 * * Read and write 16-bit and 32-bit signed integers, with * the defined endianness. By default, all wad I/O is done * in little-endian mode. * * Return 0 on success, non-zero on failure. */ int (*wad_write_i16) (FILE *, int16_t) = fwrite_i16_le; int (*wad_write_i32) (FILE *, int32_t) = fwrite_i32_le; int (*wad_read_i16) (FILE *, int16_t *) = fread_i16_le; int (*wad_read_i32) (FILE *, int32_t *) = fread_i32_le; /* * set_input_wad_endianness * set_output_wad_endianness * * Define the endianness to use for wad input and output * respectively. Normally, all wads are little-endian. */ void set_output_wad_endianness(int big_endian) { wad_write_i16 = big_endian ? fwrite_i16_be : fwrite_i16_le; wad_write_i32 = big_endian ? fwrite_i32_be : fwrite_i32_le; } void set_input_wad_endianness(int big_endian) { wad_read_i16 = big_endian ? fread_i16_be : fread_i16_le; wad_read_i32 = big_endian ? fread_i32_be : fread_i32_le; } /* * wad_read_name * wad_write_name * * Read and write a directory name to a wad. The name is * truncated to 8 characters, upper-cased, terminated and * padded to 8 with NULs. * * Return 0 on success, non-zero on failure. */ int wad_read_name(FILE * fd, char name[8]) { size_t n; int end = 0; for (n = 0; n < 8; n++) { int c = getc(fd); name[n] = end ? '\0' : toupper(c); if (c == '\0') end = 1; } return feof(fd) || ferror(fd); } int wad_write_name(FILE * fd, const char *name) { size_t n; for (n = 0; n < 8 && name[n]; n++) putc(toupper(name[n]), fd); for (; n < 8; n++) putc('\0', fd); return ferror(fd); } deutex-5.2.3/src/wadio.h000066400000000000000000000013641475516661100151020ustar00rootroot00000000000000/* Copyright ® Olivier Montanuy, André Majorel, contributors to the DeuTex project. DeuTex incorporates code derived from DEU 5.21 that was put in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. SPDX-License-Identifier: GPL-2.0-or-later */ /* * wadio.h * Wad low level I/O routines * AYM 1999-03-06 */ void set_output_wad_endianness(int big_endian); void set_input_wad_endianness(int big_endian); extern int (*wad_write_i16) (FILE *, int16_t); extern int (*wad_write_i32) (FILE *, int32_t); extern int (*wad_read_i16) (FILE *, int16_t *); extern int (*wad_read_i32) (FILE *, int32_t *); int wad_read_name(FILE * fd, char name[8]); int wad_write_name(FILE * fd, const char *name);