├── .gitignore ├── .travis.yml ├── AUTHORS ├── COPYING ├── COPYING.LIB ├── LICENSE ├── Makefile.am ├── NEWS.adoc ├── README.adoc ├── bootstrap ├── configure.ac ├── man ├── .gitignore ├── Makefile.am └── deutex.txt.in ├── pkg.w32 ├── .gitignore ├── Makefile ├── config.make.in └── cp-with-libs └── src ├── .gitignore ├── Makefile.am ├── 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 ├── picture.c ├── picture.h ├── png_tools.c ├── png_tools.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 /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | Makefile.in 3 | /autom4te.cache 4 | /aclocal.m4 5 | /compile 6 | /config.guess 7 | /config.h 8 | /config.h.in 9 | /config.log 10 | /config.status 11 | /configure 12 | /depcomp 13 | /install-sh 14 | /missing 15 | /stamp-h1 16 | 17 | *.html 18 | 19 | *~ 20 | \#*\# 21 | .\#* 22 | .DS_Store 23 | .vscode 24 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | arch: 3 | - amd64 4 | - ppc64le 5 | compiler: gcc 6 | script: ./bootstrap && ./configure && make 7 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | André Majorel 2 | Ayub Ahmed 3 | Jon Dowland 4 | Mike Swanson 5 | Nick Zatkovich 6 | Olivier Montanuy 7 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | License terms for DeuTex 2 | 3 | ------------------------------------------------------------------------ 4 | 5 | DeuTex is Copyright © 1994-1995 Olivier Montanuy, 6 | Copyright © 1999-2005 André Majorel, 7 | Copyright © 2006-2025 contributors to the DeuTex project. 8 | 9 | The parts of DeuTex that have been written by Olivier Montanuy, 10 | Kevin McGrail, André Majorel and other contributors are 11 | distributed under the terms of version 2 of the GNU general 12 | public license (GPL). 13 | 14 | This program is free software; you can redistribute it and/or modify 15 | it under the terms of the GNU General Public License as published by 16 | the Free Software Foundation; either version 2 of the License, or 17 | (at your option) any later version. 18 | 19 | This program is distributed in the hope that it will be useful, 20 | but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | GNU General Public License for more details. 23 | 24 | You should have received a copy of the GNU Library General Public 25 | License along with this library; if not, write to the Free 26 | Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 27 | 02111-1307, USA. 28 | 29 | ------------------------------------------------------------------------ 30 | 31 | However, the DeuTex distribution includes material that was 32 | written by other people or distributed under different licenses : 33 | 34 | 1. DeuTex incorporates code derived from DEU 5.21. DEU 5.21 was 35 | put in the public domain in 1994 by Raphaël Quinet and Brendon 36 | Wyber. No part of DeuTex is public-domain, however. 37 | 38 | 2. The file lzw.c contains the following notes : 39 | 40 | > Copyright 1993, David Koblas (koblas=netcom+com) 41 | > 42 | > Permission to use, copy, modify, and to distribute this software 43 | > and its documentation for any purpose is hereby granted without 44 | > fee, provided that the above copyright notice appear in all 45 | > copies and that both that copyright notice and this permission 46 | > notice appear in supporting documentation. There is no 47 | > representations about the suitability of this software for 48 | > any purpose. this software is provided "as is" without express 49 | > or implied warranty. 50 | 51 | > This file is derived from ppmtogif.c and giftoppm.c 52 | > only the compression/decompression routines were kept. 53 | > 54 | > Based on GIFENCOD by David Rowley . 55 | > A Lempel-Zim compression based on "compress". 56 | > 57 | > Copyright (C) 1989 by Jef Poskanzer. 58 | > 59 | > Permission to use, copy, modify, and distribute this software and 60 | > its documentation for any purpose and without fee is hereby 61 | > granted, provided that the above copyright notice appear in all 62 | > copies and that both that copyright notice and this permission 63 | > notice appear in supporting documentation. This software is 64 | > provided "as is" without express or implied warranty. 65 | > 66 | > The Graphics Interchange Format(c) is the Copyright property of 67 | > CompuServe Incorporated. GIF(sm) is a Service Mark property of 68 | > CompuServe Incorporated. 69 | 70 | 3. The files endianio.c and endianm.c are Copyright © André Majorel 1999 71 | and available under the terms of the LGPL. 72 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = man src 2 | EXTRA_DIST = LICENSE NEWS.adoc README.adoc \ 3 | pkg.w32/config.make.in pkg.w32/cp-with-libs pkg.w32/Makefile 4 | 5 | if ASCIIDOC 6 | CLEANFILES = *.html 7 | %.html: %.adoc 8 | TZ=UTC asciidoc $< 9 | 10 | html: README.html NEWS.html 11 | $(MAKE) -C man html 12 | endif 13 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | DeuTex 2 | ====== 3 | 4 | DeuTex can do many things with _Doom_, _Freedoom_, _Heretic_, _Hexen_, 5 | and _Strife_ https://doomwiki.org/wiki/WAD[“WAD”] files, such as 6 | extracting and inserting graphics, sounds, levels, and other 7 | resources. It can be used for creating and modifying IWAD (“Internal 8 | WAD”) and PWAD (“Patch WAD”) files both. 9 | 10 | It is a command-line driven program and as such, is suitable for 11 | scripting such as from a shell script or Makefile. 12 | 13 | File formats 14 | ------------ 15 | 16 | For insertion and extraction, DeuTex supports a few common formats. 17 | 18 | For music, DeuTex supports both the DMX “mus” file format native to 19 | _Doom_, and also MIDI files supported in _Doom_ 1.666 and newer. 20 | There is no conversion between these formats, they are inserted and 21 | extracted as-is. 22 | 23 | For audio samples, they are saved and read using simple WAV files 24 | containing only uncompressed PCM audio streams. 25 | 26 | For the various forms of graphics, the BMP (uncompressed only), GIF, 27 | PPM, and PNG footnote:[Requires libpng 1.6 or newer.] formats are 28 | supported. 29 | 30 | Unrecognized-format lumps are saved with +.lmp+ extension in the 31 | +lumps+ directory and are re-inserted as-is from there. Levels are 32 | saved in an encapsulated PWAD file; breaking them apart would not be 33 | useful for editing and all the lumps that make up a level are 34 | co-dependent on each other. 35 | 36 | Building and installing 37 | ----------------------- 38 | 39 | DeuTex uses an Autoconf+Automake build system, so its compilation and 40 | installation process is identical to most other Unix packages: 41 | 42 | ./configure 43 | make 44 | make install 45 | 46 | When building directly from the version control repository, you will 47 | need autoconf and automake installed and run the `./bootstrap` command 48 | first to generate the `./configure` file. 49 | 50 | To build and install the manpage, AsciiDoc must be installed, in 51 | particular the DocBook-based `a2x` command for transforming it into 52 | manpage format. DeuTex will still build without it, however, and the 53 | AsciiDoc source in +man/deutex.txt+ is fairly readable on its own. 54 | 55 | To build and install on ReactOS or Microsoft Windows will require an 56 | environment compatible with Unix shells and programs, such as 57 | https://cygwin.com/[Cygwin] or http://www.msys2.org/[MSYS2]. 58 | 59 | History 60 | ------- 61 | 62 | DeuTex began life as a fork of Doom Editing Utilities (known as DEU 63 | for short) by Olivier Montanuy in 1994, expunging the graphical user 64 | interface in favor of command line and scriptable usage scenarios. 65 | Originally written for DOS, its primary home is now modern Unix and 66 | Windows systems, and is a fundamental piece to building 67 | _https://freedoom.github.io/[Freedoom’s]_ IWAD files. 68 | 69 | The name comes from a play on LaTeX and in turn TeX, a popular 70 | typesetting system in academia but no technical connection to DeuTex. 71 | It is pronounced as two syllables, the first like “due” or “dew,” and 72 | the second like “tech” (not “tex”!), owing from its namesake. 73 | 74 | Copyright 75 | --------- 76 | 77 | Copyright © Olivier Montanuy, André Majorel, contributors to the 78 | DeuTex project. 79 | 80 | This program is free software; you can redistribute it and/or modify 81 | it under the terms of the GNU General Public License as published by 82 | the Free Software Foundation; either version 2 of the License, or (at 83 | your option) any later version. 84 | 85 | This program is distributed in the hope that it will be useful, but 86 | WITHOUT ANY WARRANTY; without even the implied warranty of 87 | MERCHANTABILITY or FITNESS FOR ANY PARTICULAR PURPOSE. See the GNU 88 | General Public License for more details, provided under the name 89 | +COPYING+. 90 | -------------------------------------------------------------------------------- /bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | autoreconf -i 4 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([DeuTex], [5.2.3], [https://github.com/Doom-Utils/deutex/issues]) 2 | AC_PACKAGE_URL([https://github.com/Doom-Utils/deutex]) 3 | AM_INIT_AUTOMAKE([foreign]) 4 | AC_PROG_CC 5 | 6 | AM_SILENT_RULES([yes]) 7 | 8 | AC_CHECK_PROG([ASCIIDOC], [a2x], [yes]) 9 | AC_ARG_ENABLE([man], 10 | AS_HELP_STRING([--disable-man], 11 | [Disable manpage generation]), 12 | [], [enable_man=check] 13 | ) 14 | AS_IF([test "x$enable_man" = "xyes"], 15 | AS_IF([test "x$ASCIIDOC" != "xyes"], 16 | AC_MSG_ERROR([AsciiDoc a2x program is required in order to build the manual]) 17 | ) 18 | ) 19 | AM_CONDITIONAL([ASCIIDOC], [test "x$enable_man" != "xno" && test "x$ASCIIDOC" = "xyes"]) 20 | 21 | AC_ARG_WITH([libpng], 22 | AS_HELP_STRING([--without-libpng], 23 | [Build without libpng @<:@default=check@:>@]), 24 | [], 25 | [[with_libpng=check]]) 26 | AS_IF([test "$with_libpng" != no], 27 | [PKG_CHECK_MODULES([PNG], [libpng >= 1.6.0], 28 | [AC_DEFINE([HAVE_LIBPNG], [1], [libpng installed])], 29 | [AS_IF([test "$with_libpng" != check], 30 | [AC_MSG_FAILURE([--with-libpng was given, but test for libpng failed]) 31 | ]) ]) ]) 32 | 33 | AC_CONFIG_HEADERS([config.h]) 34 | AC_CONFIG_FILES([ 35 | Makefile 36 | man/Makefile 37 | man/deutex.txt 38 | pkg.w32/config.make 39 | src/Makefile 40 | ]) 41 | AC_OUTPUT 42 | -------------------------------------------------------------------------------- /man/.gitignore: -------------------------------------------------------------------------------- 1 | *.6 2 | deutex.txt 3 | -------------------------------------------------------------------------------- /man/Makefile.am: -------------------------------------------------------------------------------- 1 | if ASCIIDOC 2 | man_MANS = @PACKAGE@.6 3 | CLEANFILES = @PACKAGE@.6 @PACKAGE@.html 4 | 5 | @PACKAGE@.6: deutex.txt 6 | a2x -f manpage deutex.txt 7 | 8 | @PACKAGE@.html: deutex.txt 9 | TZ=UTC asciidoc deutex.txt 10 | 11 | html: @PACKAGE@.html 12 | endif 13 | -------------------------------------------------------------------------------- /man/deutex.txt.in: -------------------------------------------------------------------------------- 1 | @PACKAGE@(6) 2 | ========= 3 | :doctype: manpage 4 | 5 | NAME 6 | ---- 7 | @PACKAGE@ - do things with wad files 8 | 9 | SYNOPSIS 10 | -------- 11 | *@PACKAGE@* *-?*|*-h*|*-help*|*--help* 12 | 13 | *@PACKAGE@* *--version* 14 | 15 | *@PACKAGE@* ['OPTIONS'] *-add* 'incomplete.wad' 'out.wad' 16 | 17 | *@PACKAGE@* ['OPTIONS'] *-af* 'flats.wad' 18 | 19 | *@PACKAGE@* ['OPTIONS'] *-append* 'incomplete.wad' 20 | 21 | *@PACKAGE@* ['OPTIONS'] *-as* 'sprite.wad' 22 | 23 | *@PACKAGE@* ['OPTIONS'] *-check* 'in.wad' 24 | 25 | *@PACKAGE@* ['OPTIONS'] *-debug* ['in.gif'] 26 | 27 | *@PACKAGE@* ['OPTIONS'] *-get* 'entry' ['in.wad'] 28 | 29 | *@PACKAGE@* ['OPTIONS'] *-join* 'incomplete.wad' 'in.wad' 30 | 31 | *@PACKAGE@* ['OPTIONS'] *-make* ['dirctivs.txt'] 'out.wad' 32 | 33 | *@PACKAGE@* ['OPTIONS'] *-merge* 'in.wad' 34 | 35 | *@PACKAGE@* ['OPTIONS'] *-pkgfx* ['in.wad' ['out.txt']] 36 | 37 | *@PACKAGE@* ['OPTIONS'] *-pknormal* ['in.wad' ['out.txt']] 38 | 39 | *@PACKAGE@* ['OPTIONS'] *-restore* 40 | 41 | *@PACKAGE@* ['OPTIONS'] *-usedidx* ['in.wad'] 42 | 43 | *@PACKAGE@* ['OPTIONS'] *-usedtex* ['in.wad'] 44 | 45 | *@PACKAGE@* ['OPTIONS'] *-unused* 'in.wad' 46 | 47 | *@PACKAGE@* ['OPTIONS'] *-wadir* ['in.wad'] 48 | 49 | *@PACKAGE@* ['OPTIONS'] *-xtract* 'in.wad' ['dirctivs.txt'] 50 | 51 | DESCRIPTION 52 | ----------- 53 | @PACKAGE_NAME@ is a wad composer for Doom, Heretic, Hexen and Strife. 54 | It can be used to extract the lumps of a wad and save them as 55 | individual files or the reverse, and much more. 56 | 57 | When extracting a lump to a file, it does not just copy the raw data, 58 | it converts it to an appropriate format (such as PPM for graphics, Sun 59 | audio for samples, etc.). Conversely, when it reads files for 60 | inclusion in pwads, it does the necessary conversions (for example, 61 | from PPM to Doom picture format). 62 | 63 | Decomposing a wad 64 | ~~~~~~~~~~~~~~~~~ 65 | To decompose a wad (i.e. extract its contents), use the *-extract* 66 | (a.k.a. *-xtract*) command. When decomposing a wad, @PACKAGE_NAME@ 67 | creates one file for each lump. The files are created in one of the 68 | following subdirectories of the working directory: *flats/*, *lumps/*, 69 | *musics/*, *patches/*, *sounds/*, *sprites/*, *textures/*. The 70 | decomposing process also creates a very important file, *wadinfo.txt*, 71 | which will be used later when composing. 72 | 73 | To extract the contents of the Doom II iwad, 74 | 75 | @PACKAGE@ -doom2 /path/to/doom2.wad -xtract 76 | 77 | To extract the contents of a Doom II pwad named mywad.wad, 78 | 79 | @PACKAGE@ -doom2 /path/to/doom2.wad -xtract mywad.wad 80 | 81 | To extract only the sprites, 82 | 83 | @PACKAGE@ -doom2 /path/to/doom2.wad -sprites -xtract 84 | 85 | To extract only the sounds, 86 | 87 | @PACKAGE@ -doom2 /path/to/doom2.wad -sounds -xtract 88 | 89 | Composing (building) a wad 90 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 91 | Composing is the symmetrical process. It's done with the three 92 | commands *-build*, *-create* and *-make*, that are equivalent. Using 93 | *wadinfo.txt* and the files in *flats/*, *lumps/*, *musics/*, 94 | *patches/*, *sounds/*, *sprites/* and *textures/*, @PACKAGE_NAME@ 95 | creates a new wad. 96 | 97 | To create a new pwad named mywad.wad, 98 | 99 | @PACKAGE@ -doom2 /path/to/doom2.wad -make mywad.wad 100 | 101 | To create a new iwad named mytc.wad, 102 | 103 | @PACKAGE@ -doom2 /path/to/doom2.wad -iwad -make mytc.wad 104 | 105 | Other operations 106 | ~~~~~~~~~~~~~~~~ 107 | @PACKAGE_NAME@ has many (too many?) other commands like *-join*, 108 | *-merge*, *-usedtex* etc. 109 | 110 | OPTIONS 111 | ------- 112 | Modal options not requiring an iwad 113 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 114 | *-?*, *-h*, *-help*, *--help*:: 115 | Print list of options. 116 | 117 | *-syntax*:: 118 | Print the syntax of wad creation directives. 119 | 120 | *--version*:: 121 | Print version number and exit immediately. 122 | 123 | *-unused* 'in.wad':: 124 | Find unused spaces in a wad. 125 | 126 | Modal options requiring an iwad 127 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 128 | *-add* 'in.wad' 'out.wad':: 129 | Copy sp & fl of iwad and 'in.wad' to 'out.wad'. 130 | 131 | *-af* 'flats.wad':: 132 | Append all floors/ceilings to the wad. 133 | 134 | *-append* 'io.wad':: 135 | Add sprites & flats of iwad to io.wad. 136 | 137 | *-as* 'sprite.wad':: 138 | Append all sprites to the wad. 139 | 140 | *-build*|*-create*|*-make* ['in.txt'] 'out.wad':: 141 | Make a pwad. 142 | 143 | *-check*|*-test* 'in.wad':: 144 | Check the textures. 145 | 146 | *-debug* ['file']:: 147 | Debug color conversion. 148 | 149 | *-extract*|*-xtract* ['in.wad' ['out.txt']]:: 150 | Extract some/all entries from a wad. 151 | 152 | *-get* 'entry' ['in.wad']:: 153 | Get a wad entry from main wad or in.wad. 154 | 155 | *-join* 'incomplete.wad' 'in.wad':: 156 | Append sprites & flats of Doom to a pwad. 157 | 158 | *-merge* 'in.wad':: 159 | Merge doom.wad and a pwad. 160 | 161 | *-pkgfx* ['in.wad' ['out.txt']]:: 162 | Detect identical graphics. 163 | 164 | *-pknormal* ['in.wad' ['out.txt']]:: 165 | Detect identical normal. 166 | 167 | *-restore*:: 168 | Restore doom.wad and the pwad. 169 | 170 | *-usedidx* ['in.wad']:: 171 | Color index usage statistics. 172 | 173 | *-usedtex* ['in.wad']:: 174 | List textures used in all levels. 175 | 176 | *-wadir* ['in.wad']:: 177 | List and identify entries in a wad. 178 | 179 | General options 180 | ~~~~~~~~~~~~~~~ 181 | *-overwrite*:: 182 | Overwrite all. 183 | 184 | *-dir* 'dir':: 185 | Extraction directory (default `.`). 186 | 187 | Iwad 188 | ~~~~ 189 | *-doom* 'dir':: 190 | Path to Doom iwad. 191 | 192 | *-doom2* 'dir':: 193 | Path to Doom II iwad. 194 | 195 | *-doom02* 'dir':: 196 | Path to Doom alpha 0.2 iwad. 197 | 198 | *-doom04* 'dir':: 199 | Path to Doom alpha 0.4 iwad. 200 | 201 | *-doom05* 'dir':: 202 | Path to Doom alpha 0.5 iwad. 203 | 204 | *-doompr* 'dir':: 205 | Path to Doom PR pre-beta iwad. 206 | 207 | *-heretic* 'dir':: 208 | Path to Heretic iwad. 209 | 210 | *-hexen* 'dir':: 211 | Path to Hexen iwad. 212 | 213 | *-strife* 'dir':: 214 | Path to Strife iwad. 215 | 216 | *-strife10* 'dir':: 217 | Path to Strife 1.0 iwad. 218 | 219 | Wad options 220 | ~~~~~~~~~~~ 221 | *-be*:: 222 | Assume all wads are big endian (default LE). 223 | 224 | *-deu*:: 225 | Add 64k of junk for DEU 5.21 compatibility. 226 | 227 | *-george*|*-s_end*:: 228 | Use *S_END* for sprites, not *SS_END*. 229 | 230 | *-ibe*:: 231 | Input wads are big endian (default LE). 232 | 233 | *-ile*:: 234 | Input wads are little endian (default). 235 | 236 | *-ipf* 'code':: 237 | Picture format (*alpha*, *normal*, *pr*; default normal). 238 | 239 | *-itf* 'code':: 240 | Input texture format (*nameless*, *none*, *normal*, *strife11*; 241 | default normal). 242 | 243 | *-itl* 'code':: 244 | Texture lump (*none*, *normal*, *textures*; default normal). 245 | 246 | *-iwad*:: 247 | Compose iwad, not pwad. 248 | 249 | *-le*:: 250 | Assume all wads are little endian (default). 251 | 252 | *-obe*:: 253 | Create big endian wads (default LE). 254 | 255 | *-ole*:: 256 | Create little endian wads (default). 257 | 258 | *-otf* 'code':: 259 | Output texture format (*nameless*, *none*, *normal*, *strife11*; 260 | default normal). 261 | 262 | *-pngoffsets*:: 263 | Override offsets in WADINFO with offsets contained in PNG metadata 264 | 265 | *-tf* 'code':: 266 | Texture format (*nameless*, *none*, *normal*, *strife11*; default 267 | normal). 268 | 269 | Lump selection 270 | ~~~~~~~~~~~~~~ 271 | *-flats*:: 272 | Select flats. 273 | 274 | *-graphics*:: 275 | Select graphics. 276 | 277 | *-levels*:: 278 | Select levels. 279 | 280 | *-lumps*:: 281 | Select lumps. 282 | 283 | *-musics*:: 284 | Select musics. 285 | 286 | *-patches*:: 287 | Select patches. 288 | 289 | *-scripts*:: 290 | Select Strife scripts. 291 | 292 | *-sneas*:: 293 | Select sneas (sneaps and sneats). 294 | 295 | *-sneaps*:: 296 | Select sneaps. 297 | 298 | *-sneats*:: 299 | Select sneats. 300 | 301 | *-sounds*:: 302 | Select sounds. 303 | 304 | *-sprites*:: 305 | Select sprites. 306 | 307 | *-textures*:: 308 | Select textures. 309 | 310 | Graphics 311 | ~~~~~~~~ 312 | *-bmp*:: 313 | Save pictures as BMP (*.bmp*). 314 | 315 | *-png*:: 316 | Save pictures as PNG (*.png*). Default format. 317 | 318 | *-gif*:: 319 | Save pictures as GIF (*.gif*). 320 | 321 | *-ppm*:: 322 | Save pictures as rawbits PPM (P6, *.ppm*). 323 | 324 | *-rgb* 'r' 'g' 'b':: 325 | Specify the transparent colour (default 0 47 47). 326 | 327 | Sound 328 | ~~~~~ 329 | *-rate* 'code':: 330 | Policy for != 11025 Hz (*reject*, *force*, *warn*, *accept*; default warn). 331 | 332 | Reporting 333 | ~~~~~~~~~ 334 | *-di* 'name':: 335 | Debug identification of entry. 336 | 337 | *-v0*|*-v1*|*-v2*|*-v3*|*-v4*|*-v5*:: 338 | Set verbosity level, default 2. 339 | 340 | DIAGNOSTICS 341 | ----------- 342 | All messages are identified by a unique code. Some messages are 343 | identical; the code is useful to distinguish them. All codes have 344 | four characters: two letters and two digits. The letters identify the 345 | part of the code where the message comes from, the digits give the 346 | message number within that area. In general, numbers are assigned so 347 | that messages that come from parts of the code that are executed 348 | earlier have lower numbers. 349 | 350 | FILES 351 | ----- 352 | 'dir'*/flats/*:: 353 | When extracting, flats are saved to this directory. When composing, 354 | flats are read from this directory. 355 | 356 | 'dir'*/graphics/*:: 357 | When extracting, graphics are saved to this directory. When 358 | composing, graphics are read from this directory. 359 | 360 | 'dir'*/levels/*:: 361 | When extracting, levels are saved to this directory. When composing, 362 | levels are read from this directory. 363 | 364 | 'dir'*/lumps/*:: 365 | When extracting, lumps are saved to this directory. When composing, 366 | lumps are read from this directory. 367 | 368 | 'dir'*/musics/*:: 369 | When extracting, musics are saved to this directory. When composing, 370 | musics are read from this directory. 371 | 372 | 'dir'*/patches/*:: 373 | When extracting, patches are saved to this directory. When composing, 374 | patches are read from this directory. 375 | 376 | 'dir'*/scripts/*:: 377 | When extracting, Strife scripts are saved to this directory. When 378 | composing, Strife scripts are read from this directory. 379 | 380 | 'dir'*/sneaps/*:: 381 | When extracting, Doom alpha sneaps are saved to this directory. When 382 | composing, Doom alpha sneaps are read from this directory. 383 | 384 | 'dir'*/sneats/*:: 385 | When extracting, Doom alpha sneats are saved to this directory. When 386 | composing, Doom alpha sneats are read from this directory. 387 | 388 | 'dir'*/sounds/*:: 389 | When extracting, sounds are saved to this directory. When composing, 390 | sounds are read from this directory. 391 | 392 | 'dir'*/sprites/*:: 393 | When extracting, sprites are saved to this directory. When composing, 394 | sprites are read from this directory. 395 | 396 | 'dir'*/textures/texture1.txt*:: 397 | The *TEXTURE1* lump (all but Doom alpha 0.4 and 0.5). 398 | 399 | 'dir'*/textures/texture2.txt*:: 400 | The *TEXTURE2* lump (all commercial IWADs except Doom 2). 401 | 402 | 'dir'*/textures/textures.txt*:: 403 | The *TEXTURES* lump (Doom alpha 0.4 and 0.5). 404 | 405 | 'dir'*/tx_start/*:: 406 | Special texture directory for certain engines such as ZDoom. 407 | Specifying a positive integer after the name in wadinfo.txt causes no 408 | format conversion to be performed (eg, PNGs and BMPs remain as PNGs 409 | and BMPs in the WAD), otherwise an attempt to convert to Doom’s patch 410 | format is done. 411 | 412 | 'dir'*/wadinfo.txt*:: 413 | The default master file. 414 | 415 | ENVIRONMENT 416 | ----------- 417 | *DOOMWADDIR*:: 418 | The directory where the iwad resides. The value of this environment 419 | variable is overridden by *-main*, *-doom* and friends. 420 | 421 | COPYRIGHT 422 | --------- 423 | @PACKAGE_NAME@ is copyright © 1994-1995 Olivier Montanuy, copyright © 424 | 1999-2005 André Majorel, copyright © 2006-2021 contributors to 425 | @PACKAGE_NAME@. 426 | 427 | Most of this program is under the GNU General Public License version 428 | 2, but some of it is available under other licenses. This program is 429 | distributed in the hope that it will be useful, but WITHOUT ANY 430 | WARRANTY; without even the implied warranty of MERCHANTABILITY or 431 | FITNESS FOR A PARTICULAR PURPOSE. See LICENSE for specific 432 | information and copyright notices. All trademarks are property of 433 | their owners. 434 | 435 | AUTHORS 436 | ------- 437 | The original author of @PACKAGE_NAME@ is Olivier Montanuy. From 1994 438 | to 1996, @PACKAGE_NAME@ was maintained by Olivier Montanuy with help 439 | from Per Allansson, James Bonfield, Sharon Bowles, Mark Mathews, and 440 | Chuck Rossi. The original manual was written by Kevin McGrail. 441 | 442 | From version 4.0 (1999) through 4.4.902 (2005), the maintainer was 443 | André Majorel (http://www.teaser.fr/~amajorel). 444 | 445 | The project has since been maintained by a loose collaboration of 446 | authors primarily as part of the Debian project and Freedoom project. 447 | They include Jon Dowland, Simon Howard, Mike Swanson, RjY, Ayub 448 | Ahmed, and Nick Zatkovich. 449 | 450 | REPORTING BUGS 451 | -------------- 452 | Please report bugs to the issue tracker at 453 | https://github.com/Doom-Utils/deutex. 454 | -------------------------------------------------------------------------------- /pkg.w32/.gitignore: -------------------------------------------------------------------------------- 1 | !Makefile 2 | *.zip 3 | config.make 4 | staging 5 | -------------------------------------------------------------------------------- /pkg.w32/Makefile: -------------------------------------------------------------------------------- 1 | include config.make 2 | 3 | DOC_HTML_FILES = ../NEWS.html ../README.html ../man/$(PACKAGE).html 4 | DOC_TXT_FILES = ../AUTHORS ../COPYING 5 | ZIP=$(PACKAGE)-$(PACKAGE_VERSION)_w32.zip 6 | 7 | all: $(ZIP) 8 | 9 | clean: 10 | rm -rf staging $(ZIP) 11 | 12 | $(ZIP): staging 13 | zip -Xjr $@ $ 0: 147 | filename = to_process.pop() 148 | for dll in file_dependencies(filename, objdump): 149 | try: 150 | dll = find_dll(dll, dll_paths) 151 | if dll not in result: 152 | result |= {dll} 153 | to_process |= {dll} 154 | except IOError as e: 155 | missing |= {dll} 156 | 157 | return result, missing 158 | 159 | 160 | def get_dll_path(): 161 | """Examine command line arguments and determine the DLL search path. 162 | 163 | If the --path argument is provided, paths from this are added. 164 | Furthermore, if --ldflags is provided, this is interpreted as a list of 165 | linker flags and the -L paths are used to find associated paths that are 166 | likely to contain DLLs, with the assumption that autotools usually 167 | installs DLLs to ${prefix}/bin when installing Unix-style libraries into 168 | ${prefix}/lib. 169 | 170 | Returns: 171 | List of filesystem paths to check for DLLs. 172 | """ 173 | result = set(args.dll_path) 174 | 175 | if args.ldflags != "": 176 | for arg in shlex.split(args.ldflags): 177 | if arg.startswith("-L"): 178 | prefix, libdir = os.path.split(arg[2:]) 179 | if libdir != "lib": 180 | continue 181 | bindir = os.path.join(prefix, "bin") 182 | if os.path.exists(bindir): 183 | result |= {bindir} 184 | 185 | return list(result) 186 | 187 | 188 | args = parser.parse_args() 189 | 190 | dll_path = get_dll_path() 191 | dll_files, missing = all_dependencies(args.source, args.objdump, dll_path) 192 | 193 | # Exit with failure if DLLs are missing. 194 | if missing: 195 | sys.stderr.write("Missing DLLs not found in path %s:\n" % (dll_path,)) 196 | for filename in missing: 197 | sys.stderr.write("\t%s\n" % filename) 198 | sys.exit(1) 199 | 200 | # Destination may be a full path (rename) or may be a directory to copy into: 201 | # cp foo.exe bar/baz.exe 202 | # cp foo.exe bar/ 203 | if os.path.isdir(args.destination): 204 | dest_dir = args.destination 205 | else: 206 | dest_dir = os.path.dirname(args.destination) 207 | 208 | # Copy .exe and DLLs. 209 | shutil.copy(args.source, args.destination) 210 | for filename in dll_files: 211 | shutil.copy(filename, dest_dir) 212 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.exe 3 | deutex 4 | .deps 5 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | bin_PROGRAMS = deutex 2 | deutex_SOURCES = color.c color.h compose.c deutex.c deutex.h \ 3 | endianio.c endianio.h endianm.c endianm.h extract.c extract.h ident.c \ 4 | ident.h listdir.c lists.c lists.h lzw.c merge.c merge.h mkwad.c \ 5 | mkwad.h png_tools.c png_tools.h picture.c picture.h sound.c sound.h \ 6 | sscript.c sscript.h text.c text.h texture.c texture.h tools.c tools.h \ 7 | usedidx.c usedidx.h wadio.c wadio.h 8 | 9 | deutex_LDADD = ${PNG_LIBS} 10 | AM_CPPFLAGS = ${PNG_CFLAGS} 11 | -------------------------------------------------------------------------------- /src/color.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ® Olivier Montanuy, 3 | André Majorel, 4 | contributors to the DeuTex project. 5 | 6 | DeuTex incorporates code derived from DEU 5.21 that was put in the 7 | public domain in 1994 by Raphaël Quinet and Brendon Wyber. 8 | 9 | SPDX-License-Identifier: GPL-2.0-or-later 10 | */ 11 | 12 | #include "deutex.h" 13 | #include "tools.h" 14 | #include "color.h" 15 | 16 | /* 17 | ** hash table 18 | ** 19 | */ 20 | uint8_t COLindex(uint8_t R, uint8_t G, uint8_t B, uint8_t index); 21 | static uint8_t COLpalMatch(uint8_t R, uint8_t G, uint8_t B); 22 | 23 | static struct PIXEL *COLpal; /* The game palette (comes from PLAYPAL) */ 24 | static struct PIXEL *COLpalAlt = NULL; /* Alternate palette (TITLEPAL) */ 25 | static struct PIXEL COLinv; 26 | static uint8_t COLinvisib; 27 | static bool COLok = false; 28 | 29 | /* 30 | * pixel_cmp 31 | * Compare two PIXEL structures à la memcmp() 32 | */ 33 | static int pixel_cmp(const void *pixel1, const void *pixel2) 34 | { 35 | const struct PIXEL *const p1 = (const struct PIXEL *) pixel1; 36 | const struct PIXEL *const p2 = (const struct PIXEL *) pixel2; 37 | if (p1->R != p2->R) 38 | return p1->R - p2->R; 39 | if (p1->G != p2->G) 40 | return p1->G - p2->G; 41 | if (p1->B != p2->B) 42 | return p1->B - p2->B; 43 | return 0; 44 | 45 | } 46 | 47 | const int COLsame = 3; 48 | 49 | /* 50 | * COLdiff - compute the distance between two colours 51 | * 52 | * Return a number between 0 (closest) and 24384 (farthest). 53 | */ 54 | int16_t COLdiff(uint8_t R, uint8_t G, uint8_t B, uint8_t idx) 55 | { 56 | register struct PIXEL *pixel = &COLpal[(int16_t) (idx & 0xFF)]; 57 | register int16_t d; /*signed */ 58 | register int16_t e = 0; 59 | d = (((int16_t) R) & 0xFF) - (((int16_t) pixel->R) & 0xFF); 60 | d >>= 1; 61 | e += d * d; 62 | d = (((int16_t) G) & 0xFF) - (((int16_t) pixel->G) & 0xFF); 63 | d >>= 1; 64 | e += d * d; 65 | d = (((int16_t) B) & 0xFF) - (((int16_t) pixel->B) & 0xFF); 66 | d >>= 1; 67 | e += d * d; 68 | if (e < 0) 69 | return 0x7FFF; 70 | return e; 71 | } 72 | 73 | static uint8_t COLpalMatch(uint8_t R, uint8_t G, uint8_t B) 74 | { 75 | int16_t i, test, min = 0x7FFF; 76 | uint8_t idxmin = '\0'; 77 | if (!COLok) 78 | Bug("PL24", "COLok"); 79 | for (i = 0; i < 256; i++) { 80 | if ((uint8_t) i != COLinvisib) { 81 | test = COLdiff(R, G, B, (uint8_t) i); 82 | if (test < min) { 83 | min = test; 84 | idxmin = (uint8_t) i; 85 | } 86 | if (min < COLsame) { 87 | break; 88 | } 89 | } 90 | } 91 | return idxmin; 92 | } 93 | 94 | /* Choose only 10,11...16 */ 95 | #define POWER 12 96 | const int16_t HashP2 = POWER; /* 10=1024 */ 97 | const int16_t HashSz = 1 << POWER; /* 1<< HashP2 */ 98 | const int16_t HashMask = (1 << POWER) - 1; /* HashSz-1 */ 99 | /*const int16_t HashStop = -1;*/ 100 | static uint8_t *COLhash; /*hash table */ 101 | /*static int16_t *COLnext;*/ 102 | 103 | int16_t Hash(uint8_t r, uint8_t g, uint8_t b) 104 | { 105 | int res; 106 | uint8_t R = r & 0xFC, G = g & 0xFC, B = b & 0xFC; 107 | res = (((R << 3) ^ G) << 2) ^ B; 108 | res = (res << 3) + (R & 0xC3) + (G & 0x61) + (~B & 0x98); 109 | res = (res << 5) + (R & 0x3B) + (~G & 0x95) + (B & 0x33); 110 | res ^= res >> 8; 111 | res &= HashMask; 112 | return (int16_t) res; 113 | } 114 | 115 | /*original colors*/ 116 | static void COLputColHash(int16_t index, uint8_t R, uint8_t G, uint8_t B) 117 | { 118 | int16_t count, idx, nextidx; 119 | idx = Hash(R, G, B); 120 | for (count = 0; count < 8; count++) { 121 | nextidx = (idx + count) & HashMask; 122 | if (COLhash[nextidx] == COLinvisib) { 123 | COLhash[nextidx] = (uint8_t) (index & 0xFF); 124 | return; 125 | } 126 | } 127 | Bug("PL07", "Can't hash Doom pal"); 128 | } 129 | 130 | /*new colors, with matching*/ 131 | static uint8_t COLgetIndexHash(uint8_t R, uint8_t G, uint8_t B) 132 | { 133 | int16_t idx, nextidx, count; 134 | uint8_t res; 135 | idx = Hash(R, G, B); 136 | for (count = 0; count < 8; count++) { 137 | nextidx = (idx + count) & HashMask; 138 | res = COLhash[nextidx]; 139 | if (res == COLinvisib) { /*free */ 140 | COLhash[nextidx] = res = COLpalMatch(R, G, B); 141 | return res; 142 | } else if (COLdiff(R, G, B, res) < COLsame) { 143 | return res; 144 | } 145 | } 146 | /*no good solution. slow match */ 147 | return COLpalMatch(R, G, B); 148 | } 149 | 150 | /* 151 | * Normal palette (PLAYPAL) 152 | */ 153 | void COLinit(uint8_t invR, uint8_t invG, uint8_t invB, char *Colors, 154 | int16_t Colsz, const char *pathname, const char *lumpname) 155 | { 156 | int16_t i; 157 | const char *name = NULL; 158 | /*int16_t R,G,B; */ 159 | if (COLok) 160 | Bug("PL02", "COLok"); 161 | if (Colsz < 256 * sizeof(struct PIXEL)) { 162 | if (lumpname == NULL) { 163 | ProgError("PL03", "%s: wrong size for PLAYPAL", 164 | fname(pathname)); 165 | } else { 166 | ProgError("PL04", "%s: %s: wrong size for PLAYPAL", 167 | fname(pathname), lump_name(lumpname)); 168 | } 169 | } 170 | COLok = true; 171 | COLpal = (struct PIXEL *) Malloc(256 * sizeof(struct PIXEL)); 172 | for (i = 0; i < NCOLOURS; i++) { 173 | COLpal[i].R = Colors[i * 3 + 0]; 174 | COLpal[i].G = Colors[i * 3 + 1]; 175 | COLpal[i].B = Colors[i * 3 + 2]; 176 | } 177 | if (COLpal[0].R == 0 && COLpal[0].G == 0 && COLpal[0].B == 0 178 | && COLpal[0xf7].R == 0 && COLpal[0xf7].G == 0 179 | && COLpal[0xf7].B == 0) { 180 | i = 0xf7; 181 | name = "Doom"; 182 | } else if (COLpal[35].R == 255 && COLpal[35].G == 255 183 | && COLpal[35].B == 255 && COLpal[255].R == 255 184 | && COLpal[255].G == 255 && COLpal[255].B == 255) { 185 | i = 0xff; 186 | name = "Heretic"; 187 | } else if (COLpal[33].R == 29 && COLpal[33].G == 32 188 | && COLpal[33].B == 29 && COLpal[255].R == 255 189 | && COLpal[255].G == 255 && COLpal[255].B == 255) { 190 | i = 0xff; 191 | name = "Hexen"; 192 | } else if (COLpal[0].R == 0 && COLpal[0].G == 0 && COLpal[0].B == 0 193 | && COLpal[240].R == 0 && COLpal[240].G == 0 194 | && COLpal[240].B == 0) { 195 | i = 0xf0; 196 | name = "Strife"; 197 | } else { 198 | i = 0xff; 199 | name = NULL; 200 | } 201 | /* 202 | ** correction to doom palette 203 | */ 204 | COLinvisib = (uint8_t) (i & 0xFF); 205 | Info("PL05", "Palette is %s", name ? name : "(unknown)"); 206 | if (name == NULL) { 207 | Warning("PL06", 208 | "Unknown palette, using colour 0xff as transparent colour"); 209 | Warning("PL06", "Some graphics may appear moth-eaten"); 210 | } 211 | COLinv.R = COLpal[i].R = invR; 212 | COLinv.G = COLpal[i].G = invG; 213 | COLinv.B = COLpal[i].B = invB; 214 | 215 | /* Init hash table. 216 | We take special care of hashing only unique RGB triplets. 217 | This precaution is unnecessary for Doom, Heretic, Hexen 218 | and Strife but Doom alpha O.2, 0.4 and 0.5 have a PLAYPAL 219 | that contains many duplicates that would fill the hash 220 | table with useless data. */ 221 | { 222 | struct PIXEL *unique = Malloc(NCOLOURS * sizeof *unique); 223 | for (i = 0; i < NCOLOURS; i++) 224 | unique[i] = COLpal[i]; 225 | qsort(unique, NCOLOURS, sizeof *unique, pixel_cmp); 226 | COLhash = (uint8_t *) Malloc(HashSz); 227 | Memset(COLhash, COLinvisib, HashSz); /*clear hash table */ 228 | for (i = 0; i < NCOLOURS; i++) { 229 | if ((uint8_t) i != COLinvisib 230 | && (i == 0 || pixel_cmp(unique + i, unique + i - 1) != 0)) 231 | COLputColHash(i, unique[i].R, unique[i].G, unique[i].B); 232 | } 233 | free(unique); 234 | } 235 | } 236 | 237 | void COLfree(void) 238 | { 239 | if (!COLok) 240 | Bug("PL99", "COLok"); 241 | COLok = false; 242 | free(COLpal); 243 | free(COLhash); 244 | if (COLpalAlt != NULL) 245 | free(COLpalAlt); 246 | } 247 | 248 | uint8_t COLinvisible(void) 249 | { 250 | if (!COLok) 251 | Bug("PL27", "COLok"); 252 | return COLinvisib; 253 | } 254 | 255 | struct PIXEL *COLdoomPalet(void) 256 | { 257 | if (!COLok) 258 | Bug("PL20", "COLok"); 259 | return COLpal; 260 | } 261 | 262 | uint8_t COLindex(uint8_t R, uint8_t G, uint8_t B, uint8_t index) 263 | { 264 | int16_t i; 265 | if (!COLok) 266 | Bug("PL23", "COLok"); 267 | /*check for invisible color */ 268 | if (R == COLinv.R && G == COLinv.G && B == COLinv.B) 269 | return COLinvisib; 270 | /*check for DOOM palette */ 271 | i = ((int16_t) index) & 0xFF; 272 | if (R == COLpal[i].R) 273 | if (G == COLpal[i].G) 274 | if (B == COLpal[i].B) 275 | return index; 276 | /*else, check hash palette */ 277 | i = (int16_t) COLgetIndexHash(R, G, B); 278 | return (uint8_t) i; 279 | } 280 | 281 | /* 282 | * Alternate palette (TITLEPAL) 283 | */ 284 | static char *titlepal_data = NULL; 285 | static size_t titlepal_size = 0; 286 | 287 | void COLinitAlt(char *_titlepal_data, int32_t _titlepal_size) 288 | { 289 | titlepal_data = _titlepal_data; 290 | titlepal_size = _titlepal_size; 291 | } 292 | 293 | struct PIXEL *COLaltPalet(void) 294 | { 295 | if (COLpalAlt != NULL) 296 | return COLpalAlt; 297 | 298 | /* What follows is done only once : */ 299 | if (titlepal_data == NULL) { 300 | int n; 301 | 302 | Warning("PL11", "TITLEPAL not found, using PLAYPAL instead"); 303 | COLpalAlt = (struct PIXEL *) Malloc(NCOLOURS * sizeof *COLpalAlt); 304 | for (n = 0; n < NCOLOURS; n++) 305 | COLpalAlt[n] = COLpal[n]; 306 | } else { 307 | struct PIXEL *p; 308 | struct PIXEL *pmax; 309 | const unsigned char *d = (const unsigned char *) titlepal_data; 310 | const unsigned char *dmax = d + titlepal_size; 311 | 312 | if (titlepal_size < 3 * NCOLOURS) 313 | Warning("PL13", "TITLEPAL too short (%ld), filling with black", 314 | (long) titlepal_size); 315 | COLpalAlt = (struct PIXEL *) Malloc(NCOLOURS * sizeof *COLpalAlt); 316 | /* Copy the contents of TITLEPAL into COLpalAlt */ 317 | for (p = COLpalAlt, pmax = p + NCOLOURS; p < pmax; p++) { 318 | p->R = d < dmax ? *d++ : 0; 319 | p->G = d < dmax ? *d++ : 0; 320 | p->B = d < dmax ? *d++ : 0; 321 | } 322 | free(titlepal_data); 323 | titlepal_data = NULL; /* Paranoia */ 324 | } 325 | 326 | return COLpalAlt; 327 | } 328 | -------------------------------------------------------------------------------- /src/color.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ® Olivier Montanuy, 3 | André Majorel, 4 | contributors to the DeuTex project. 5 | 6 | DeuTex incorporates code derived from DEU 5.21 that was put in the 7 | public domain in 1994 by Raphaël Quinet and Brendon Wyber. 8 | 9 | SPDX-License-Identifier: GPL-2.0-or-later 10 | */ 11 | 12 | /*init colors before any operation*/ 13 | void COLinit(uint8_t invR, uint8_t invG, uint8_t invB, char *Colors, 14 | int16_t Colsz, const char *pathname, const char *lumpname); 15 | void COLinitAlt(char *_titlepal_data, int32_t _titlepal_size); 16 | void COLfree(void); 17 | 18 | /*cross reference for picture.c only*/ 19 | struct PIXEL { 20 | uint8_t R; 21 | uint8_t G; 22 | uint8_t B; 23 | }; 24 | uint8_t COLindex(uint8_t R, uint8_t G, uint8_t B, uint8_t idx); 25 | uint8_t COLinvisible(void); 26 | struct PIXEL *COLdoomPalet(void); 27 | struct PIXEL *COLaltPalet(void); 28 | /*end of cross reference for picture.c*/ 29 | -------------------------------------------------------------------------------- /src/deutex.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ® Olivier Montanuy, 3 | André Majorel, 4 | contributors to the DeuTex project. 5 | 6 | DeuTex incorporates code derived from DEU 5.21 that was put in the 7 | public domain in 1994 by Raphaël Quinet and Brendon Wyber. 8 | 9 | SPDX-License-Identifier: GPL-2.0-or-later 10 | */ 11 | #ifndef DEUTEX_H 12 | #define DEUTEX_H 13 | 14 | #include "config.h" 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | /* fopen() modes */ 23 | #define FOPEN_RB "rb" 24 | #define FOPEN_RBP "rb+" 25 | #define FOPEN_WB "wb" 26 | #define FOPEN_RT "r" 27 | #define FOPEN_WT "w" 28 | #define FOPEN_AT "a" 29 | #define FOPEN_AB "a" 30 | 31 | /* Number of bytes to read from or write to a file */ 32 | typedef unsigned long iolen_t; 33 | 34 | #define MEMORYCACHE (0x8000L) 35 | /* steps to increase size of WAD directory*/ 36 | #define MAXPWADDIR (128) 37 | /*Add 64000 bytes of pure junk, because of a bug in DEU5.21 */ 38 | #define MAXJUNK64 (1000) 39 | /*special value, means int is not valid*/ 40 | #define INVALIDINT (-6666) 41 | /*indicate an extern WAD entry*/ 42 | #define EXTERNAL (0x80000000L) 43 | 44 | /* Wad magic numbers */ 45 | #define IWADMAGIC 0x5749 /* little-endian 16-bit int for "IW" */ 46 | #define PWADMAGIC 0x5750 /* little-endian 16-bit int for "PW" */ 47 | #define WADMAGIC 0x4441 /* little-endian 16-bit int for "AD" */ 48 | 49 | /*type of WAD files. correspond to 1st half of name*/ 50 | typedef int16_t WADTYPE; 51 | #define IWAD (IWADMAGIC) 52 | #define PWAD (PWADMAGIC) 53 | 54 | /* Number of entries in the game palette */ 55 | #define NCOLOURS 256 56 | 57 | /* Entry selection Bits*/ 58 | typedef int16_t NTRYB; 59 | #define BLEVEL (0x0001) 60 | #define BLUMP (0x0002) 61 | #define BSOUND (0x0004) 62 | #define BTEXTUR (0x0008) 63 | #define BGRAPHIC (0x0010) 64 | #define BSPRITE (0x0020) 65 | #define BPATCH (0x0040) 66 | #define BFLAT (0x0080) 67 | #define BMUSIC (0x0100) 68 | #define BSNEAP (0x0200) 69 | #define BSNEAT (0x0400) 70 | #define BSCRIPT (0x0800) 71 | #define BSNEA (BSNEAP|BSNEAT) 72 | #define BALL (0x7FFF) 73 | 74 | typedef int16_t SNDTYPE; 75 | #define SNDNONE (0) 76 | #define SNDWAV (2) 77 | typedef int16_t IMGTYPE; 78 | #define PICNONE (0) 79 | #define PICBMP (1) 80 | #define PICGIF (2) 81 | #define PICPPM (3) 82 | #define PICWAD (5) 83 | #define PICPNG (6) 84 | #define PICJPEG (7) 85 | 86 | /*wad directory*/ 87 | struct WADDIR { /*same as in doom */ 88 | int32_t start; /*start of entry */ 89 | int32_t size; /*size of entry */ 90 | char name[8]; /*name of entry */ 91 | }; 92 | 93 | struct WADINFO { 94 | int32_t ntry; /*entries in dir */ 95 | int32_t dirpos; /*position of dir */ 96 | struct WADDIR *dir; /*directory */ 97 | int32_t maxdir; /*max nb of entry in dir */ 98 | int32_t wposit; /*position for write */ 99 | int32_t maxpos; /*farther referenced byte in WAD */ 100 | FILE *fd; /* File pointer */ 101 | char *filename; /* Pointer on block malloc'd by WADRopen*() and 102 | free'd by WADRclose() and containing the name 103 | of the file. */ 104 | char ok; /*security ok&1=read ok&2=write */ 105 | }; 106 | 107 | typedef int16_t PICTYPE; 108 | #define PGRAPH 0x02 109 | #define PWEAPN 0x04 110 | #define PSPRIT 0x06 111 | #define PPATCH 0x08 112 | #define PFLAT 0x0a 113 | #define PLUMP 0x0c 114 | #define PSNEAP 0x0d 115 | #define PSNEAT 0x0e 116 | 117 | typedef int16_t ENTRY; 118 | #define EMASK 0xFF00 119 | #define EVOID 0x0000 120 | #define ELEVEL 0x0100 121 | #define EMAP 0x0200 /*Levels (doom1) and Maps(Doom2) */ 122 | #define ELUMP 0x0300 123 | #define ETEXTUR 0x0400 124 | #define EPNAME 0x0500 /*textures */ 125 | #define ESOUND 0x0600 126 | #define ESNDPC 0x0601 127 | #define ESNDWAV 0x0602 128 | #define EGRAPHIC 0x0700 129 | #define ESPRITE 0x0800 130 | #define EPATCH 0x0900 131 | #define EPATCH1 0x0901 132 | #define EPATCH2 0x0902 133 | #define EPATCH3 0x0903 134 | #define EFLAT 0x0A00 135 | #define EFLAT1 0x0A01 136 | #define EFLAT2 0x0A02 137 | #define EFLAT3 0x0A03 138 | #define EMUSIC 0x0B00 139 | #define EMUS 0x0B01 140 | #define EMIDI 0x0B02 141 | #define ETXSTART 0x0C00 142 | #define EDATA 0x1000 143 | #define ESNEA 0x2000 144 | #define ESNEAP 0x2001 /* Snea using PLAYPAL */ 145 | #define ESNEAT 0x2002 /* Snea using TITLEPAL */ 146 | #define ESSCRIPT 0x3000 /* Strife script (SCRIPTnn) */ 147 | #define EZZZZ 0x7F00 /*unidentified entries */ 148 | 149 | void CMPOmakePWAD(const char *doomwad, WADTYPE type, const char *PWADname, 150 | const char *DataDir, const char *texin, NTRYB select, 151 | char trnR, char trnG, char trnB, bool George); 152 | void EXE2list(FILE * out, char *doomexe, int32_t start, int16_t thres); 153 | void EXEsubstit(const char *texin, const char *doomexe, int32_t start, 154 | int16_t thres); 155 | 156 | void XTRlistDir(const char *doomwad, const char *wadin, NTRYB select); 157 | 158 | void XTRvoidSpacesInWAD(const char *wadin); 159 | 160 | void XTRcompakWAD(const char *DataDir, const char *wadin, const char 161 | *texout, bool ShowIdx); 162 | void XTRstructureTest(const char *doomwad, const char *wadin); 163 | void XTRtextureUsed(const char *wadin); 164 | 165 | /* 166 | * Types defined elsewhere 167 | */ 168 | struct cusage_s; 169 | typedef struct cusage_s cusage_t; 170 | 171 | /* 172 | * Misc globals, set by command line arguments 173 | */ 174 | typedef enum { PF_NORMAL, PF_ALPHA, PF_PR } picture_format_t; 175 | typedef enum { TF_NORMAL, TF_NAMELESS, TF_NONE, 176 | TF_STRIFE11 177 | } texture_format_t; 178 | typedef enum { TL_NORMAL, TL_TEXTURES, TL_NONE } texture_lump_t; 179 | typedef enum { RP_REJECT, RP_FORCE, RP_WARN, RP_ACCEPT } rate_policy_t; 180 | typedef enum { CLOBBER_YES, CLOBBER_NO, CLOBBER_ASK } clobber_t; 181 | extern picture_format_t picture_format; 182 | extern texture_format_t input_texture_format; 183 | extern texture_format_t output_texture_format; 184 | extern texture_lump_t texture_lump; 185 | extern rate_policy_t rate_policy; 186 | extern clobber_t clobber; 187 | extern bool use_png_offsets; 188 | extern const char *debug_ident; 189 | extern const char *palette_lump; 190 | 191 | #endif //DEUTEX_H 192 | -------------------------------------------------------------------------------- /src/endianio.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ® André Majorel, 3 | contributors to the DeuTex project. 4 | 5 | Those functions allow to read little-endian and big-endian integers 6 | from a file regardless of the endianness of the CPU. 7 | 8 | SPDX-License-Identifier: LGPL-2.1-or-later 9 | */ 10 | 11 | #include "deutex.h" 12 | #include "endianio.h" 13 | 14 | /* 15 | * fread_i16_be 16 | * Read a big-endian 16-bit signed integer from file . 17 | * Returns 0 on success, != 0 on failure. 18 | */ 19 | int fread_i16_be(FILE * fd, int16_t * buf) 20 | { 21 | *buf = (getc(fd) << 8) 22 | | getc(fd); 23 | return feof(fd) || ferror(fd); 24 | } 25 | 26 | /* 27 | * fread_i16_le 28 | * Read a little-endian 16-bit signed integer from file . 29 | * Returns 0 on success, != 0 on failure. 30 | */ 31 | int fread_i16_le(FILE * fd, int16_t * buf) 32 | { 33 | *buf = getc(fd) 34 | | (getc(fd) << 8); 35 | return feof(fd) || ferror(fd); 36 | } 37 | 38 | /* 39 | * fread_i32_be 40 | * Read a big-endian 32-bit signed integer from file . 41 | * Returns 0 on success, != 0 on failure. 42 | */ 43 | int fread_i32_be(FILE * fd, int32_t * buf) 44 | { 45 | *buf = ((int32_t) getc(fd) << 24) 46 | | ((int32_t) getc(fd) << 16) 47 | | ((uint16_t) getc(fd) << 8) 48 | | getc(fd); 49 | return feof(fd) || ferror(fd); 50 | } 51 | 52 | /* 53 | * fread_i32_le 54 | * Read a little-endian 32-bit signed integer from file . 55 | * Returns 0 on success, != 0 on failure. 56 | */ 57 | int fread_i32_le(FILE * fd, int32_t * buf) 58 | { 59 | *buf = getc(fd) 60 | | ((uint16_t) getc(fd) << 8) 61 | | ((int32_t) getc(fd) << 16) 62 | | ((int32_t) getc(fd) << 24); 63 | return feof(fd) || ferror(fd); 64 | } 65 | 66 | /* 67 | * fread_u16_le 68 | * Read a little-endian 16-bit unsigned integer from file . 69 | * Returns 0 on success, != 0 on failure. 70 | */ 71 | int fread_u16_le(FILE * fd, uint16_t * buf) 72 | { 73 | *buf = getc(fd) 74 | | (getc(fd) << 8); 75 | return feof(fd) || ferror(fd); 76 | } 77 | 78 | /* 79 | * fwrite_i16_le 80 | * Write a little-endian 16-bit signed integer to file . 81 | * Returns 0 on success, != 0 on failure. 82 | */ 83 | int fwrite_i16_le(FILE * fd, int16_t buf) 84 | { 85 | putc(buf & 0xff, fd); 86 | putc((buf >> 8) & 0xff, fd); 87 | return feof(fd) || ferror(fd); 88 | } 89 | 90 | /* 91 | * fwrite_i16_be 92 | * Write a big-endian 16-bit signed integer to file . 93 | * Returns 0 on success, != 0 on failure. 94 | */ 95 | int fwrite_i16_be(FILE * fd, int16_t buf) 96 | { 97 | putc((buf >> 8) & 0xff, fd); 98 | putc(buf & 0xff, fd); 99 | return feof(fd) || ferror(fd); 100 | } 101 | 102 | /* 103 | * fwrite_i32_le 104 | * Write a little-endian 32-bit signed integer to file . 105 | * Returns 0 on success, != 0 on failure. 106 | */ 107 | int fwrite_i32_le(FILE * fd, int32_t buf) 108 | { 109 | putc(buf & 0xff, fd); 110 | putc((buf >> 8) & 0xff, fd); 111 | putc((buf >> 16) & 0xff, fd); 112 | putc((buf >> 24) & 0xff, fd); 113 | return feof(fd) || ferror(fd); 114 | } 115 | 116 | /* 117 | * fwrite_i32_be 118 | * Write a big-endian 32-bit signed integer to file . 119 | * Returns 0 on success, != 0 on failure. 120 | */ 121 | int fwrite_i32_be(FILE * fd, int32_t buf) 122 | { 123 | putc((buf >> 24) & 0xff, fd); 124 | putc((buf >> 16) & 0xff, fd); 125 | putc((buf >> 8) & 0xff, fd); 126 | putc(buf & 0xff, fd); 127 | return feof(fd) || ferror(fd); 128 | } 129 | 130 | /* 131 | * fwrite_u16_le 132 | * Write a little-endian 16-bit unsigned integer to file . 133 | * Returns 0 on success, != 0 on failure. 134 | */ 135 | int fwrite_u16_le(FILE * fd, uint16_t buf) 136 | { 137 | putc(buf & 0xff, fd); 138 | putc((buf >> 8) & 0xff, fd); 139 | return feof(fd) || ferror(fd); 140 | } 141 | -------------------------------------------------------------------------------- /src/endianio.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ® André Majorel, 3 | contributors to the DeuTex project. 4 | 5 | Those functions allow to read little-endian and big-endian integers 6 | from a file regardless of the endianness of the CPU. 7 | 8 | SPDX-License-Identifier: LGPL-2.1-or-later 9 | */ 10 | 11 | int fread_i16_le(FILE * fd, int16_t * buf); 12 | int fread_i16_be(FILE * fd, int16_t * buf); 13 | int fread_i32_le(FILE * fd, int32_t * buf); 14 | int fread_i32_be(FILE * fd, int32_t * buf); 15 | int fread_u16_le(FILE * fd, uint16_t * buf); 16 | int fwrite_i16_le(FILE * fd, int16_t buf); 17 | int fwrite_i16_be(FILE * fd, int16_t buf); 18 | int fwrite_i32_le(FILE * fd, int32_t buf); 19 | int fwrite_i32_be(FILE * fd, int32_t buf); 20 | int fwrite_u16_le(FILE * fd, uint16_t buf); 21 | -------------------------------------------------------------------------------- /src/endianm.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ® André Majorel, 3 | contributors to the DeuTex project. 4 | 5 | Those functions allow to read little-endian and big-endian integers 6 | from memory regardless of the endianness of the CPU. 7 | 8 | SPDX-License-Identifier: LGPL-2.1-or-later 9 | */ 10 | 11 | #include "deutex.h" 12 | #include "endianm.h" 13 | 14 | /* 15 | * read_i16_le 16 | * Read a little-endian 16-bit signed integer from memory area 17 | * pointed to by . 18 | */ 19 | void read_i16_le(const void *ptr, i16 * buf) 20 | { 21 | *buf = ((const unsigned char *) ptr)[0] 22 | | (((const unsigned char *) ptr)[1] << 8); 23 | } 24 | 25 | /* 26 | * read_i32_le 27 | * Read a little-endian 32-bit signed integer from memory area 28 | * pointed to by . 29 | */ 30 | void read_i32_le(const void *ptr, i32 * buf) 31 | { 32 | *buf = ((const unsigned char *) ptr)[0] 33 | | ((u16) ((const unsigned char *) ptr)[1] << 8) 34 | | ((i32) ((const unsigned char *) ptr)[2] << 16) 35 | | ((i32) ((const unsigned char *) ptr)[3] << 24); 36 | } 37 | 38 | /* 39 | * read_i32_be 40 | * Read a big-endian 32-bit signed integer from memory area 41 | * pointed to by . 42 | */ 43 | void read_i32_be(const void *ptr, i32 * buf) 44 | { 45 | *buf = ((i32) ((const unsigned char *) ptr)[0] << 24) 46 | | ((i32) ((const unsigned char *) ptr)[1] << 16) 47 | | ((u16) ((const unsigned char *) ptr)[2] << 8) 48 | | (((const unsigned char *) ptr)[3]); 49 | } 50 | 51 | /* 52 | * peek_i16_le 53 | * Read a little-endian 16-bit signed integer from memory area 54 | * pointed to by . 55 | */ 56 | i16 peek_i16_le(const void *ptr) 57 | { 58 | return ((const unsigned char *) ptr)[0] 59 | | (((const unsigned char *) ptr)[1] << 8); 60 | } 61 | 62 | /* 63 | * peek_u16_le 64 | * Read a little-endian 16-bit unsigned integer from memory area 65 | * pointed to by . 66 | */ 67 | u16 peek_u16_le(const void *ptr) 68 | { 69 | return ((const unsigned char *) ptr)[0] 70 | | (((const unsigned char *) ptr)[1] << 8); 71 | } 72 | 73 | /* 74 | * peek_i32_be 75 | * Read a big-endian 32-bit signed integer from memory area 76 | * pointed to by . 77 | */ 78 | i32 peek_i32_be(const void *ptr) 79 | { 80 | return ((i32) (((const unsigned char *) ptr)[0]) << 24) 81 | | ((i32) (((const unsigned char *) ptr)[1]) << 16) 82 | | ((u16) (((const unsigned char *) ptr)[2]) << 8) 83 | | ((const unsigned char *) ptr)[3]; 84 | } 85 | 86 | /* 87 | * peek_i32_le 88 | * Read a little-endian 32-bit signed integer from memory area 89 | * pointed to by . 90 | */ 91 | i32 peek_i32_le(const void *ptr) 92 | { 93 | return ((const unsigned char *) ptr)[0] 94 | | ((u16) ((const unsigned char *) ptr)[1] << 8) 95 | | ((i32) ((const unsigned char *) ptr)[2] << 16) 96 | | ((i32) ((const unsigned char *) ptr)[3] << 24); 97 | } 98 | 99 | /* 100 | * write_i16_le 101 | * Write a little-endian 16-bit signed integer to memory area 102 | * pointed to by . 103 | */ 104 | void write_i16_le(void *ptr, i16 val) 105 | { 106 | ((unsigned char *) ptr)[0] = val; 107 | ((unsigned char *) ptr)[1] = val >> 8; 108 | } 109 | 110 | /* 111 | * write_i32_be 112 | * Write a big-endian 32-bit signed integer to memory area 113 | * pointed to by . 114 | */ 115 | void write_i32_be(void *ptr, i32 val) 116 | { 117 | ((unsigned char *) ptr)[0] = val >> 24; 118 | ((unsigned char *) ptr)[1] = val >> 16; 119 | ((unsigned char *) ptr)[2] = val >> 8; 120 | ((unsigned char *) ptr)[3] = val; 121 | } 122 | 123 | /* 124 | * write_i32_le 125 | * Write a little-endian 32-bit signed integer to memory area 126 | * pointed to by . 127 | */ 128 | void write_i32_le(void *ptr, i32 val) 129 | { 130 | ((unsigned char *) ptr)[0] = val; 131 | ((unsigned char *) ptr)[1] = val >> 8; 132 | ((unsigned char *) ptr)[2] = val >> 16; 133 | ((unsigned char *) ptr)[3] = val >> 24; 134 | } 135 | -------------------------------------------------------------------------------- /src/endianm.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ® André Majorel, 3 | contributors to the DeuTex project. 4 | 5 | Those functions allow to read little-endian and big-endian integers 6 | from memory regardless of the endianness of the CPU. 7 | 8 | SPDX-License-Identifier: LGPL-2.1-or-later 9 | */ 10 | 11 | /* Use the names DeuTex provides */ 12 | #ifndef i16 13 | #define i16 int16_t 14 | #endif 15 | 16 | #ifndef i32 17 | #define i32 int32_t 18 | #endif 19 | 20 | #ifndef u16 21 | #define u16 uint16_t 22 | #endif 23 | 24 | #ifndef u32 25 | #define u32 uint32_t 26 | #endif 27 | 28 | void read_i16_le(const void *ptr, i16 * buf); 29 | void read_i32_le(const void *ptr, i32 * buf); 30 | void read_i32_be(const void *ptr, i32 * buf); 31 | i16 peek_i16_le(const void *ptr); 32 | u16 peek_u16_le(const void *ptr); 33 | i32 peek_i32_be(const void *ptr); 34 | i32 peek_i32_le(const void *ptr); 35 | void write_i16_le(void *ptr, i16 val); 36 | void write_i32_be(void *ptr, i32 val); 37 | void write_i32_le(void *ptr, i32 val); 38 | -------------------------------------------------------------------------------- /src/extract.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ® Olivier Montanuy, 3 | André Majorel, 4 | contributors to the DeuTex project. 5 | 6 | DeuTex incorporates code derived from DEU 5.21 that was put in the 7 | public domain in 1994 by Raphaël Quinet and Brendon Wyber. 8 | 9 | SPDX-License-Identifier: GPL-2.0-or-later 10 | */ 11 | 12 | /*extract.c: list dir and extract entries*/ 13 | void XTRextractWAD(const char *doomwad, const char *DataDir, const char 14 | *wadin, const char *texout, IMGTYPE Picture, 15 | SNDTYPE Sound, NTRYB select, char trnR, char trnG, 16 | char trnB, bool WSafe, cusage_t * cusage); 17 | 18 | /*extract.c: get a single entry*/ 19 | void XTRgetEntry(const char *doomwad, const char *DataDir, 20 | const char *wadin, const char *entry, IMGTYPE Picture, 21 | SNDTYPE Sound, char trnR, char trnG, char trnB); 22 | 23 | /*obsolete junk*/ 24 | void XTRtextureList(char *doomwad, char *DataDir); 25 | 26 | void XTRpatchList(char *doomwad, char *DataDir, IMGTYPE Picture, char trnR, 27 | char trnG, char trnB); 28 | -------------------------------------------------------------------------------- /src/ident.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ® Olivier Montanuy, 3 | André Majorel, 4 | contributors to the DeuTex project. 5 | 6 | DeuTex incorporates code derived from DEU 5.21 that was put in the 7 | public domain in 1994 by Raphaël Quinet and Brendon Wyber. 8 | 9 | SPDX-License-Identifier: GPL-2.0-or-later 10 | */ 11 | 12 | /* DOOM specifics*/ 13 | 14 | /* Entry Identification */ 15 | ENTRY *IDENTentriesIWAD(struct WADINFO * wad, char *Pnam, int32_t Pnamsz, 16 | bool Fast); 17 | ENTRY *IDENTentriesPWAD(struct WADINFO *wad, char *Pnam, int32_t Pnamsz); 18 | int16_t IDENTlevel(const char *buffer); 19 | /* Level Part Identification */ 20 | int IDENTlevelPart(const char *name); 21 | /* Insertion point determination*/ 22 | int16_t IDENTinsrY(PICTYPE type, int16_t insrY, int16_t szy); 23 | int16_t IDENTinsrX(PICTYPE type, int16_t insrX, int16_t szx); 24 | ENTRY IDENTsneap(struct WADINFO *info, int16_t n); 25 | 26 | const char *entry_type_name(ENTRY type); /* For EFLATS, return "flat" */ 27 | const char *entry_type_plural(ENTRY type); /* For EFLATS, return "flats" */ 28 | const char *entry_type_dir(ENTRY type); /* For EFLATS, return "flats" */ 29 | const char *entry_type_section(ENTRY type); /* For EFLATS, return "flats" */ 30 | PICTYPE entry_type_pictype(ENTRY type); /* For EFLATS, return PFLAT */ 31 | -------------------------------------------------------------------------------- /src/lists.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ® Olivier Montanuy, 3 | André Majorel, 4 | contributors to the DeuTex project. 5 | 6 | DeuTex incorporates code derived from DEU 5.21 that was put in the 7 | public domain in 1994 by Raphaël Quinet and Brendon Wyber. 8 | 9 | SPDX-License-Identifier: GPL-2.0-or-later 10 | */ 11 | 12 | /* 13 | This codes needs a serious cleaning now that it's stable! 14 | Too many parts are non optimised. 15 | */ 16 | 17 | #include "deutex.h" 18 | #include "tools.h" 19 | #include "mkwad.h" /*for find entry */ 20 | #include "lists.h" 21 | 22 | #define X_NONE 0 23 | #define X_START 2 24 | #define X_END 1 25 | 26 | /* 27 | ** list of distinct entries types 28 | */ 29 | struct ELIST { 30 | int16_t Top; /*num of entries allocated for list */ 31 | int16_t Pos; /*current top position */ 32 | struct WADDIR *Lst; 33 | }; 34 | static struct ELIST LISlmp; 35 | static struct ELIST LISspr; 36 | static struct ELIST LISpat; 37 | static struct ELIST LISflt; 38 | 39 | /* 40 | ** init 41 | */ 42 | static void LISinitLists(void) 43 | { /* GRAPHIC, LEVEL, LUMP */ 44 | LISlmp.Top = 1; 45 | LISlmp.Pos = 0; 46 | LISlmp.Lst = NULL; 47 | LISspr.Top = 1; 48 | LISspr.Pos = 0; 49 | LISspr.Lst = NULL; 50 | LISpat.Top = 1; 51 | LISpat.Pos = 0; 52 | LISpat.Lst = NULL; 53 | LISflt.Top = 1; 54 | LISflt.Pos = 0; 55 | LISflt.Lst = NULL; 56 | } 57 | 58 | /* 59 | ** count the number of entry types 60 | */ 61 | static void LIScountTypes(ENTRY * ids, int16_t nb) 62 | { 63 | int16_t i; 64 | for (i = 0; i < nb; i++) { 65 | switch (ids[i] & EMASK) { 66 | case EPNAME: 67 | case ETEXTUR: 68 | case EMAP: 69 | case ELEVEL: 70 | case ELUMP: 71 | case EGRAPHIC: 72 | case EMUSIC: 73 | case ESOUND: 74 | case EDATA: 75 | LISlmp.Top++; 76 | break; 77 | case ESPRITE: 78 | LISspr.Top++; 79 | break; 80 | case EPATCH: 81 | LISpat.Top++; 82 | break; 83 | case EFLAT: 84 | LISflt.Top++; 85 | break; 86 | case EZZZZ: 87 | case EVOID: 88 | break; 89 | default: 90 | Bug("LI01", "LisUkn"); 91 | } 92 | } 93 | } 94 | 95 | static void LISallocLists(void) 96 | { /*allocate memory for the lists */ 97 | LISlmp.Lst = 98 | (struct WADDIR *) Malloc(LISlmp.Top * sizeof(struct WADDIR)); 99 | LISspr.Lst = 100 | (struct WADDIR *) Malloc(LISspr.Top * sizeof(struct WADDIR)); 101 | LISpat.Lst = 102 | (struct WADDIR *) Malloc(LISpat.Top * sizeof(struct WADDIR)); 103 | LISflt.Lst = 104 | (struct WADDIR *) Malloc(LISflt.Top * sizeof(struct WADDIR)); 105 | } 106 | 107 | static void LISfreeLists(void) 108 | { 109 | free(LISflt.Lst); 110 | free(LISpat.Lst); 111 | free(LISspr.Lst); 112 | free(LISlmp.Lst); 113 | } 114 | 115 | /* 116 | ** Delete unwanted sprites 117 | ** 118 | */ 119 | static bool LISunwantedPhase(char pv[2], char phase, char view, 120 | bool AllViews) 121 | { 122 | if (pv[0] != phase) 123 | return false; 124 | if (pv[1] == view) 125 | return true; /*delete phase if equal */ 126 | switch (pv[1]) { /*view 0 unwanted if any other view exist */ 127 | case '0': 128 | return (AllViews == true); 129 | /*view unwanted if view 0 exist */ 130 | case '1': 131 | case '2': 132 | case '3': 133 | case '4': 134 | case '5': 135 | case '6': 136 | case '7': 137 | case '8': 138 | return (AllViews == false); 139 | } 140 | return false; 141 | } 142 | 143 | static bool LISdeleteSprite(char root[8], char phase, char view) 144 | { 145 | int16_t l, sz; 146 | bool Okay = false; 147 | bool AllViews; 148 | switch (view) { 149 | case '0': 150 | AllViews = false; 151 | break; 152 | case '1': 153 | case '2': 154 | case '3': 155 | case '4': 156 | case '5': 157 | case '6': 158 | case '7': 159 | case '8': 160 | AllViews = true; 161 | break; 162 | case '\0': 163 | default: /*Artifacts */ 164 | return false; 165 | } 166 | /*root: only the 4 first char are to be tested */ 167 | for (l = LISspr.Pos - 1; l >= 0; l--) { 168 | if (strncmp(LISspr.Lst[l].name, root, 4) == 0) { 169 | /*2nd unwanted: replace by 0 */ 170 | if (LISunwantedPhase 171 | (&(LISspr.Lst[l].name[6]), phase, view, AllViews)) { 172 | LISspr.Lst[l].name[6] = '\0'; 173 | LISspr.Lst[l].name[7] = '\0'; 174 | Okay = true; 175 | } 176 | /*1st unwanted: replace by second */ 177 | if (LISunwantedPhase 178 | (&(LISspr.Lst[l].name[4]), phase, view, AllViews)) { 179 | LISspr.Lst[l].name[4] = LISspr.Lst[l].name[6]; 180 | LISspr.Lst[l].name[5] = LISspr.Lst[l].name[7]; 181 | LISspr.Lst[l].name[6] = '\0'; 182 | LISspr.Lst[l].name[7] = '\0'; 183 | Okay = true; 184 | } 185 | /*neither 1st nor second wanted: delete */ 186 | if (LISspr.Lst[l].name[4] == '\0') { 187 | sz = LISspr.Pos - (l + 1); 188 | if (sz > 0) 189 | Memcpy(&(LISspr.Lst[l]), &(LISspr.Lst[l + 1]), 190 | (sz) * sizeof(struct WADDIR)); 191 | LISspr.Pos--; 192 | Okay = true; 193 | } 194 | } 195 | } 196 | return Okay; 197 | } 198 | 199 | /* 200 | ** list management. very basic. no hash table. 201 | */ 202 | static void LISaddMission(struct WADDIR *dir, int16_t found) 203 | { 204 | int16_t l = 0, dummy = 0; 205 | /*check */ 206 | if (LISlmp.Pos >= LISlmp.Top) 207 | Bug("LI03", "LisTsm"); /*level list too small */ 208 | /*try to locate mission entry at l */ 209 | for (l = 0; l < LISlmp.Pos; l++) { 210 | if (strncmp(LISlmp.Lst[l].name, dir[0].name, 8) == 0) 211 | break; /*l LISlmp.Pos) 216 | LISlmp.Pos = dummy; 217 | if (LISlmp.Pos > LISlmp.Top) 218 | Bug("LI05", "LisTsm"); /*level list too small */ 219 | /*copy new level, or replace existing */ 220 | Memcpy((&(LISlmp.Lst[l])), (dir), found * sizeof(struct WADDIR)); 221 | } 222 | 223 | static void LISadd(struct ELIST *L, struct WADDIR *dir) 224 | { /*check current position */ 225 | if ((L->Pos) >= L->Top) 226 | Bug("LI07", "LisSml"); /*list count too small */ 227 | /* ADD in List */ 228 | Memcpy(&(L->Lst[(L->Pos)]), (dir), sizeof(struct WADDIR)); 229 | (L->Pos)++; /*increase pointer in list */ 230 | return; 231 | } 232 | 233 | /* 234 | ** Find in list, and replace if exist 235 | ** 236 | */ 237 | static bool LISfindRplc(struct ELIST *L, struct WADDIR *dir) 238 | { 239 | int16_t l; 240 | for (l = 0; l < (L->Pos); l++) 241 | if (strncmp(L->Lst[l].name, dir[0].name, 8) == 0) { 242 | Memcpy(&(L->Lst[l]), &(dir[0]), sizeof(struct WADDIR)); 243 | return true; 244 | } 245 | return false; /*not found */ 246 | } 247 | 248 | /* Substitute In List 249 | ** if entry exist, it is substituted, else added at the end of list 250 | ** suitable for LUMPS and SPRITES 251 | ** but generally those shall not be added: they may not be recognized 252 | */ 253 | static void LISsubstit(struct ELIST *L, struct WADDIR *dir) 254 | { /* SUBSTIT in List */ 255 | if (LISfindRplc(L, dir)) 256 | return; 257 | Warning("LI09", "Entry %s might be ignored by the game", 258 | lump_name(dir[0].name)); 259 | LISadd(L, dir); 260 | } 261 | 262 | /* Move In List 263 | ** if entry already exist, destroy it, and put at the end 264 | ** suitable for PATCH and FLATS (to define new animations) 265 | */ 266 | static void LISmove(struct ELIST *L, struct WADDIR *dir) 267 | { 268 | int16_t l, sz; 269 | for (l = 0; l < (L->Pos); l++) 270 | if (strncmp(L->Lst[l].name, dir[0].name, 8) == 0) { 271 | /*entry already exist. destroy it */ 272 | sz = (L->Pos) - (l + 1); 273 | if (sz > 0) 274 | Memcpy(&(L->Lst[l]), &(L->Lst[l + 1]), 275 | (sz) * sizeof(struct WADDIR)); 276 | (L->Pos)--; 277 | } 278 | LISadd(L, dir); 279 | } 280 | 281 | /* Put Sprites In List 282 | ** if entry already exist, replace it 283 | ** if entry does not exist, check if it does not obsolete 284 | ** an IWAD sprite entry 285 | ** suitable for SPRITES only 286 | */ 287 | static void LISaddSprite(struct WADDIR *dir, bool Warn) 288 | { 289 | bool Okay = false; 290 | /*warn: false= no warning. true = warn. */ 291 | /* If entry already exists, replace it */ 292 | if (LISfindRplc(&LISspr, dir)) 293 | return; 294 | /* Entry does not exist. Check that this sprite 295 | ** viewpoint doesn't obsolete other sprite viewpoints. 296 | */ 297 | if (dir[0].name[4] != '\0') { 298 | Okay |= 299 | LISdeleteSprite(dir[0].name, dir[0].name[4], dir[0].name[5]); 300 | if (dir[0].name[6] != '\0') 301 | Okay |= 302 | LISdeleteSprite(dir[0].name, dir[0].name[6], 303 | dir[0].name[7]); 304 | } 305 | if (!Okay && Warn) { 306 | Warning("LI11", "Entry %s might be ignored by the game", 307 | lump_name(dir[0].name)); 308 | } 309 | LISadd(&LISspr, dir); 310 | } 311 | 312 | /* 313 | ** create a new directory list 314 | ** 315 | */ 316 | static void LISmakeNewDir(struct WADINFO *nwad, int16_t S_END, 317 | int16_t P_END, int16_t F_END, int16_t Pn, 318 | int16_t Fn) 319 | { 320 | int16_t n; 321 | struct WADDIR *dir; 322 | /*levels,lumps, graphics into new dir */ 323 | if (LISlmp.Pos > 0) { 324 | for (n = 0; n < LISlmp.Pos; n++) { 325 | dir = &LISlmp.Lst[n]; 326 | WADRdirAddPipo(nwad, dir->start, dir->size, dir->name); 327 | } 328 | } 329 | /*sprites into new dir */ 330 | if (LISspr.Pos > 0) { 331 | WADRdirAddPipo(nwad, 0, 0, 332 | ((S_END & X_START) ? "S_START" : "SS_START")); 333 | for (n = 0; n < LISspr.Pos; n++) { 334 | dir = &LISspr.Lst[n]; 335 | WADRdirAddPipo(nwad, dir->start, dir->size, dir->name); 336 | } 337 | WADRdirAddPipo(nwad, 0, 0, ((S_END & X_END) ? "S_END" : "SS_END")); 338 | } 339 | /*patch 1,2,3 into new dir */ 340 | if (LISpat.Pos > 0) { 341 | WADRdirAddPipo(nwad, 0, 0, 342 | ((P_END & X_START) ? "P_START" : "PP_START")); 343 | if ((Pn >= 1) && (P_END & X_START)) 344 | WADRdirAddPipo(nwad, 0, 0, "P1_START"); 345 | for (n = 0; n < LISpat.Pos; n++) { 346 | dir = &LISpat.Lst[n]; 347 | WADRdirAddPipo(nwad, dir->start, dir->size, dir->name); 348 | } 349 | if ((Pn >= 1) && (P_END & X_START)) { 350 | WADRdirAddPipo(nwad, 0, 0, "P1_END"); 351 | if (Pn >= 2) { 352 | WADRdirAddPipo(nwad, 0, 0, "P2_START"); 353 | WADRdirAddPipo(nwad, 0, 0, "P2_END"); 354 | if (Pn >= 3) { 355 | WADRdirAddPipo(nwad, 0, 0, "P3_START"); 356 | WADRdirAddPipo(nwad, 0, 0, "P3_END"); 357 | } 358 | } 359 | } 360 | WADRdirAddPipo(nwad, 0, 0, ((P_END & X_END) ? "P_END" : "PP_END")); 361 | } 362 | /*flat 1,2,3 into new dir */ 363 | if (LISflt.Pos > 0) { 364 | WADRdirAddPipo(nwad, 0, 0, 365 | ((F_END & X_START) ? "F_START" : "FF_START")); 366 | if ((Fn >= 1) && (F_END & X_START)) 367 | WADRdirAddPipo(nwad, 0, 0, "F1_START"); 368 | for (n = 0; n < LISflt.Pos; n++) { 369 | dir = &LISflt.Lst[n]; 370 | WADRdirAddPipo(nwad, dir->start, dir->size, dir->name); 371 | } 372 | if ((Fn >= 1) && (F_END & X_START)) { 373 | WADRdirAddPipo(nwad, 0, 0, "F1_END"); 374 | if (Fn >= 2) { 375 | WADRdirAddPipo(nwad, 0, 0, "F2_START"); 376 | WADRdirAddPipo(nwad, 0, 0, "F2_END"); 377 | if (Fn >= 3) { 378 | WADRdirAddPipo(nwad, 0, 0, "F3_START"); 379 | WADRdirAddPipo(nwad, 0, 0, "F3_END"); 380 | } 381 | } 382 | } 383 | WADRdirAddPipo(nwad, 0, 0, ((F_END & X_END) ? "F_END" : "FF_END")); 384 | } 385 | } 386 | 387 | /* 388 | ** merge IWAD and PWAD directories 389 | ** This function is a good example of COMPLETELY INEFFICIENT CODING. 390 | ** but since it's so hard to have it work correctly, 391 | ** Optimising was out of question. 392 | ** 393 | ** 394 | */ 395 | struct WADDIR *LISmergeDir(int32_t * pNtry, bool Append, bool Complain, 396 | NTRYB select, struct WADINFO *iwad, 397 | ENTRY * iiden, int32_t iwadflag, 398 | struct WADINFO *pwad, ENTRY * piden, 399 | int32_t pwadflag) 400 | { 401 | struct WADDIR *idir; 402 | struct WADDIR *pdir; 403 | struct WADINFO nwad; 404 | int16_t inb, pnb; 405 | int16_t i, p, found; 406 | struct WADINFO *refwad; 407 | ENTRY type; 408 | int16_t Pn = 0; /*0=nothing 1=P1_ 2=P2_ 3=P3_ */ 409 | int16_t Fn = 0; /*0=nothing 1=F1_ 2=F2_ 3=F3_ */ 410 | bool NoSprite = false; /*no sprites declared. seek in graphics */ 411 | int16_t S_END = X_NONE; 412 | int16_t F_END = X_NONE; 413 | int16_t P_END = X_NONE; 414 | /* 415 | ** select Sprites markers (tricky hack!) 416 | */ 417 | refwad = (!Append || (select & BSPRITE)) ? iwad : pwad; 418 | if (WADRfindEntry(refwad, "S_END") >= 0) { 419 | /*full sprite list is here */ 420 | S_END |= X_END; 421 | } 422 | if (WADRfindEntry(refwad, "S_START") >= 0) { 423 | /*full sprite list is here */ 424 | S_END |= X_START; 425 | } 426 | if (!(select & BSPRITE)) { 427 | if (S_END & X_END) { 428 | /*sprites already appended. keep it so. */ 429 | Complain = false; 430 | } 431 | } 432 | /* 433 | ** select Patches markers (tricky hack!) 434 | */ 435 | refwad = (!Append) ? iwad : pwad; 436 | if (WADRfindEntry(refwad, "P_END") >= 0) { 437 | /*full patch list is here */ 438 | P_END |= X_END; 439 | if (WADRfindEntry(refwad, "P3_END") >= 0) 440 | Pn = 3; 441 | else if (WADRfindEntry(refwad, "P2_END") >= 0) 442 | Pn = 2; 443 | else if (WADRfindEntry(refwad, "P1_END") >= 0) 444 | Pn = 1; 445 | } 446 | if (WADRfindEntry(refwad, "P_START") >= 0) { 447 | /*full patch list is here */ 448 | P_END |= X_START; 449 | } 450 | /* 451 | ** select Flats markers (tricky hack!) 452 | */ 453 | refwad = (!Append || (select & BFLAT)) ? iwad : pwad; 454 | if (WADRfindEntry(refwad, "F_END") >= 0) { 455 | /*full flat list is here */ 456 | F_END |= X_END; 457 | if (WADRfindEntry(refwad, "F3_END") >= 0) 458 | Fn = 3; 459 | else if (WADRfindEntry(refwad, "F2_END") >= 0) 460 | Fn = 2; 461 | else if (WADRfindEntry(refwad, "F1_END") >= 0) 462 | Fn = 1; 463 | } 464 | if (WADRfindEntry(refwad, "F_START") >= 0) { 465 | /*full flat list is here */ 466 | F_END |= X_START; 467 | } 468 | if (!(select & BFLAT)) { 469 | if (F_END & X_END) { 470 | /*flats already appended. keep it so. */ 471 | Complain = false; 472 | } 473 | } 474 | /* 475 | ** make lists of types lists 476 | */ 477 | inb = (int16_t) iwad->ntry; 478 | pnb = (int16_t) pwad->ntry; 479 | idir = iwad->dir; 480 | pdir = pwad->dir; 481 | /*alloc memory for a fake new wad */ 482 | nwad.ok = 0; 483 | WADRopenPipo(&nwad, (int32_t) inb + (int32_t) pnb + 40); 484 | /*init lists */ 485 | LISinitLists(); 486 | /*identify the elements and count types */ 487 | LIScountTypes(iiden, (int16_t) iwad->ntry); 488 | LIScountTypes(piden, (int16_t) pwad->ntry); 489 | LISallocLists(); 490 | /* distribute IWAD enties */ 491 | for (i = 0; i < inb; i++) { 492 | idir[i].start |= iwadflag; 493 | } 494 | for (i = 0; i < inb; i++) { 495 | type = iiden[i]; 496 | switch (type & EMASK) { 497 | case ELEVEL: 498 | case EMAP: 499 | if (!Append) { 500 | /*APPEND doesn't need old enties */ 501 | if (select & BLEVEL) { 502 | for (found = 1; found < 11; found++) { 503 | if (iiden[i + found] != iiden[i]) 504 | break; 505 | } 506 | LISaddMission(&(idir[i]), found); 507 | i += found - 1; 508 | } 509 | } 510 | break; 511 | case ELUMP: 512 | case EGRAPHIC: 513 | case ETEXTUR: 514 | case EPNAME: 515 | case EMUSIC: 516 | case ESOUND: 517 | case EDATA: 518 | if (!Append) { 519 | /*APPEND doesn't need old enties */ 520 | LISadd(&LISlmp, &(idir[i])); 521 | } 522 | break; 523 | case EPATCH: 524 | /*if(AllPat!=true) type=ELUMP; */ 525 | if (!Append) { 526 | /*APPEND doesn't need old enties */ 527 | if (select & BPATCH) { 528 | LISadd(&LISpat, &(idir[i])); 529 | } 530 | } 531 | break; 532 | case ESPRITE: 533 | if (select & BSPRITE) { 534 | LISadd(&LISspr, &(idir[i])); 535 | } 536 | break; 537 | case EFLAT: 538 | if (select & BFLAT) { 539 | LISadd(&LISflt, &(idir[i])); 540 | } 541 | break; 542 | case EVOID: 543 | case EZZZZ: 544 | break; 545 | default: 546 | /*LisUkI */ 547 | Bug("LI13", "Iwad entry has unknown type %04X", 548 | (unsigned) type); 549 | break; 550 | } 551 | } 552 | free(iiden); 553 | /*update position of PWAD entries */ 554 | for (p = 0; p < pnb; p++) { 555 | pdir[p].start |= pwadflag; 556 | } 557 | /* 558 | ** detect the absence of sprites 559 | */ 560 | if (select & BSPRITE) { 561 | NoSprite = true; /*if no sprites, graphics could be sprites */ 562 | for (p = 0; p < pnb; p++) { 563 | if ((piden[p] & EMASK) == ESPRITE) { 564 | NoSprite = false; 565 | break; /*don't bother searching */ 566 | } 567 | } 568 | } 569 | /*put PWAD entries */ 570 | for (p = 0; p < pnb; p++) { 571 | type = piden[p]; 572 | switch (piden[p] & EMASK) { 573 | /*special treatment for missions */ 574 | case ELEVEL: 575 | case EMAP: 576 | for (found = 1; found < 11; found++) { 577 | if (piden[p + found] != piden[p]) 578 | break; 579 | } 580 | LISaddMission(&(pdir[p]), found); 581 | p += found - 1; 582 | break; 583 | case EPATCH: 584 | LISmove(&LISpat, &(pdir[p])); 585 | break; 586 | case EDATA: 587 | case ELUMP: 588 | if (!Append && Complain) 589 | /*warn if missing */ 590 | LISsubstit(&LISlmp, &(pdir[p])); 591 | else 592 | /*no warning needed */ 593 | LISmove(&LISlmp, &(pdir[p])); 594 | break; 595 | case ETEXTUR: 596 | case EPNAME: 597 | if (!Append && Complain) 598 | /*warn if missing */ 599 | LISsubstit(&LISlmp, &(pdir[p])); 600 | else 601 | /*no warning needed */ 602 | LISmove(&LISlmp, &(pdir[p])); 603 | break; 604 | case ESOUND: 605 | if (!Append && Complain) 606 | /*warn if missing */ 607 | LISsubstit(&LISlmp, &(pdir[p])); 608 | else 609 | /*no warning needed */ 610 | LISmove(&LISlmp, &(pdir[p])); 611 | break; 612 | case EMUSIC: 613 | if (!Append && Complain) 614 | /*warn if missing */ 615 | LISsubstit(&LISlmp, &(pdir[p])); 616 | else 617 | /*no warning needed */ 618 | LISmove(&LISlmp, &(pdir[p])); 619 | break; 620 | case EGRAPHIC: 621 | if (NoSprite) { 622 | /*special: look for sprites identified as graphics */ 623 | if (!LISfindRplc(&LISspr, &(pdir[p]))) { 624 | /*not a sprite. add in lumps */ 625 | LISmove(&LISlmp, &(pdir[p])); 626 | } 627 | } else { 628 | /*normal */ 629 | if (!Append && Complain) 630 | /*warn if missing */ 631 | LISsubstit(&LISlmp, &(pdir[p])); 632 | else 633 | /*no warning needed */ 634 | LISmove(&LISlmp, &(pdir[p])); 635 | } 636 | break; 637 | case ESPRITE: 638 | /*special sprite viewpoint kill */ 639 | /*warn if missing */ 640 | LISaddSprite(&(pdir[p]), Complain); 641 | break; 642 | case EFLAT: 643 | /*add and replace former flat if needed */ 644 | LISmove(&LISflt, &(pdir[p])); 645 | break; 646 | case EVOID: 647 | case EZZZZ: 648 | break; 649 | default: 650 | /* LisUkP */ 651 | Bug("LI15", "Pwad entry has unknown type %04X", 652 | (unsigned) type); 653 | break; 654 | } 655 | } 656 | free(piden); 657 | /*create the new directory */ 658 | LISmakeNewDir(&nwad, S_END, P_END, F_END, Pn, Fn); 659 | /*free memory */ 660 | LISfreeLists(); 661 | /*return parameters wad dir and wad dir size */ 662 | return WADRclosePipo(&nwad, /*size */ pNtry); 663 | } 664 | -------------------------------------------------------------------------------- /src/lists.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ® Olivier Montanuy, 3 | André Majorel, 4 | contributors to the DeuTex project. 5 | 6 | DeuTex incorporates code derived from DEU 5.21 that was put in the 7 | public domain in 1994 by Raphaël Quinet and Brendon Wyber. 8 | 9 | SPDX-License-Identifier: GPL-2.0-or-later 10 | */ 11 | 12 | /*function to merge two WAD directories (complex)*/ 13 | struct WADDIR *LISmergeDir(int32_t * pNtry, bool OnlySF, bool Complain, 14 | NTRYB select, struct WADINFO *iwad, 15 | ENTRY * iiden, int32_t iwadflag, 16 | struct WADINFO *pwad, ENTRY * piden, 17 | int32_t pwadflag); 18 | -------------------------------------------------------------------------------- /src/lzw.c: -------------------------------------------------------------------------------- 1 | /* +-------------------------------------------------------------------+ */ 2 | /* | Copyright 1993, David Koblas (koblas@netcom.com) | */ 3 | /* | | */ 4 | /* | Permission to use, copy, modify, and to distribute this software | */ 5 | /* | and its documentation for any purpose is hereby granted without | */ 6 | /* | fee, provided that the above copyright notice appear in all | */ 7 | /* | copies and that both that copyright notice and this permission | */ 8 | /* | notice appear in supporting documentation. There is no | */ 9 | /* | representations about the suitability of this software for | */ 10 | /* | any purpose. this software is provided "as is" without express | */ 11 | /* | or implied warranty. | */ 12 | /* | | */ 13 | /* +-------------------------------------------------------------------+ */ 14 | /* 15 | ** This file is derived from ppmtogif.c and giftoppm.c 16 | ** only the compression/decompression routines were kept. 17 | ** 18 | ** Based on GIFENCOD by David Rowley .A 19 | ** Lempel-Zim compression based on "compress". 20 | ** 21 | ** Copyright (C) 1989 by Jef Poskanzer. 22 | ** 23 | ** Permission to use, copy, modify, and distribute this software and its 24 | ** documentation for any purpose and without fee is hereby granted, provided 25 | ** that the above copyright notice appear in all copies and that both that 26 | ** copyright notice and this permission notice appear in supporting 27 | ** documentation. This software is provided "as is" without express or 28 | ** implied warranty. 29 | ** 30 | ** The Graphics Interchange Format(c) is the Copyright property of 31 | ** CompuServe Incorporated. GIF(sm) is a Service Mark property of 32 | ** CompuServe Incorporated. 33 | ** 34 | ** This coder is included in DeuTex only because that of David Kaplan 35 | ** didn't work 36 | ** 37 | */ 38 | 39 | #include "deutex.h" 40 | #include "tools.h" 41 | 42 | /* 43 | * General DEFINEs 44 | */ 45 | 46 | #define BITS 12 47 | #define HSIZE 5003 /* 80% occupancy */ 48 | 49 | typedef char char_type; 50 | typedef int16_t code_int; /* 2**BITS values of type int, and also -1 */ 51 | typedef int32_t count_int; 52 | 53 | /*static int16_t table[2][(1< 125 | 126 | static int16_t n_bits; /* number of bits/code */ 127 | static int16_t maxbits; /* user settable max # bits/code */ 128 | static code_int maxcode; /* maximum code, given n_bits */ 129 | static code_int maxmaxcode; /* should NEVER generate this code */ 130 | #define MAXCODE(n_bits) (((code_int) 1 << (n_bits)) - 1) 131 | 132 | #define HashTabOf(i) htab[i] 133 | #define CodeTabOf(i) codetab[i] 134 | 135 | static code_int hsize; /* for dynamic table sizing */ 136 | 137 | static uint32_t cur_accum; 138 | static int16_t cur_bits; 139 | 140 | /* 141 | * To save much memory, we overlay the table used by compress() with those 142 | * used by decompress(). The tab_prefix table is the same size and type 143 | * as the codetab. The tab_suffix table needs 2**BITS characters. We 144 | * get this from the beginning of htab. The output stack uses the rest 145 | * of htab, and contains characters. There is plenty of room for any 146 | * possible stack (stack used to be 8000 characters). 147 | */ 148 | 149 | #define tab_prefixof(i) CodeTabOf(i) 150 | #define tab_suffixof(i) ((char_type*)(htab))[i] 151 | #define de_stack ((char_type*)&tab_suffixof((code_int)1< 0) 255 | goto probe; 256 | nomatch: 257 | output((code_int) ent); 258 | out_count++; 259 | ent = c; 260 | 261 | if (free_ent < maxmaxcode) { 262 | CodeTabOf(i) = free_ent++; /* code -> hashtable */ 263 | HashTabOf(i) = fcode; 264 | } else 265 | cl_block(); 266 | } 267 | /* 268 | * Put out the final code. 269 | */ 270 | output((code_int) ent); 271 | ++out_count; 272 | output((code_int) EOFCode); 273 | } 274 | 275 | /***************************************************************** 276 | * TAG( output ) 277 | * 278 | * Output the given code. 279 | * Inputs: 280 | * code: A n_bits-bit integer. If == -1, then EOF. This assumes 281 | * that n_bits =< (int32_t)wordsize - 1. 282 | * Outputs: 283 | * Outputs code to the file. 284 | * Assumptions: 285 | * Chars are 8 bits int32_t. 286 | * Algorithm: 287 | * Maintain a BITS character long buffer (so that 8 codes will 288 | * fit in it exactly). Use the VAX insv instruction to insert each 289 | * code in turn. When the buffer fills up empty it and start over. 290 | */ 291 | 292 | static uint32_t masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 293 | 0x001F, 0x003F, 0x007F, 0x00FF, 294 | 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 295 | 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF 296 | }; 297 | 298 | static void output(code_int code) 299 | { 300 | cur_accum &= masks[cur_bits]; 301 | if (cur_bits > 0) 302 | cur_accum |= ((int32_t) code << cur_bits); 303 | else 304 | cur_accum = code; 305 | cur_bits += n_bits; 306 | while (cur_bits >= 8) { 307 | char_out((uint16_t) (cur_accum & 0xff)); 308 | cur_accum >>= 8; 309 | cur_bits -= 8; 310 | } 311 | 312 | /* 313 | * If the next entry is going to be too big for the code size, 314 | * then increase it, if possible. 315 | */ 316 | if (free_ent > maxcode || clear_flg) { 317 | if (clear_flg) { 318 | maxcode = (code_int) (MAXCODE(n_bits = g_init_bits)); 319 | clear_flg = 0; 320 | } else { 321 | n_bits++; 322 | if (n_bits == maxbits) 323 | maxcode = maxmaxcode; 324 | else 325 | maxcode = (code_int) (MAXCODE(n_bits)); 326 | } 327 | } 328 | if (code == EOFCode) { /* At EOF, write the rest of the buffer. */ 329 | while (cur_bits > 0) { 330 | char_out((uint16_t) (cur_accum & 0xff)); 331 | cur_accum >>= 8; 332 | cur_bits -= 8; 333 | } 334 | flush_char(); 335 | fflush(g_outfile); 336 | } 337 | } 338 | 339 | /* 340 | * Clear out the hash table 341 | */ 342 | static void cl_block() 343 | { /* table clear for block compress */ 344 | cl_hash((count_int) hsize); 345 | free_ent = (code_int) (ClearCode + 2); 346 | clear_flg = 1; 347 | output((code_int) ClearCode); 348 | } 349 | 350 | static void cl_hash(register count_int hsize) 351 | { /* reset code table */ 352 | count_int *htab_p = htab + (int) hsize; 353 | register int32_t i; 354 | register int32_t m1 = -1; 355 | i = hsize - 16; 356 | do { /* might use Sys V memset(3) here */ 357 | *(htab_p - 16) = m1; 358 | *(htab_p - 15) = m1; 359 | *(htab_p - 14) = m1; 360 | *(htab_p - 13) = m1; 361 | *(htab_p - 12) = m1; 362 | *(htab_p - 11) = m1; 363 | *(htab_p - 10) = m1; 364 | *(htab_p - 9) = m1; 365 | *(htab_p - 8) = m1; 366 | *(htab_p - 7) = m1; 367 | *(htab_p - 6) = m1; 368 | *(htab_p - 5) = m1; 369 | *(htab_p - 4) = m1; 370 | *(htab_p - 3) = m1; 371 | *(htab_p - 2) = m1; 372 | *(htab_p - 1) = m1; 373 | htab_p -= 16; 374 | } while ((i -= 16) >= 0); 375 | 376 | for (i += 16; i > 0; --i) 377 | *--htab_p = m1; 378 | } 379 | 380 | /****************************************************************************** 381 | * 382 | * GIF Specific routines 383 | * 384 | ******************************************************************************/ 385 | 386 | /* 387 | * Number of characters so far in this 'packet' 388 | */ 389 | static int16_t a_count; 390 | 391 | /* 392 | * Set up the 'byte output' routine 393 | */ 394 | static void char_init() 395 | { 396 | a_count = 0; 397 | } 398 | 399 | /* 400 | * Define the storage for the packet accumulator 401 | */ 402 | static char accum[256]; 403 | 404 | /* 405 | * Add a character to the end of the current packet, and if it is 254 406 | * characters, flush the packet to disk. 407 | */ 408 | static void char_out(int16_t c) 409 | { 410 | accum[a_count++] = (char) c; 411 | if (a_count >= 254) 412 | flush_char(); 413 | } 414 | 415 | /* 416 | * Flush the packet to disk, and reset the accumulator 417 | */ 418 | static void flush_char() 419 | { 420 | if (a_count > 0) { 421 | fputc(a_count, g_outfile); 422 | fwrite(accum, 1, a_count, g_outfile); 423 | a_count = 0; 424 | } 425 | } 426 | 427 | /* The End */ 428 | 429 | /* 430 | ** 431 | ** Gif decompression 432 | ** 433 | */ 434 | 435 | #define true 1 436 | #define false 0 437 | #define ReadOK(file,buffer,len) (fread(buffer, len, 1, file) == 1) 438 | 439 | static int16_t ZeroDataBlock = false; 440 | 441 | int16_t GetDataBlock(FILE * fd, unsigned char *buf) 442 | { 443 | unsigned char count; 444 | if (!ReadOK(fd, &count, 1)) { 445 | return -1; 446 | } 447 | ZeroDataBlock = (count == 0) ? true : false; 448 | if ((count != 0) && (!ReadOK(fd, buf, count))) { 449 | return -1; 450 | } 451 | return (int16_t) (count & 0xFF); 452 | } 453 | 454 | int16_t GetCode(FILE * fd, int16_t code_size, int16_t flag) 455 | { 456 | static unsigned char buf[280]; 457 | static int16_t curbit, lastbit, done, last_byte; 458 | int16_t i, j, ret; 459 | unsigned char count; 460 | 461 | if (flag) { 462 | curbit = 0; 463 | lastbit = 0; 464 | done = false; 465 | return 0; 466 | } 467 | 468 | if ((curbit + code_size) >= lastbit) { 469 | if (done) { 470 | return -1; 471 | } 472 | buf[0] = buf[last_byte - 2]; 473 | buf[1] = buf[last_byte - 1]; 474 | if ((count = (unsigned char) GetDataBlock(fd, buf + (2))) == 0) 475 | done = true; 476 | last_byte = (int16_t) (2 + count); 477 | curbit = (int16_t) ((curbit - lastbit) + 16); 478 | lastbit = (int16_t) ((2 + count) * 8); 479 | } 480 | 481 | ret = 0; 482 | for (i = curbit, j = 0; j < code_size; ++i, ++j) 483 | ret |= ((buf[i / 8] & (1 << (i % 8))) != 0) << j; 484 | curbit += code_size; 485 | return ret; 486 | } 487 | 488 | int16_t LWZReadByte(FILE * fd, int16_t flag, int16_t input_code_size) 489 | { 490 | static int16_t fresh = false; 491 | int16_t code, incode; 492 | register int16_t i; 493 | static int16_t code_size, set_code_size; 494 | static int16_t max_code, max_code_size; 495 | static int16_t firstcode, oldcode; 496 | static int16_t clear_code, end_code; 497 | static int16_t *sp; 498 | if (flag) { 499 | set_code_size = input_code_size; 500 | code_size = (int16_t) (set_code_size + 1); 501 | clear_code = (int16_t) (1 << set_code_size); 502 | end_code = (int16_t) (clear_code + 1); 503 | max_code_size = (int16_t) (2 * clear_code); 504 | max_code = (int16_t) (clear_code + 2); 505 | GetCode(fd, 0, true); 506 | fresh = true; 507 | for (i = 0; i < clear_code; ++i) { 508 | table[0][i] = 0; 509 | table[1][i] = i; 510 | } 511 | for (; i < (1 << BITS); ++i) 512 | table[0][i] = table[1][0] = 0; 513 | sp = stack; 514 | return 0; 515 | } else if (fresh) { 516 | fresh = false; 517 | do { 518 | firstcode = oldcode = GetCode(fd, code_size, false); 519 | } while (firstcode == clear_code); 520 | return firstcode; 521 | } 522 | if (sp > stack) 523 | return *--sp; 524 | 525 | while ((code = GetCode(fd, code_size, false)) >= 0) { 526 | if (code == clear_code) { 527 | for (i = 0; i < clear_code; ++i) { 528 | table[0][i] = 0; 529 | table[1][i] = i; 530 | } 531 | for (; i < (1 << BITS); ++i) 532 | table[0][i] = table[1][i] = 0; 533 | code_size = (int16_t) (set_code_size + 1); 534 | max_code_size = (int16_t) (2 * clear_code); 535 | max_code = (int16_t) (clear_code + 2); 536 | sp = stack; 537 | firstcode = oldcode = GetCode(fd, code_size, false); 538 | return firstcode; 539 | } else if (code == end_code) { 540 | int16_t count; 541 | unsigned char buf[260]; 542 | 543 | if (ZeroDataBlock) 544 | return -2; 545 | while ((count = GetDataBlock(fd, buf)) > 0); 546 | return -2; 547 | } 548 | 549 | incode = code; 550 | if (code >= max_code) { 551 | *sp++ = firstcode; 552 | code = oldcode; 553 | } 554 | while (code >= clear_code) { 555 | *sp++ = table[1][code]; 556 | if (code == table[0][code]) { 557 | return -1; 558 | } 559 | code = table[0][code]; 560 | } 561 | *sp++ = firstcode = table[1][code]; 562 | if ((code = max_code) < (1 << BITS)) { 563 | table[0][code] = oldcode; 564 | table[1][code] = firstcode; 565 | ++max_code; 566 | if ((max_code >= max_code_size) 567 | && (max_code_size < (1 << BITS))) { 568 | max_code_size *= 2; 569 | ++code_size; 570 | } 571 | } 572 | oldcode = incode; 573 | if (sp > stack) 574 | return *--sp; 575 | } 576 | return code; 577 | } 578 | -------------------------------------------------------------------------------- /src/merge.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ® Olivier Montanuy, 3 | André Majorel, 4 | contributors to the DeuTex project. 5 | 6 | DeuTex incorporates code derived from DEU 5.21 that was put in the 7 | public domain in 1994 by Raphaël Quinet and Brendon Wyber. 8 | 9 | SPDX-License-Identifier: GPL-2.0-or-later 10 | */ 11 | 12 | #include "deutex.h" 13 | #include 14 | #include "tools.h" 15 | #include "endianm.h" 16 | #include "mkwad.h" 17 | #include "texture.h" 18 | #include "ident.h" 19 | #include "lists.h" 20 | #include "merge.h" 21 | 22 | #include 23 | 24 | /* 25 | ** old references 26 | ** 27 | */ 28 | static const int32_t HDRdirSz = 5 * sizeof(struct WADDIR); 29 | static struct WADDIR HDRdir[6]; 30 | /* 31 | ** Take some entries from a WAD 32 | */ 33 | static void HDRplunderWad(struct WADINFO *rwad, struct WADINFO *ewad) 34 | { 35 | char *data; 36 | int32_t wsize, sz = 0; 37 | int32_t ostart, osize; 38 | int16_t n; 39 | int32_t oldfilesz; 40 | /* 41 | ** calculate required size 42 | */ 43 | oldfilesz = WADRposition(rwad); /*old file size */ 44 | /* 45 | ** copy entries from WAD 46 | */ 47 | Phase("ME10", "Copying entries from wad (please wait)"); 48 | data = (char *) Malloc(MEMORYCACHE); 49 | for (n = 0; n < (rwad->ntry); n++) { 50 | ostart = rwad->dir[n].start; 51 | osize = rwad->dir[n].size; 52 | /*detect external entries */ 53 | if (ostart & EXTERNAL) { /*update new directory */ 54 | WADRalign4(rwad); 55 | rwad->dir[n].start = WADRposition(rwad); 56 | /*get entry size */ 57 | if (osize > 0) { /*copy old entry */ 58 | WADRseek(ewad, ostart & (~EXTERNAL)); 59 | for (wsize = 0; wsize < osize; wsize += sz) { 60 | sz = (osize - wsize > 61 | MEMORYCACHE) ? MEMORYCACHE : osize - wsize; 62 | WADRreadBytes(ewad, data, sz); 63 | if (WADRwriteBytes2(rwad, data, sz) < 0) { 64 | WADRchsize(rwad, oldfilesz); 65 | ProgError("ME13", "Not enough disk space"); 66 | break; 67 | } 68 | } 69 | } 70 | } 71 | } 72 | 73 | free(data); 74 | } 75 | 76 | /* 77 | ** Copy a WAD, and link to it's entries 78 | */ 79 | static int32_t HDRinsertWad(struct WADINFO *rwad, struct WADINFO *ewad, 80 | int32_t * pesize) 81 | { 82 | char *data; 83 | int32_t wsize, sz = 0; 84 | int32_t estart, esize; 85 | int16_t n; 86 | int32_t oldfilesz; 87 | /* 88 | ** calculate required size 89 | */ 90 | oldfilesz = WADRposition(rwad); /*old file size */ 91 | WADRalign4(rwad); 92 | estart = WADRposition(rwad); 93 | WADRseek(ewad, 0); 94 | esize = ewad->maxpos; 95 | Phase("ME16", "Inserting wad file into wad"); 96 | data = (char *) Malloc(MEMORYCACHE); 97 | for (wsize = 0; wsize < esize; wsize += sz) { 98 | sz = (esize - wsize > MEMORYCACHE) ? MEMORYCACHE : esize - wsize; 99 | WADRreadBytes(ewad, data, sz); 100 | if (WADRwriteBytes2(rwad, data, sz) < 0) { 101 | WADRchsize(rwad, oldfilesz); 102 | ProgError("ME19", "Not enough disk space"); 103 | break; 104 | } 105 | /*if((wsize&0xF000)==0) Phase("."); FIXME need /dev/tty output */ 106 | } 107 | free(data); 108 | for (n = 0; n < (rwad->ntry); n++) { /*detect external entries */ 109 | if ((rwad->dir[n].start) & EXTERNAL) { /*update new directory */ 110 | rwad->dir[n].start &= (~EXTERNAL); 111 | rwad->dir[n].start += estart; 112 | } 113 | } 114 | /* Phase("\n"); FIXME need /dev/tty output */ 115 | *pesize = esize; 116 | return estart; 117 | } 118 | 119 | void HDRrestoreWAD(const char *wadres) 120 | { 121 | struct WADINFO rwad; 122 | int32_t dirpos, ntry, n; 123 | int32_t rwadstart = 0, rwadsize = 0; 124 | int32_t ewadstart = 0, ewadsize = 0; 125 | static char ewadname[8]; 126 | static char ewadfile[40]; 127 | char *data; 128 | int32_t size = 0, wsize = 0, sz = 0; 129 | int32_t time; 130 | FILE *fp; 131 | bool Fail; 132 | Phase("ME22", "Attempting to restore wad %s", fname(wadres)); 133 | /*open DOOM.WAD */ 134 | rwad.ok = 0; 135 | WADRopenR(&rwad, wadres); 136 | 137 | /*get position of fake directory entry, reference to old dir */ 138 | dirpos = rwad.dirpos - HDRdirSz; 139 | WADRseek(&rwad, dirpos); 140 | WADRreadBytes(&rwad, (char *) HDRdir, HDRdirSz); 141 | Fail = false; 142 | if (peek_i32_le(&HDRdir[0].start) != 0x24061968L) 143 | Fail = true; 144 | if (peek_i32_le(&HDRdir[0].size) != 666L) 145 | Fail = true; 146 | if (strncmp(HDRdir[0].name, "IZNOGOOD", 8) != 0) 147 | Fail = true; 148 | if (Fail) { 149 | if ((n = WADRfindEntry(&rwad, "_DEUTEX_")) >= 0) 150 | if (rwad.dir[n].size >= HDRdirSz) { 151 | dirpos = rwad.dir[n].start; 152 | WADRseek(&rwad, dirpos); 153 | WADRreadBytes(&rwad, (char *) HDRdir, HDRdirSz); 154 | Fail = false; 155 | if (peek_i32_le(&HDRdir[0].start) != 0x24061968L) 156 | Fail = true; 157 | if (peek_i32_le(&HDRdir[0].size) != 666L) 158 | Fail = true; 159 | if (strncmp(HDRdir[0].name, "IZNOGOOD", 8) != 0) 160 | Fail = true; 161 | } 162 | } 163 | if (Fail) 164 | ProgError("ME25", "Not a modified WAD"); 165 | Phase("ME28", "Restoration infos seem correct"); 166 | dirpos = peek_i32_le(&HDRdir[1].start); 167 | ntry = peek_i32_le(&HDRdir[1].size); 168 | rwadstart = peek_i32_le(&HDRdir[2].start); 169 | rwadsize = peek_i32_le(&HDRdir[2].size); 170 | ewadstart = peek_i32_le(&HDRdir[3].start); 171 | ewadsize = peek_i32_le(&HDRdir[3].size); 172 | Normalise(ewadname, HDRdir[3].name); /*name of WAD inside */ 173 | /*original file time */ 174 | time = peek_i32_le(&HDRdir[4].size); 175 | if (peek_i32_le(&HDRdir[4].start)) { /*extract the PWAD */ 176 | sprintf(ewadfile, "%.8s.WAD", ewadname); 177 | ToLowerCase(ewadfile); 178 | fp = fopen(ewadfile, FOPEN_RB); 179 | if (fp != NULL) { 180 | fclose(fp); 181 | Info("ME31", "%s already exists, internal WAD discarded", 182 | fname(ewadfile)); 183 | } else { 184 | Phase("ME34", "Restoring internal wad %s", fname(ewadfile)); 185 | if ((fp = fopen(ewadfile, FOPEN_WB)) != NULL) { 186 | data = (char *) Malloc(MEMORYCACHE); 187 | size = ewadsize; 188 | WADRseek(&rwad, ewadstart); 189 | fseek(fp, 0, SEEK_SET); 190 | for (wsize = 0; wsize < size; wsize += sz) { 191 | sz = (size - wsize > 192 | MEMORYCACHE) ? MEMORYCACHE : size - wsize; 193 | WADRreadBytes(&rwad, data, sz); 194 | errno = 0; 195 | if (fwrite(data, (size_t) sz, 1, fp) != 1) { 196 | Warning("ME37", "%s: %s", fnameofs(ewadfile, (long) 197 | ewadstart), 198 | errno == 199 | 0 ? "write error" : strerror(errno)); 200 | break; 201 | } 202 | } 203 | free(data); 204 | fclose(fp); 205 | } else 206 | Warning("ME40", "%s: %s", fname(ewadfile), 207 | strerror(errno)); 208 | } 209 | } 210 | WADRopenA(&rwad, wadres); 211 | /*correct the directory reference of DOOM.WAD */ 212 | WADRsetDirRef(&rwad, ntry, dirpos); 213 | /*restore original size */ 214 | WADRchsize(&rwad, rwadstart + rwadsize); 215 | WADRclose(&rwad); 216 | Set_File_Time(wadres, time); 217 | Output("Restoration of %s should be successful\n", fname(wadres)); 218 | } 219 | 220 | static void HDRsetDir(struct WADINFO *rwad, bool IsIwad, bool Restore, 221 | int32_t time, int32_t dirpos, int32_t ntry, 222 | int32_t rsize, int32_t estart, int32_t esize, 223 | const char *wadext) 224 | { 225 | static char name[8]; 226 | int32_t pos; 227 | /*Set the old references */ 228 | Phase("ME43", "Writing new wad directory"); 229 | write_i32_le(&HDRdir[0].start, 0x24061968L); 230 | write_i32_le(&HDRdir[0].size, 666L); 231 | Normalise(HDRdir[0].name, "IZNOGOOD"); 232 | /*Set original WAD DIRECTORY */ 233 | write_i32_le(&HDRdir[1].start, dirpos); 234 | write_i32_le(&HDRdir[1].size, ntry); 235 | Normalise(HDRdir[1].name, IsIwad ? "DOOM_DIR" : "PWAD_DIR"); 236 | /*Store original WAD size and start */ 237 | write_i32_le(&HDRdir[2].start, 0); 238 | write_i32_le(&HDRdir[2].size, rsize); 239 | Normalise(HDRdir[2].name, "ORIGINAL"); 240 | /*Store external WAD size and start */ 241 | write_i32_le(&HDRdir[3].start, estart); 242 | write_i32_le(&HDRdir[3].size, esize); 243 | GetNameOfWAD(name, wadext); 244 | Normalise(HDRdir[3].name, name); 245 | /*old file time */ 246 | write_i32_le(&HDRdir[4].size, time); 247 | write_i32_le(&HDRdir[4].start, Restore); 248 | Normalise(HDRdir[4].name, "TIME"); 249 | /*save position of old ref if no previous _DEUTEX_ */ 250 | WADRalign4(rwad); 251 | pos = (int32_t) WADRfindEntry(rwad, "_DEUTEX_"); 252 | if (pos < 0) { 253 | pos = WADRposition(rwad); /*BC++ 4.5 bug */ 254 | WADRdirAddEntry(rwad, pos, HDRdirSz, "_DEUTEX_"); 255 | } 256 | /*write old refs */ 257 | WADRwriteBytes(rwad, (char *) HDRdir, HDRdirSz); 258 | /*write the directory */ 259 | rwad->dirpos = WADRposition(rwad); 260 | WADRwriteDir(rwad, 1); 261 | } 262 | 263 | /* 264 | ** merge WAD 265 | ** 266 | */ 267 | 268 | void PSTmergeWAD(const char *doomwad, const char *wadin, NTRYB select) 269 | { 270 | static struct WADINFO iwad; 271 | static struct WADINFO pwad; 272 | ENTRY *iiden; /*identify entry in IWAD */ 273 | ENTRY *piden; /*identify entry in PWAD */ 274 | int16_t pnm; 275 | char *Pnam; 276 | int32_t Pnamsz = 0; 277 | int32_t dirpos, ntry, isize, pstart, psize, time; 278 | struct WADDIR *NewDir; 279 | int32_t NewNtry; 280 | Phase("ME46", "Attempting to merge iwad %s and pwad %s", 281 | fname(doomwad), wadin); 282 | /*open iwad,get iwad directory */ 283 | iwad.ok = 0; 284 | WADRopenR(&iwad, doomwad); 285 | /*find PNAMES */ 286 | pnm = WADRfindEntry(&iwad, "PNAMES"); 287 | if (pnm < 0) 288 | ProgError("ME49", "Can't find PNAMES in iwad"); 289 | Pnam = WADRreadEntry(&iwad, pnm, &Pnamsz); 290 | /* identify iwad */ 291 | iiden = IDENTentriesIWAD(&iwad, Pnam, Pnamsz, true); 292 | /* get pwad directory, and identify */ 293 | pwad.ok = 0; 294 | WADRopenR(&pwad, wadin); 295 | piden = IDENTentriesPWAD(&pwad, Pnam, Pnamsz); 296 | free(Pnam); 297 | /*where to put pwad? at pwadstart */ 298 | /* merge the two directories */ 299 | NewDir = 300 | LISmergeDir(&NewNtry, false, true, select, &iwad, iiden, 0, &pwad, 301 | piden, EXTERNAL); 302 | /* prepare for append */ 303 | time = 304 | WADRprepareAppend(doomwad, &iwad, NewDir, NewNtry, &dirpos, &ntry, 305 | &isize); 306 | /* complete IWAD with PWAD, restorable */ 307 | pstart = HDRinsertWad(&iwad, &pwad, &psize); 308 | /* set directory */ 309 | HDRsetDir(&iwad, true, true, time, dirpos, ntry, isize, pstart, psize, 310 | wadin); 311 | /* close */ 312 | WADRclose(&pwad); 313 | WADRclose(&iwad); 314 | } 315 | 316 | /* 317 | ** -app complete a PWAD with DOOM entries 318 | ** 319 | */ 320 | void ADDappendSpriteFloor(const char *doomwad, const char *wadres, 321 | NTRYB select) 322 | { 323 | struct WADINFO iwad; 324 | struct WADINFO pwad; 325 | ENTRY *iiden; /*identify entry in IWAD */ 326 | ENTRY *piden; /*identify entry in PWAD */ 327 | int16_t pnm; 328 | char *Pnam; 329 | int32_t Pnamsz; 330 | int32_t dirpos, ntry, psize, time; 331 | struct WADDIR *NewDir; 332 | int32_t NewNtry; 333 | Phase("ME52", "Appending sprites and/or flats"); 334 | Phase("ME52", " from iwad %s", fname(doomwad)); 335 | Phase("ME52", " to pwad %s", fname(wadres)); 336 | /* get iwad directory, and identify */ 337 | iwad.ok = 0; 338 | WADRopenR(&iwad, doomwad); 339 | /*find PNAMES */ 340 | pnm = WADRfindEntry(&iwad, "PNAMES"); 341 | if (pnm < 0) 342 | ProgError("ME61", "Can't find PNAMES in iwad"); 343 | Pnam = WADRreadEntry(&iwad, pnm, &Pnamsz); 344 | /* identify iwad */ 345 | iiden = IDENTentriesIWAD(&iwad, Pnam, Pnamsz, true); 346 | /* get pwad directory, and identify */ 347 | pwad.ok = 0; 348 | WADRopenR(&pwad, wadres); 349 | piden = IDENTentriesPWAD(&pwad, Pnam, Pnamsz); 350 | free(Pnam); 351 | /* merge the two directories */ 352 | NewDir = 353 | LISmergeDir(&NewNtry, true, true, select, &iwad, iiden, EXTERNAL, 354 | &pwad, piden, 0); 355 | /* prepare for append */ 356 | time = 357 | WADRprepareAppend(wadres, &pwad, NewDir, NewNtry, &dirpos, &ntry, 358 | &psize); 359 | /* append DOOM sprites to PWAD */ 360 | HDRplunderWad(&pwad, &iwad); 361 | /* set dir */ 362 | HDRsetDir(&pwad, false, false, time, dirpos, ntry, psize, -1, -1, 363 | doomwad); 364 | /* close */ 365 | WADRclose(&iwad); 366 | WADRclose(&pwad); 367 | } 368 | 369 | /* 370 | ** join: complete a PWAD with another PWAD entries 371 | ** 372 | */ 373 | void ADDjoinWads(const char *doomwad, const char *wadres, const char 374 | *wadext, NTRYB select) 375 | { 376 | struct WADINFO iwad; 377 | /*IWAD*/ struct WADINFO ewad; /*external Wad */ 378 | struct WADINFO rwad; 379 | ENTRY *eiden; /*identify entry in IWAD */ 380 | ENTRY *riden; /*identify entry in PWAD */ 381 | int16_t entry; 382 | char *Entry; 383 | int32_t EntrySz; 384 | int16_t pnm; 385 | char *Patch; 386 | int32_t PatchSz; 387 | int32_t start, size; 388 | int16_t etexu, rtexu; 389 | struct WADDIR *NewDir; 390 | int32_t NewNtry; 391 | bool TexuMrg = false; 392 | int32_t dirpos, ntry, rsize, estart, esize, time; 393 | Phase("ME64", "Merging pwad %s", fname(wadext)); 394 | Phase("ME64", " into pwad %s", fname(wadres)); 395 | /* get iwad directory, and identify */ 396 | iwad.ok = 0; 397 | WADRopenR(&iwad, doomwad); 398 | /*find PNAMES */ 399 | entry = WADRfindEntry(&iwad, "PNAMES"); 400 | if (entry < 0) 401 | ProgError("ME70", "Can't find PNAMES in iwad"); 402 | Entry = WADRreadEntry(&iwad, entry, &EntrySz); 403 | /* get ewad directory, and identify */ 404 | ewad.ok = 0; 405 | WADRopenR(&ewad, wadext); 406 | eiden = IDENTentriesPWAD(&ewad, Entry, EntrySz); 407 | /* get rwad directory, and identify */ 408 | rwad.ok = 0; 409 | WADRopenR(&rwad, wadres); 410 | riden = IDENTentriesPWAD(&rwad, Entry, EntrySz); 411 | free(Entry); 412 | /*merge texture1 if needed */ 413 | etexu = WADRfindEntry(&ewad, "TEXTURE1"); 414 | rtexu = WADRfindEntry(&rwad, "TEXTURE1"); 415 | if ((etexu >= 0) && (rtexu >= 0)) { 416 | TexuMrg = true; 417 | iwad.ok = 0; 418 | WADRopenR(&iwad, doomwad); 419 | /*find PNAMES in IWAD and init */ 420 | pnm = WADRfindEntry(&iwad, "PNAMES"); 421 | if (pnm < 0) 422 | Bug("ME73", "JnPnm"); 423 | Entry = WADRreadEntry(&iwad, entry, &EntrySz); 424 | PNMinit(Entry, EntrySz); 425 | free(Entry); 426 | /*declare TEXTURE1 from IWAD */ 427 | TXUinit(); 428 | } 429 | WADRclose(&iwad); 430 | if (TexuMrg) { /*add TEXTURE1 from rwad */ 431 | Phase("ME76", " With TEXTURE1 from %s", fname(wadres)); 432 | PatchSz = 0; 433 | Patch = NULL; 434 | pnm = WADRfindEntry(&rwad, "PNAMES"); 435 | if (pnm >= 0) { 436 | Phase("ME79", " Declaring patches from %s", fname(wadres)); 437 | riden[pnm] = EVOID; 438 | Patch = WADRreadEntry(&rwad, pnm, &PatchSz); 439 | } 440 | Entry = WADRreadEntry(&rwad, rtexu, &EntrySz); 441 | TXUreadTEXTURE(rwad.dir[pnm].name, Entry, EntrySz, Patch, PatchSz, 442 | true); 443 | if (PatchSz != 0) 444 | free(Patch); 445 | free(Entry); 446 | riden[rtexu] = EVOID; /* forget r texu */ 447 | /*TEXTURE1 from ewad */ 448 | Phase("ME82", " And TEXTURE1 from %s", fname(wadext)); 449 | PatchSz = 0; 450 | Patch = NULL; 451 | pnm = WADRfindEntry(&ewad, "PNAMES"); 452 | if (pnm >= 0) { 453 | Phase("ME85", " Declaring Patches from %s", fname(wadext)); 454 | eiden[pnm] = EVOID; 455 | Patch = WADRreadEntry(&ewad, pnm, &PatchSz); 456 | } 457 | Entry = WADRreadEntry(&ewad, etexu, &EntrySz); 458 | TXUreadTEXTURE(ewad.dir[pnm].name, Entry, EntrySz, Patch, PatchSz, 459 | false); 460 | if (PatchSz != 0) 461 | free(Patch); 462 | free(Entry); 463 | eiden[etexu] = EVOID; /* forget e texu */ 464 | } 465 | /* merge the two directories, all entries */ 466 | NewDir = 467 | LISmergeDir(&NewNtry, false, false, select, &rwad, riden, 0, &ewad, 468 | eiden, EXTERNAL); 469 | /* prepare for append */ 470 | time = 471 | WADRprepareAppend(wadres, &rwad, NewDir, NewNtry, &dirpos, &ntry, 472 | &rsize); 473 | /* append PWAD into PWAD, restorable */ 474 | estart = HDRinsertWad(&rwad, &ewad, &esize); 475 | /* append texu/pname */ 476 | if (TexuMrg) { 477 | WADRalign4(&rwad); 478 | start = WADRposition(&rwad); 479 | size = TXUwriteTEXTUREtoWAD(&rwad); 480 | WADRdirAddEntry(&rwad, start, size, "TEXTURE1"); 481 | TXUfree(); 482 | WADRalign4(&rwad); 483 | start = WADRposition(&rwad); 484 | size = PNMwritePNAMEtoWAD(&rwad); 485 | WADRdirAddEntry(&rwad, start, size, "PNAMES"); 486 | PNMfree(); 487 | } 488 | 489 | /* set directory */ 490 | HDRsetDir(&rwad, false, true, time, dirpos, ntry, rsize, estart, esize, 491 | wadext); 492 | /*end */ 493 | WADRclose(&rwad); 494 | WADRclose(&ewad); 495 | } 496 | 497 | /* 498 | ** Add sprites and Floors 499 | ** 500 | ** must delete void entries (old DMADDS files) 501 | ** must select SPRITES of FLOORS 502 | ** 503 | */ 504 | void ADDallSpriteFloor(const char *wadout, const char *doomwad, const char 505 | *wadres, NTRYB select) 506 | { 507 | struct WADINFO iwad; 508 | struct WADINFO pwad; 509 | struct WADINFO rwad; 510 | int16_t n; 511 | ENTRY *iiden; /*identify entry in IWAD */ 512 | ENTRY *piden; /*identify entry in PWAD */ 513 | int32_t start, size, ostart, osize; 514 | int16_t pnm; 515 | char *Pnam; 516 | int32_t Pnamsz; 517 | struct WADDIR *NewDir; 518 | int32_t NewNtry; 519 | 520 | Phase("ME88", "Copying sprites and/or flats"); 521 | Phase("ME88", " from iwad %s", fname(doomwad)); 522 | Phase("ME88", " and pwad %s", fname(wadres)); 523 | Phase("ME88", " into pwad %s", fname(wadout)); 524 | /* get iwad directory, and identify */ 525 | iwad.ok = 0; 526 | WADRopenR(&iwad, doomwad); 527 | /*find PNAMES */ 528 | pnm = WADRfindEntry(&iwad, "PNAMES"); 529 | if (pnm < 0) 530 | ProgError("ME91", "Can't find PNAMES in main WAD"); 531 | Pnam = WADRreadEntry(&iwad, pnm, &Pnamsz); 532 | /* identify iwad */ 533 | iiden = IDENTentriesIWAD(&iwad, Pnam, Pnamsz, true); 534 | /* get pwad directory, and identify */ 535 | pwad.ok = 0; 536 | WADRopenR(&pwad, wadres); 537 | piden = IDENTentriesPWAD(&pwad, Pnam, Pnamsz); 538 | free(Pnam); 539 | /*where to put pwad? at pwadstart */ 540 | if ((iwad.maxpos | pwad.maxpos) & EXTERNAL) 541 | Bug("ME94", "AddExt"); 542 | /* merge the two directories */ 543 | NewDir = 544 | LISmergeDir(&NewNtry, true, true, select, &iwad, iiden, EXTERNAL, 545 | &pwad, piden, 0); 546 | /* create a new PWAD */ 547 | rwad.ok = 0; 548 | WADRopenW(&rwad, wadout, PWAD, 1); 549 | for (n = 0; n < NewNtry; n++) { 550 | ostart = NewDir[n].start; 551 | osize = NewDir[n].size; 552 | WADRalign4(&rwad); 553 | start = WADRposition(&rwad); 554 | if (ostart & EXTERNAL) 555 | size = 556 | WADRwriteWADbytes(&rwad, &iwad, ostart & (~EXTERNAL), 557 | osize); 558 | else 559 | size = WADRwriteWADbytes(&rwad, &pwad, ostart, osize); 560 | WADRdirAddEntry(&rwad, start, size, NewDir[n].name); 561 | } 562 | free(NewDir); 563 | /*close files memory */ 564 | WADRclose(&iwad); 565 | WADRclose(&pwad); 566 | WADRwriteDir(&rwad, 1); 567 | WADRclose(&rwad); 568 | Phase("ME98", "Addition of sprites and floors is complete"); 569 | } 570 | -------------------------------------------------------------------------------- /src/merge.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ® Olivier Montanuy, 3 | André Majorel, 4 | contributors to the DeuTex project. 5 | 6 | DeuTex incorporates code derived from DEU 5.21 that was put in the 7 | public domain in 1994 by Raphaël Quinet and Brendon Wyber. 8 | 9 | SPDX-License-Identifier: GPL-2.0-or-later 10 | */ 11 | 12 | /*merge a WAD into an IWAD*/ 13 | void PSTmergeWAD(const char *doomwad, const char *wadin, NTRYB select); 14 | /*put all sprites in another WAD, like DMADDS*/ 15 | void ADDallSpriteFloor(const char *wadout, const char *doomwad, 16 | const char *wadin, NTRYB select); 17 | /*append all sprites*/ 18 | void ADDappendSpriteFloor(const char *doomwad, const char *wadin, 19 | NTRYB select); 20 | /*join two WADs, including textures and pnames*/ 21 | void ADDjoinWads(const char *doomwad, const char *wadres, 22 | const char *wadext, NTRYB select); 23 | /*restore a modified WAD*/ 24 | void HDRrestoreWAD(const char *wadres); 25 | -------------------------------------------------------------------------------- /src/mkwad.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ® Olivier Montanuy, 3 | André Majorel, 4 | contributors to the DeuTex project. 5 | 6 | DeuTex incorporates code derived from DEU 5.21 that was put in the 7 | public domain in 1994 by Raphaël Quinet and Brendon Wyber. 8 | 9 | SPDX-License-Identifier: GPL-2.0-or-later 10 | */ 11 | 12 | /*for merging directories*/ 13 | void WADRopenPipo(struct WADINFO *info, int32_t ntry); 14 | struct WADDIR *WADRclosePipo(struct WADINFO *info, int32_t * ntry); 15 | int32_t WADRdirAddPipo(struct WADINFO *info, int32_t start, int32_t size, const char 16 | *entry); 17 | 18 | /*Open a WAD file for read*/ 19 | void WADRopenR(struct WADINFO *info, const char *wadin); 20 | /*Open a WAD file for write*/ 21 | void WADRopenW(struct WADINFO *info, const char *wadout, WADTYPE type, 22 | int verbose); 23 | /*Open a WAD file for append*/ 24 | void WADRopenA(struct WADINFO *info, const char *wadinout); 25 | /*Close a WAD file*/ 26 | void WADRclose(struct WADINFO *info); 27 | 28 | /*WAD file structure*/ 29 | /*set position of internal WAD directory*/ 30 | void WADRsetDirRef(struct WADINFO *info, int32_t ntry, int32_t dirpos); 31 | /*change size of a WAD*/ 32 | void WADRchsize(struct WADINFO *info, int32_t fsize); 33 | /*increase size of WAD, do not update position*/ 34 | bool WADRchsize2(struct WADINFO *info, int32_t fsize); 35 | 36 | /*composition of internal WAD directory*/ 37 | /*add an entry to the directory*/ 38 | int32_t WADRdirAddEntry(struct WADINFO *info, int32_t start, int32_t size, const char 39 | *name); 40 | /*write the directory (and set it's position)*/ 41 | void WADRwriteDir(struct WADINFO *info, int verbose); 42 | /*find an entry in the directory*/ 43 | int16_t WADRfindEntry(struct WADINFO *info, const char *entry); /*-1 or index of entry in directory*/ 44 | 45 | /*set data in a WAD (write position doesn't change)*/ 46 | void WADRsetLong(struct WADINFO *info, int32_t pos, int32_t val); 47 | void WADRsetShort(struct WADINFO *info, int32_t pos, int16_t val); 48 | /*align on long*/ 49 | void WADRalign4(struct WADINFO *info); /*align on long word, for next entry */ 50 | /*tell position of pointer*/ 51 | int32_t WADRposition(struct WADINFO *info); /*current position */ 52 | /*write date (write position increase)*/ 53 | int32_t WADRwriteLong(struct WADINFO *info, int32_t val); 54 | int32_t WADRwriteShort(struct WADINFO *info, int16_t val); 55 | int32_t WADRwriteBytes(struct WADINFO *info, char *data, int32_t size); 56 | int32_t WADRwriteBytes2(struct WADINFO *info, char *data, int32_t size); 57 | int32_t WADRwriteLump(struct WADINFO *info, const char *file); 58 | int32_t WADRwriteWADbytes(struct WADINFO *info, struct WADINFO *src, 59 | int32_t start, int32_t size); 60 | int32_t WADRwriteWADentry(struct WADINFO *info, struct WADINFO *src, 61 | int16_t n); 62 | void WADRwriteWADlevelParts(struct WADINFO *info, struct WADINFO *src, 63 | int16_t n, size_t nlumps); 64 | void WADRwriteWADlevel(struct WADINFO *info, const char *file, const char 65 | *level); 66 | 67 | /*read data*/ 68 | void WADRseek(struct WADINFO *info, int32_t position); 69 | iolen_t WADRreadBytes(struct WADINFO *info, char *buffer, iolen_t nbytes); 70 | iolen_t WADRreadBytes(struct WADINFO *info, char *buffer, iolen_t nbytes); 71 | int16_t WADRreadShort(struct WADINFO *info); 72 | int32_t WADRreadLong(struct WADINFO *info); 73 | char *WADRreadEntry(struct WADINFO *info, int16_t N, int32_t * psize); 74 | char *WADRreadEntry2(struct WADINFO *info, int16_t n, int32_t * psize); 75 | void WADRsaveEntry(struct WADINFO *info, int16_t N, const char *file); 76 | 77 | /*make some preparations before appending data to an existing WAD*/ 78 | /*so that it can be restored later*/ 79 | int32_t WADRprepareAppend(const char *wadres, struct WADINFO *rwad, struct WADDIR 80 | *NewDir, int32_t NewNtry, int32_t * dirpos, 81 | int32_t * ntry, int32_t * size); 82 | -------------------------------------------------------------------------------- /src/picture.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ® Olivier Montanuy, 3 | André Majorel, 4 | contributors to the DeuTex project. 5 | 6 | DeuTex incorporates code derived from DEU 5.21 that was put in the 7 | public domain in 1994 by Raphaël Quinet and Brendon Wyber. 8 | 9 | SPDX-License-Identifier: GPL-2.0-or-later 10 | */ 11 | 12 | /* first call COLinit();*/ 13 | bool PICsaveInFile(char *file, PICTYPE type, char *pic, int32_t picsz, 14 | int16_t * pXinsr, int16_t * pYinsr, IMGTYPE Picture, 15 | const char *name, cusage_t * cusage); 16 | int32_t PICsaveInWAD(struct WADINFO *info, char *file, PICTYPE type, 17 | int16_t Xinsr, int16_t Yinsr, IMGTYPE Picture); 18 | /* last call COLfree();*/ 19 | 20 | /*picture.c: only for test*/ 21 | void GIFtoBMP(char *file, char *bmpdir, char *name); 22 | void BMPtoGIF(char *file, char *bmpdir, char *name); 23 | 24 | /* 25 | * pic_head_t 26 | * parse_pic_header() returns header information 27 | * through this structure. 28 | */ 29 | typedef struct { 30 | int16_t width; /* Width of picture */ 31 | int16_t height; /* Height of picture */ 32 | int16_t xofs; /* X-offset of picture */ 33 | int16_t yofs; /* Y-offset of picture */ 34 | const void *colofs; /* Pointer on array of column offsets */ 35 | const unsigned char *data; /* Pointer on column data */ 36 | size_t colofs_size; /* Size of a column offset in bytes (2 or 4) */ 37 | int dummy_bytes; /* Are there dummy bytes around post data ? */ 38 | } pic_head_t; 39 | 40 | int parse_pic_header(const char *buf, long size, pic_head_t * h, 41 | char *message); 42 | -------------------------------------------------------------------------------- /src/png_tools.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © contributors to the DeuTex project. 3 | 4 | SPDX-License-Identifier: GPL-2.0-or-later 5 | */ 6 | 7 | /* 8 | This is a quick-and-dirty hack to read/write the grAb chunk from a 9 | png file. A grAb chunk is a custom PNG chunk that zdoom and SLADE 10 | uses to store the offset of the image. 11 | */ 12 | 13 | #include "deutex.h" 14 | #include "tools.h" 15 | #include "png_tools.h" 16 | #include "endianm.h" 17 | 18 | uint32_t gen_grAb_crc(unsigned char *buf) 19 | { 20 | uint32_t crc = 0xffffffff; 21 | uint32_t crc_table[256]; 22 | uint32_t c; 23 | int32_t n, k; 24 | for (n = 0; n < 256; n++) { 25 | c = (uint32_t) n; 26 | for (k = 0; k < 8; k++) { 27 | if (c & 1) 28 | c = ((uint32_t) 0xedb88320) ^ (c >> 1); 29 | else 30 | c = c >> 1; 31 | } 32 | crc_table[n] = c; 33 | } 34 | for (n = 0; n < 12; n++) 35 | crc = crc_table[(crc ^ buf[n]) & 0xff] ^ (crc >> 8); 36 | return crc ^ ((uint32_t) 0xffffffff); 37 | } 38 | 39 | unsigned char *read_whole_image(char *file, long *sz) 40 | { 41 | FILE *fd; 42 | unsigned char *buffer; 43 | size_t result; 44 | fd = fopen(file, FOPEN_RB); 45 | if (fd == NULL) { 46 | ProgError("XX16", "PNG image read error"); 47 | return NULL; 48 | } 49 | fseek(fd, 0L, SEEK_END); 50 | *sz = ftell(fd); 51 | rewind(fd); 52 | buffer = malloc(sizeof(unsigned char) * (*sz)); 53 | result = fread(buffer, 1, *sz, fd); 54 | if (result != *sz) { 55 | ProgError("XX12", "PNG image read error"); 56 | *sz = 0; 57 | return NULL; 58 | } 59 | fclose(fd); 60 | return buffer; 61 | } 62 | 63 | bool read_grAb_chunk(unsigned char *buffer, long sz, int32_t * xofs, 64 | int32_t * yofs, long *grabpos) 65 | { 66 | long i; 67 | bool is_grab = false; 68 | if (buffer == NULL) 69 | return false; 70 | for (i = 4; i < sz; i++) { 71 | if (buffer[i] == 'I' && buffer[i + 1] == 'D' && 72 | buffer[i + 2] == 'A' && buffer[i + 3] == 'T') { 73 | break; 74 | } 75 | if (buffer[i] == 'g' && buffer[i + 1] == 'r' && 76 | buffer[i + 2] == 'A' && buffer[i + 3] == 'b') { 77 | i += 4; 78 | if (i < sz) { 79 | read_i32_be(buffer + i, xofs); 80 | i += 4; 81 | if (i < sz) { 82 | read_i32_be(buffer + i, yofs); 83 | *grabpos = i - 8; 84 | is_grab = true; 85 | } else { 86 | ProgError("XX23", "read_grAb_chunk error"); 87 | } 88 | } else { 89 | ProgError("XX22", "read_grAb_chunk error"); 90 | } 91 | } 92 | } 93 | return is_grab; 94 | } 95 | 96 | void read_grAb(char *file, int16_t * Xinsr, int16_t * Yinsr) 97 | { 98 | long sz = 0; 99 | long IDATpos = 0; 100 | unsigned char *buffer = read_whole_image(file, &sz); 101 | if (buffer == NULL) 102 | return; 103 | long grabpos = 0; 104 | int32_t xofs = 0; 105 | int32_t yofs = 0; 106 | if (read_grAb_chunk(buffer, sz, &xofs, &yofs, &grabpos)) { 107 | *Xinsr = (int16_t) xofs; 108 | *Yinsr = (int16_t) yofs; 109 | } else { 110 | *Xinsr = INVALIDINT; 111 | *Yinsr = INVALIDINT; 112 | } 113 | free(buffer); 114 | } 115 | 116 | //To be called after the PNG is already written. 117 | void write_grAb(char *file, int16_t Xinsr, int16_t Yinsr) 118 | { 119 | long sz = 0; 120 | FILE *fd; 121 | long grabpos = 33; 122 | long index = 0; 123 | long IDATpos = 0; 124 | int32_t grabsz = 0; 125 | int32_t xofs = 0; 126 | int32_t yofs = 0; 127 | int32_t crc = 0; 128 | unsigned char grab_chunk[20]; 129 | bool grab_exists; 130 | unsigned char *buffer = read_whole_image(file, &sz); 131 | //create grAb Chunk 132 | //size of chunk 133 | write_i32_be(grab_chunk, 8); 134 | //name of chunk 135 | strncpy((char *) grab_chunk + 4, "grAb", 4); 136 | //xoffset 137 | write_i32_be(grab_chunk + 8, (int32_t) Xinsr); 138 | //yoffset 139 | write_i32_be(grab_chunk + 12, (int32_t) Yinsr); 140 | crc = gen_grAb_crc(grab_chunk + 4); 141 | //crc 142 | write_i32_be(grab_chunk + 16, crc); 143 | if (buffer == NULL) 144 | return; 145 | grab_exists = read_grAb_chunk(buffer, sz, &xofs, &yofs, &grabpos); 146 | fd = fopen(file, FOPEN_WB); 147 | if (fd == NULL) { 148 | ProgError("XX75", "write_grAb error"); 149 | } 150 | fwrite(buffer, sizeof(unsigned char), grabpos, fd); 151 | fwrite(grab_chunk, sizeof(char), 20, fd); 152 | if (!grab_exists) 153 | index = grabpos; 154 | else 155 | index = grabpos + 20; 156 | fwrite(buffer + index, sizeof(unsigned char), sz - index, fd); 157 | fclose(fd); 158 | free(buffer); 159 | } 160 | -------------------------------------------------------------------------------- /src/png_tools.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © contributors to the DeuTex project. 3 | 4 | SPDX-License-Identifier: GPL-2.0-or-later 5 | */ 6 | 7 | #ifndef PNG_TOOLS_H 8 | #define PNG_TOOLS_H 9 | 10 | #include "deutex.h" 11 | 12 | uint32_t gen_grAb_crc(unsigned char *buf); 13 | unsigned char *read_whole_image(char *file, long *sz); 14 | bool read_grAb_chunk(unsigned char *buffer, long sz, int32_t * xofs, 15 | int32_t * yofs, long *grabpos); 16 | void read_grAb(char *file, int16_t * Xinsr, int16_t * Yinsr); 17 | void write_grAb(char *file, int16_t Xinsr, int16_t Yinsr); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /src/sound.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ® Olivier Montanuy, 3 | André Majorel, 4 | contributors to the DeuTex project. 5 | 6 | DeuTex incorporates code derived from DEU 5.21 that was put in the 7 | public domain in 1994 by Raphaël Quinet and Brendon Wyber. 8 | 9 | SPDX-License-Identifier: GPL-2.0-or-later 10 | */ 11 | 12 | #include "deutex.h" 13 | #include 14 | #include "tools.h" 15 | #include "endianm.h" 16 | #include "mkwad.h" 17 | #include "sound.h" 18 | #include "text.h" 19 | 20 | static struct RIFFHEAD { 21 | char riff[4]; 22 | int32_t length; 23 | char wave[4]; 24 | } headr; 25 | static struct CHUNK { 26 | char name[4]; 27 | int32_t size; 28 | } headc; 29 | static struct WAVEFMT { /*format */ 30 | char fmt[4]; /* "fmt " */ 31 | int32_t fmtsize; /*0x10 */ 32 | int16_t tag; /*format tag. 1=PCM */ 33 | int16_t channel; /*1 */ 34 | int32_t smplrate; 35 | int32_t bytescnd; /*average bytes per second */ 36 | int16_t align; /*block alignment, in bytes */ 37 | int16_t nbits; /*specific to PCM format */ 38 | } headf; 39 | static struct WAVEDATA { /*data */ 40 | char data[4]; /* "data" */ 41 | int32_t datasize; 42 | } headw; 43 | 44 | static void SNDsaveWave(char *file, char *buffer, int32_t size, 45 | int32_t speed) 46 | { 47 | FILE *fp; 48 | int32_t wsize, sz = 0; 49 | fp = fopen(file, FOPEN_WB); 50 | if (fp == NULL) { 51 | ProgError("RW10", "%s: %s", fname(file), strerror(errno)); 52 | } 53 | /*header */ 54 | strncpy(headr.riff, "RIFF", 4); 55 | write_i32_le(&headr.length, 56 | 4 + sizeof(struct WAVEFMT) + sizeof(struct WAVEDATA) + 57 | size); 58 | strncpy(headr.wave, "WAVE", 4); 59 | fwrite(&headr, sizeof(struct RIFFHEAD), 1, fp); 60 | strncpy(headf.fmt, "fmt ", 4); 61 | write_i32_le(&headf.fmtsize, sizeof(struct WAVEFMT) - 8); 62 | write_i16_le(&headf.tag, 1); 63 | write_i16_le(&headf.channel, 1); 64 | write_i32_le(&headf.smplrate, speed); 65 | write_i32_le(&headf.bytescnd, speed); 66 | write_i16_le(&headf.align, 1); 67 | write_i16_le(&headf.nbits, 8); 68 | fwrite(&headf, sizeof(struct WAVEFMT), 1, fp); 69 | strncpy(headw.data, "data", 4); 70 | write_i32_le(&headw.datasize, size); 71 | fwrite(&headw, sizeof(struct WAVEDATA), 1, fp); 72 | for (wsize = 0; wsize < size; wsize += sz) { 73 | sz = (size - wsize > MEMORYCACHE) ? MEMORYCACHE : (size - wsize); 74 | if (fwrite((buffer + (wsize)), (size_t) sz, 1, fp) != 1) 75 | ProgError("RW11", "%s: write error", fname(file)); 76 | } 77 | fclose(fp); 78 | } 79 | 80 | char *SNDloadWaveFile(char *file, int32_t * psize, int32_t * pspeed) 81 | { 82 | FILE *fp; 83 | int32_t wsize, sz = 0, smplrate, datasize; 84 | int32_t chunk; 85 | char *data; 86 | fp = fopen(file, FOPEN_RB); 87 | if (fp == NULL) 88 | ProgError("RR10", "%s: %s", fname(file), strerror(errno)); 89 | /*read RIFF HEADER */ 90 | if (fread(&headr, sizeof(struct RIFFHEAD), 1, fp) != 1) 91 | ProgError("RR11", "%s: read error in header", fname(file)); 92 | /*check RIFF header */ 93 | if (strncmp(headr.riff, "RIFF", 4) != 0) 94 | ProgError("RR12", "%s: bad RIFF magic in header (%s)", fname(file), 95 | short_dump(headr.riff, 4)); 96 | if (strncmp(headr.wave, "WAVE", 4) != 0) 97 | ProgError("RR13", "%s: bad WAVE magic in header (%s)", fname(file), 98 | short_dump(headr.wave, 4)); 99 | chunk = sizeof(struct RIFFHEAD); 100 | for (sz = 0;; sz++) { 101 | if (sz > 256) 102 | ProgError("RR14", "%s: no fmt", fname(file)); 103 | fseek(fp, chunk, SEEK_SET); 104 | if (fread(&headc, sizeof(struct CHUNK), 1, fp) != 1) 105 | ProgError("RR15", "%s: no fmt", fname(file)); 106 | if (strncmp(headc.name, "fmt ", 4) == 0) 107 | break; 108 | /* There used to be a bug here; sizeof (struct CHUNK) had 109 | its bytes swapped too. Reading .wav files on big endian 110 | machines must have been broken. -- AYM 1999-07-04 */ 111 | chunk += sizeof(struct CHUNK) + peek_i32_le(&headc.size); 112 | } 113 | fseek(fp, chunk, SEEK_SET); 114 | if (fread(&headf, sizeof(struct WAVEFMT), 1, fp) == 1) { 115 | Detail("RR01", "%s is a WAVE file", fname(file)); 116 | } else { 117 | ProgError("RR02", "%s is not a WAVE file", fname(file)); 118 | } 119 | if (peek_i16_le(&headf.tag) != 1) 120 | ProgError("RR16", "%s: not raw data", fname(file)); 121 | if (peek_i16_le(&headf.channel) != 1) 122 | ProgError("RR17", "%s: not one channel", fname(file)); 123 | smplrate = peek_i32_le(&headf.smplrate); 124 | 125 | for (sz = 0;; sz++) { 126 | if (sz > 256) 127 | ProgError("RR18", "%s: no data", fname(file)); 128 | fseek(fp, chunk, SEEK_SET); 129 | if (fread(&headc, sizeof(struct CHUNK), 1, fp) != 1) 130 | ProgError("RR19", "%s: no data", fname(file)); 131 | if (strncmp(headc.name, "data", 4) == 0) 132 | break; 133 | /* Same endianness bug as above. */ 134 | chunk += sizeof(struct CHUNK) + peek_i32_le(&headc.size); 135 | } 136 | fseek(fp, chunk, SEEK_SET); 137 | if (fread(&headw, sizeof(struct WAVEDATA), 1, fp) != 1) 138 | ProgError("RR20", "%s: no data", fname(file)); 139 | datasize = peek_i32_le(&headw.datasize); 140 | /*check WAVE header */ 141 | if (datasize > 0x1000000L) /* AYM 2000-04-22 */ 142 | ProgError("RR21", "%s: sample too long (%ld)", fname(file), 143 | (long) datasize); 144 | /*read data */ 145 | data = (char *) Malloc(datasize); 146 | for (wsize = 0; wsize < datasize; wsize += sz) { 147 | sz = (datasize - wsize > 148 | MEMORYCACHE) ? MEMORYCACHE : (datasize - wsize); 149 | if (fread((data + (wsize)), (size_t) sz, 1, fp) != 1) 150 | ProgError("RR22", "%s: read error in data", fname(file)); 151 | } 152 | fclose(fp); 153 | *psize = datasize; 154 | *pspeed = smplrate & 0xFFFFL; 155 | return data; 156 | } 157 | 158 | void SNDsaveSound(char *file, char *buffer, int32_t size, SNDTYPE format, 159 | const char *name) 160 | { 161 | char *data; 162 | int32_t datasize; 163 | int32_t phys_size; 164 | int16_t type; 165 | int16_t headsize; 166 | uint16_t rate; 167 | 168 | headsize = sizeof(int16_t) + sizeof(int16_t) + sizeof(int32_t); 169 | if (size < headsize) { 170 | Warning("SD10", "Sound %s: lump has no header, skipping", 171 | lump_name(name)); 172 | return; 173 | } 174 | type = peek_i16_le(buffer); 175 | rate = peek_u16_le(buffer + 2); 176 | datasize = peek_i32_le(buffer + 4); 177 | data = buffer + headsize; 178 | if (type != 3) 179 | Warning("SD11", "Sound %s: weird type %d, extracting anyway", 180 | lump_name(name), type); 181 | 182 | phys_size = size - headsize; 183 | if (datasize > phys_size) { 184 | Warning("SD12", 185 | "Sound %s: declared length %lu > lump size %lu, truncating", 186 | lump_name(name), (unsigned long) datasize, 187 | (unsigned long) phys_size); 188 | datasize = phys_size; 189 | } else if (datasize < phys_size) { 190 | datasize = phys_size; 191 | } 192 | 193 | switch (format) { 194 | case SNDWAV: 195 | SNDsaveWave(file, data, datasize, rate); 196 | break; 197 | default: 198 | Bug("SD14", "sndsv %d", (int) format); 199 | } 200 | } 201 | 202 | int32_t SNDcopyInWAD(struct WADINFO *info, char *file, SNDTYPE format) 203 | { 204 | int32_t size = 0; 205 | int32_t datasize; 206 | int32_t rate; 207 | char *data = NULL; 208 | long wadrate; 209 | 210 | switch (format) { 211 | case SNDWAV: 212 | data = SNDloadWaveFile(file, &datasize, &rate); 213 | break; 214 | default: 215 | Bug("SC10", "sndcw %d", (int) format); 216 | } 217 | wadrate = rate; 218 | switch (rate_policy) { 219 | case RP_REJECT: 220 | if (rate != 11025) 221 | ProgError("SC11", "%s: sample rate not 11025 Hz", fname(file)); 222 | break; 223 | 224 | case RP_FORCE: 225 | if (rate > 11025) { 226 | Warning("SC12", "%s: resampling down from %ld Hz to 11025 Hz", 227 | fname(file), rate); 228 | wadrate = 11025; 229 | { 230 | double ratio = 11025.0 / rate; 231 | long s; 232 | 233 | datasize = (int32_t) (ratio * datasize) + 1; 234 | for (s = 0; s < datasize; s++) 235 | data[s] = data[(size_t) (s / ratio + 0.5)]; 236 | } 237 | data = (char *) Realloc(data, datasize); 238 | } else if (rate < 11025) { 239 | Warning("SC13", "%s: resampling up from %ld Hz to 11025 Hz", 240 | fname(file), rate); 241 | wadrate = 11025; 242 | { 243 | double ratio = 11025.0 / rate; 244 | long s; 245 | 246 | datasize = (int32_t) (ratio * datasize) + 1; 247 | data = (char *) Realloc(data, datasize); 248 | for (s = datasize - 1; s >= 0; s--) 249 | data[s] = data[(size_t) (s / ratio + 0.5)]; 250 | } 251 | } 252 | break; 253 | 254 | case RP_WARN: 255 | if (rate != 11025) 256 | Warning("SC14", 257 | "%s: sample rate != 11025 Hz, won't work on Doom < 1.4", 258 | fname(file)); 259 | break; 260 | 261 | case RP_ACCEPT: 262 | break; 263 | 264 | default: 265 | Bug("SC15", "SNDcopyInWAD: rate_policy %d", (int) rate_policy); 266 | } 267 | 268 | if (datasize > 0) { 269 | size = WADRwriteShort(info, 3); 270 | size += WADRwriteShort(info, wadrate); 271 | size += WADRwriteLong(info, datasize); 272 | size += WADRwriteBytes(info, data, datasize); 273 | } 274 | free(data); 275 | return size; 276 | } 277 | 278 | void SNDsavePCSound(const char *lumpname, const char *file, 279 | const char *buffer, int32_t size) 280 | { 281 | FILE *fp; 282 | const char *data; 283 | int16_t datasize, type, headsize; 284 | int16_t i; 285 | headsize = sizeof(int16_t) + sizeof(int16_t); 286 | if (size < headsize) 287 | ProgError("KW10", "FIXME: wrong size", fname(file)); 288 | type = peek_i16_le(buffer); 289 | datasize = peek_i16_le(buffer + 2); 290 | data = buffer + (sizeof(int16_t) + sizeof(int16_t)); 291 | if (type != 0) 292 | Bug("KW11", "FIXME: not a PC sound", fname(file)); 293 | if (size < datasize + headsize) 294 | ProgError("KW12", "FIXME: wrong size", fname(file)); 295 | fp = fopen(file, FOPEN_WT); /*text file */ 296 | if (fp == NULL) 297 | ProgError("KW13", "%s: %s", fname(file), strerror(errno)); 298 | for (i = 0; i < datasize; i++) { 299 | fprintf(fp, "%d\n", ((int) data[i]) & 0xFF); 300 | } 301 | if (fclose(fp) != 0) 302 | ProgError("KW14", "%s: %s", fname(file), strerror(errno)); 303 | } 304 | 305 | int32_t SNDcopyPCSoundInWAD(struct WADINFO *info, char *file) 306 | { 307 | struct TXTFILE *Txt; 308 | int32_t size, datasizepos; 309 | int16_t datasize, s; 310 | char c; 311 | Txt = TXTopenR(file, 1); 312 | if (Txt == NULL) 313 | ProgError("KR10", "%s: %s", fname(file), strerror(errno)); 314 | size = WADRwriteShort(info, 0); 315 | datasizepos = WADRposition(info); 316 | size += WADRwriteShort(info, -1); 317 | datasize = 0; 318 | while (TXTskipComment(Txt)) { 319 | s = TXTreadShort(Txt); 320 | if ((s < 0) || (s > 255)) 321 | ProgError("KR11", "%s: number out of bounds [0-255]", 322 | fname(file)); 323 | datasize += sizeof(char); 324 | c = (char) (s & 0xFF); 325 | size += WADRwriteBytes(info, &c, sizeof(c)); 326 | } 327 | WADRsetShort(info, datasizepos, datasize); 328 | TXTcloseR(Txt); 329 | return size; 330 | } 331 | -------------------------------------------------------------------------------- /src/sound.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ® Olivier Montanuy, 3 | André Majorel, 4 | contributors to the DeuTex project. 5 | 6 | DeuTex incorporates code derived from DEU 5.21 that was put in the 7 | public domain in 1994 by Raphaël Quinet and Brendon Wyber. 8 | 9 | SPDX-License-Identifier: GPL-2.0-or-later 10 | */ 11 | 12 | void SNDsaveSound(char *file, char *buffer, int32_t size, SNDTYPE Sound, 13 | const char *name); 14 | int32_t SNDcopyInWAD(struct WADINFO *info, char *file, SNDTYPE Sound); 15 | void SNDsavePCSound(const char *name, const char *file, const char *buffer, 16 | int32_t size); 17 | int32_t SNDcopyPCSoundInWAD(struct WADINFO *info, char *file); 18 | -------------------------------------------------------------------------------- /src/sscript.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ® Olivier Montanuy, 3 | André Majorel, 4 | contributors to the DeuTex project. 5 | 6 | DeuTex incorporates code derived from DEU 5.21 that was put in the 7 | public domain in 1994 by Raphaël Quinet and Brendon Wyber. 8 | 9 | SPDX-License-Identifier: GPL-2.0-or-later 10 | */ 11 | 12 | #include "deutex.h" 13 | #include 14 | #include 15 | #include "mkwad.h" 16 | #include "tools.h" 17 | #include "endianm.h" 18 | #include "sscript.h" 19 | 20 | #define PAGESZ 1516 21 | #define OPTIONSZ 228 22 | enum field_type_t { 23 | FT_I32, 24 | FT_NAME, 25 | FT_S16, 26 | FT_S32, 27 | FT_S80, 28 | FT_S320 29 | }; 30 | 31 | static void dump_field(FILE * fp, const unsigned char *buf, 32 | unsigned long *ofs, enum field_type_t type, 33 | const char *desc); 34 | static int mem_is_zero(const char *buf, size_t buf_size); 35 | 36 | /* 37 | * sscript_save - save a script lump to file 38 | */ 39 | int sscript_save(struct WADINFO *wad, int16_t n, const char *file) 40 | { 41 | FILE *fp = NULL; 42 | unsigned char *data = NULL; 43 | int32_t size = 0; 44 | const char *lumpname = wad->dir[n].name; 45 | 46 | data = (unsigned char *) WADRreadEntry(wad, n, &size); 47 | if (size % PAGESZ) { 48 | Warning("SS10", "script %s: weird size %ld", lump_name(lumpname), 49 | (long) size); 50 | size = PAGESZ * (size / PAGESZ); 51 | } 52 | fp = fopen(file, "w"); 53 | if (fp == NULL) { 54 | nf_err("SS15", "%s: %s", file, strerror(errno)); 55 | return 1; 56 | } 57 | fprintf(fp, "# DeuTex Strife script source version 1\n"); 58 | /* Save all pages */ 59 | { 60 | int p; 61 | for (p = 0; p < size / PAGESZ; p++) { 62 | unsigned long ofs = PAGESZ * p; 63 | int o; 64 | 65 | fputc('\n', fp); 66 | fprintf(fp, "page %d {\n", p); 67 | dump_field(fp, data, &ofs, FT_I32, " unknown0 "); 68 | dump_field(fp, data, &ofs, FT_I32, " unknown1 "); 69 | dump_field(fp, data, &ofs, FT_I32, " unknown2 "); 70 | dump_field(fp, data, &ofs, FT_I32, " unknown3 "); 71 | dump_field(fp, data, &ofs, FT_I32, " unknown4 "); 72 | dump_field(fp, data, &ofs, FT_I32, " unknown5 "); 73 | dump_field(fp, data, &ofs, FT_S16, " character "); 74 | dump_field(fp, data, &ofs, FT_NAME, " voice "); 75 | dump_field(fp, data, &ofs, FT_NAME, " background "); 76 | dump_field(fp, data, &ofs, FT_S320, " statement "); 77 | 78 | for (o = 0; o < 5; o++) { 79 | /* If the option is zeroed out, omit it */ 80 | if (mem_is_zero((char *) (data + ofs), OPTIONSZ)) { 81 | ofs += OPTIONSZ; 82 | continue; 83 | } 84 | 85 | /* Else, decode it */ 86 | fputc('\n', fp); 87 | fprintf(fp, " option %d {\n", o); 88 | dump_field(fp, data, &ofs, FT_I32, " whata "); 89 | dump_field(fp, data, &ofs, FT_I32, " whatb "); 90 | dump_field(fp, data, &ofs, FT_I32, " whatc "); 91 | dump_field(fp, data, &ofs, FT_I32, " whatd "); 92 | dump_field(fp, data, &ofs, FT_I32, " whate "); 93 | dump_field(fp, data, &ofs, FT_I32, " price "); 94 | dump_field(fp, data, &ofs, FT_I32, " whatg "); 95 | dump_field(fp, data, &ofs, FT_S32, " option "); 96 | dump_field(fp, data, &ofs, FT_S80, " success "); 97 | dump_field(fp, data, &ofs, FT_I32, " whath "); 98 | dump_field(fp, data, &ofs, FT_I32, " whati "); 99 | dump_field(fp, data, &ofs, FT_S80, " failure "); 100 | fputs(" }\n", fp); 101 | } 102 | 103 | fputs("}\n", fp); 104 | /* Sanity check */ 105 | if (ofs % PAGESZ) 106 | Bug("SS20", "script %s: page %d: bad script offset %d", 107 | lump_name(lumpname), p, (int) ofs); 108 | } 109 | } 110 | 111 | free(data); 112 | if (fclose(fp)) { 113 | nf_err("SS45", "%s: %s", file, strerror(errno)); 114 | return 1; 115 | } 116 | return 0; 117 | } 118 | 119 | /* 120 | * sscript_load 121 | */ 122 | int sscript_load(void) 123 | { 124 | printf("Oops! sscript_load not written yet!\n"); 125 | return 1; 126 | } 127 | 128 | /* 129 | * dump_field - write a field of a page to file 130 | */ 131 | static void dump_field(FILE * fp, const unsigned char *buf, 132 | unsigned long *ofs, enum field_type_t type, 133 | const char *desc) 134 | { 135 | fprintf(fp, "%-10s ", desc); 136 | if (type == FT_I32) { 137 | fprintf(fp, "%ld", (long) peek_i32_le(buf + *ofs)); 138 | *ofs += 4; 139 | } else if (type == FT_NAME) { 140 | int n; 141 | putc('"', fp); 142 | for (n = 0; n < 8 && buf[*ofs + n] != '\0'; n++) 143 | putc(tolower(buf[*ofs + n]), fp); 144 | putc('"', fp); 145 | *ofs += 8; 146 | } else if (type == FT_S16) { 147 | fprintf(fp, "\"%.16s\"", buf + *ofs); 148 | *ofs += 16; 149 | } else if (type == FT_S32) { 150 | fprintf(fp, "\"%.32s\"", buf + *ofs); 151 | *ofs += 32; 152 | } else if (type == FT_S80) { 153 | fprintf(fp, "\"%.80s\"", buf + *ofs); 154 | *ofs += 80; 155 | } else if (type == FT_S320) { 156 | fprintf(fp, "\"%.320s\"", buf + *ofs); 157 | *ofs += 320; 158 | } else { 159 | Bug("SS25", "bad field type %d", (int) type); 160 | } 161 | fputs(";\n", fp); 162 | } 163 | 164 | static int mem_is_zero(const char *buf, size_t buf_size) 165 | { 166 | int n; 167 | 168 | for (n = 0; n < buf_size; n++) { 169 | if (buf[n] != '\0') { 170 | return 0; 171 | } 172 | } 173 | return 1; 174 | } 175 | -------------------------------------------------------------------------------- /src/sscript.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ® Olivier Montanuy, 3 | André Majorel, 4 | contributors to the DeuTex project. 5 | 6 | DeuTex incorporates code derived from DEU 5.21 that was put in the 7 | public domain in 1994 by Raphaël Quinet and Brendon Wyber. 8 | 9 | SPDX-License-Identifier: GPL-2.0-or-later 10 | */ 11 | 12 | int sscript_save(struct WADINFO *wad, int16_t n, const char *file); 13 | int sscript_load(void); 14 | -------------------------------------------------------------------------------- /src/text.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ® Olivier Montanuy, 3 | André Majorel, 4 | contributors to the DeuTex project. 5 | 6 | DeuTex incorporates code derived from DEU 5.21 that was put in the 7 | public domain in 1994 by Raphaël Quinet and Brendon Wyber. 8 | 9 | SPDX-License-Identifier: GPL-2.0-or-later 10 | */ 11 | 12 | /* 13 | ** This code should contain all the tricky O/S related 14 | ** functions. If you're porting DeuTex, look here! 15 | */ 16 | 17 | #include "deutex.h" 18 | #include 19 | #include "tools.h" 20 | #include "text.h" 21 | #include 22 | 23 | /* A special instance of struct TXTFILE that is treated by the 24 | TXT*() output functions as the equivalent of /dev/null. 25 | Writing into it is a no-op. This convention is _not_ 26 | supported by the input functions ! */ 27 | struct TXTFILE TXTdummy; 28 | 29 | /*****************************************************/ 30 | 31 | const int16_t SPACE = 0x2; 32 | const int16_t NEWLINE = 0x4; 33 | const int16_t COMMENT = 0x8; 34 | const int16_t SECTION = 0x10; /*ok for SECTION header */ 35 | const int16_t NAME = 0x20; /*valid as a name identifier */ 36 | const int16_t NUMBER = 0x40; /*valid for a number */ 37 | const int16_t STPATCH = 0x80; /*start of patch? */ 38 | const int16_t EXESTRNG = 0x100; /*valid in #string# */ 39 | const int16_t BOUNDARY = 0x200; /*# */ 40 | const int16_t STEQUAL = 0x400; /*=*/ 41 | static int16_t TXTval[256]; 42 | static bool TXTok = false; 43 | 44 | void TXTinit(void) 45 | { 46 | int16_t n, val; 47 | for (n = 0; n < 256; n++) { 48 | val = 0; 49 | switch (n) { 50 | case '#': /*comment,string boundary */ 51 | val |= BOUNDARY + COMMENT; 52 | break; 53 | case ';': /*comment */ 54 | val |= COMMENT + EXESTRNG; 55 | break; 56 | case '\0': 57 | case '\n': /*newline */ 58 | val |= NEWLINE; 59 | break; 60 | case '_': /*in name */ 61 | case '\\': /* deal with VILE strange name */ 62 | val |= NAME + EXESTRNG; 63 | break; 64 | case '[': /* deal with VILE strange name */ 65 | case ']': /* deal with VILE strange name */ 66 | val |= SECTION + NAME + EXESTRNG; 67 | break; 68 | case '-': 69 | case '+': /*in integer number */ 70 | val |= NUMBER + EXESTRNG; 71 | break; 72 | case '*': /*start of patch wall */ 73 | val |= EXESTRNG + STPATCH; 74 | break; 75 | case '=': 76 | val |= STEQUAL + EXESTRNG; 77 | break; 78 | case '?': 79 | case '!': 80 | case '.': 81 | case ',': 82 | case '\'': 83 | case '&': 84 | case '(': 85 | case ')': 86 | case '$': 87 | case '%': 88 | case '@': 89 | case '/': 90 | case '<': 91 | case '>': 92 | case ' ': 93 | case '^': 94 | case '\"': 95 | case ':': 96 | val |= EXESTRNG; 97 | break; 98 | default: 99 | break; 100 | } 101 | if (isdigit(n)) 102 | val |= NUMBER + EXESTRNG; 103 | if (isalpha(n)) 104 | val |= SECTION + NAME + EXESTRNG; 105 | if (isspace(n)) 106 | val |= SPACE; 107 | if (n == '%') // Deal with Strife's "INVFONG%" and "INVFONY%" 108 | val |= NAME; 109 | TXTval[n] = val; 110 | } 111 | TXTok = true; 112 | } 113 | 114 | void TXTcloseR(struct TXTFILE *TXT) 115 | { 116 | if (!TXTok) 117 | Bug("TR91", "TxtClo"); 118 | fclose(TXT->fp); 119 | free(TXT); 120 | } 121 | 122 | struct TXTFILE *TXTopenR(const char *file, int silent) 123 | { 124 | struct TXTFILE *TXT; 125 | size_t pathname_len; 126 | /*characters */ 127 | if (!TXTok) 128 | TXTinit(); 129 | 130 | pathname_len = strlen(file); 131 | TXT = (struct TXTFILE *) Malloc(sizeof(struct TXTFILE) + pathname_len); 132 | /*some inits */ 133 | strcpy(TXT->pathname, file); 134 | TXT->Lines = 1; /*start in line 1 */ 135 | TXT->SectionStart = 0; 136 | TXT->SectionEnd = 0; 137 | TXT->fp = fopen(file, FOPEN_RT); 138 | if (TXT->fp == NULL) { 139 | if (silent) { 140 | free(TXT); 141 | return NULL; 142 | } 143 | ProgError("TR03", "%s: %s", fname(file), strerror(errno)); 144 | } 145 | return TXT; 146 | } 147 | 148 | static bool TXTgetc(struct TXTFILE *TXT, int16_t * c, int16_t * val) 149 | { 150 | int16_t cc = (int16_t) getc(TXT->fp); 151 | TXT->LastChar = cc; 152 | if (cc == EOF) 153 | return false; 154 | *c = cc = (cc & 0xFF); 155 | *val = TXTval[cc]; 156 | if (TXTval[cc] & NEWLINE) 157 | TXT->Lines++; 158 | return true; 159 | } 160 | 161 | static void TXTungetc(struct TXTFILE *TXT) 162 | { 163 | int16_t cc = TXT->LastChar; 164 | cc = (cc & 0xFF); 165 | ungetc(cc, TXT->fp); 166 | if (TXTval[cc] & NEWLINE) 167 | TXT->Lines--; 168 | } 169 | 170 | /*skip lines beginning with # or ; */ 171 | bool TXTskipComment(struct TXTFILE *TXT) 172 | { 173 | int16_t c = 0, val = 0; 174 | bool comment; 175 | for (comment = false;;) { 176 | if (!TXTgetc(TXT, &c, &val)) 177 | return false; 178 | if (val & NEWLINE) { /*eat newlines */ 179 | comment = false; 180 | continue; 181 | } 182 | if (val & COMMENT) { /*eat commentaries */ 183 | comment = true; 184 | continue; 185 | } 186 | if (val & SPACE) { /*eat space */ 187 | continue; 188 | } 189 | if (!comment) { 190 | TXTungetc(TXT); 191 | return true; 192 | } 193 | } 194 | } 195 | 196 | /* find '*' */ 197 | static bool TXTcheckStartPatch(struct TXTFILE *TXT) 198 | { 199 | int16_t c = 0, val = 0; 200 | if (!TXTgetc(TXT, &c, &val)) 201 | return false; 202 | if (val & STPATCH) 203 | return true; 204 | TXTungetc(TXT); 205 | return false; 206 | } 207 | 208 | /*read string, skip space before, stop space/\n*/ 209 | static bool TXTread(struct TXTFILE *TXT, char name[8], int16_t valid) 210 | { 211 | int16_t c = 0, val = 0, n = 0; 212 | while (1) { 213 | if (!TXTgetc(TXT, &c, &val)) 214 | return false; 215 | if (val & NEWLINE) 216 | continue; 217 | if (val & SPACE) 218 | continue; 219 | if (val & valid) 220 | break; 221 | ProgError("TR11", "%s(%ld): illegal char %s", TXT->pathname, 222 | (long) TXT->Lines, quotechar(c)); 223 | } 224 | name[0] = (char) c; 225 | for (n = 1; n < 256; n++) { 226 | if (!TXTgetc(TXT, &c, &val)) 227 | break; 228 | if (val & SPACE) { 229 | TXTungetc(TXT); 230 | break; 231 | } 232 | if (!(val & valid)) 233 | ProgError("TR13", "%s(%ld): illegal char %s in word", 234 | TXT->pathname, (long) TXT->Lines, quotechar(c)); 235 | if (n < 8) 236 | name[n] = (char) c; 237 | } 238 | if (n < 8) 239 | name[n] = '\0'; 240 | return true; 241 | } 242 | 243 | int16_t TXTreadShort(struct TXTFILE * TXT) 244 | { 245 | static char buffer[9]; 246 | TXTread(TXT, buffer, NUMBER); 247 | buffer[8] = '\0'; 248 | return (int16_t) atoi(buffer); 249 | } 250 | 251 | static bool TXTboundSection(struct TXTFILE *TXT); 252 | static bool TXTreadIdent(struct TXTFILE *TXT, char name[8]) 253 | { 254 | if (!TXTok) 255 | Bug("TR21", "%s: TxtRid", fname(TXT->pathname)); 256 | if (!TXTskipComment(TXT)) 257 | return false; 258 | /*check end of section */ 259 | if ((TXT->Lines) > (TXT->SectionEnd)) { 260 | if (!TXTboundSection(TXT)) 261 | return false; /*no other section */ 262 | } 263 | if (!TXTread(TXT, name, NAME | NUMBER)) 264 | ProgError("TR23", "%s(%ld): expected identifier or \"END:\"", 265 | TXT->pathname, (long) TXT->Lines); 266 | Normalise(name, name); 267 | return true; 268 | } 269 | 270 | /* 271 | ** STPATCH is also used to indicate repetition 272 | */ 273 | static bool TXTreadOptionalRepeat(struct TXTFILE *TXT) 274 | { 275 | int16_t c = 0, val = 0; 276 | while (1) { 277 | if (!TXTgetc(TXT, &c, &val)) 278 | return false; 279 | if (!(val & NEWLINE)) { 280 | if (val & STPATCH) 281 | break; /*look for STPATCH */ 282 | if (val & SPACE) 283 | continue; /*skip space */ 284 | } 285 | TXTungetc(TXT); 286 | return false; 287 | } 288 | return true; /*found */ 289 | } 290 | 291 | /* 292 | ** STEQUAL is used to indicate alternate name 293 | */ 294 | static void TXTreadOptionalName(struct TXTFILE *TXT, char name[8]) 295 | { 296 | int16_t c = 0, val = 0; 297 | while (1) { 298 | if (!TXTgetc(TXT, &c, &val)) 299 | return; 300 | if (!(val & NEWLINE)) { 301 | if (val & STEQUAL) 302 | continue; /*skip '=' */ 303 | if (val & SPACE) 304 | continue; /*skip space */ 305 | if (val & (NAME & (~NUMBER))) 306 | break; 307 | } 308 | TXTungetc(TXT); 309 | return; /*name is NOT modified */ 310 | } 311 | TXTungetc(TXT); 312 | if (!TXTread(TXT, name, NAME | NUMBER)) { 313 | ProgError("TR32", "%s(%ld): invalid optional name", TXT->pathname, 314 | (long) TXT->Lines); 315 | } 316 | } 317 | 318 | /* 319 | ** Read integer if exist before NEWLINE, 320 | ** but don't eat NEWLINE 321 | */ 322 | static int16_t TXTreadOptionalShort(struct TXTFILE *TXT) 323 | { 324 | static char name[9]; 325 | int16_t n, c = 0, val = 0; 326 | while (1) { 327 | if (!TXTgetc(TXT, &c, &val)) 328 | return INVALIDINT; 329 | if (!(val & NEWLINE)) { 330 | if (val & SPACE) 331 | continue; /*skip space */ 332 | if (val & STEQUAL) 333 | continue; /*skip '=' */ 334 | if (val & NUMBER) 335 | break; /*look for number */ 336 | } 337 | TXTungetc(TXT); 338 | return INVALIDINT; /*not a number. abort */ 339 | } 340 | name[0] = (char) c; 341 | for (n = 1; n < 256; n++) { 342 | if (!TXTgetc(TXT, &c, &val)) 343 | break; 344 | if (val & NEWLINE) { 345 | TXTungetc(TXT); 346 | break; 347 | } 348 | if (val & SPACE) 349 | break; 350 | if (!(val & NUMBER)) 351 | ProgError("TR42", "%s(%ld): illegal char %s in number", 352 | fname(TXT->pathname), (long) TXT->Lines, 353 | quotechar(c)); 354 | if (n < 8) 355 | name[n] = (char) c; 356 | } 357 | if (n < 8) 358 | name[n] = '\0'; 359 | name[8] = '\0'; 360 | return (int16_t) atoi(name); 361 | } 362 | 363 | /* read Blocks of the form 364 | ** [BLOCKID] 365 | ** identifier ... anything ... 366 | ** identifier ... anything ... 367 | */ 368 | static bool TXTfindSection(struct TXTFILE *TXT, bool Match) 369 | { 370 | int16_t c = 0, val = 0, n; 371 | char buffer[8]; 372 | while (1) { 373 | if (!TXTskipComment(TXT)) 374 | return false; 375 | if (!TXTgetc(TXT, &c, &val)) 376 | return false; 377 | if (c == '[') { 378 | for (n = 0; n < 256; n++) { 379 | if (!TXTgetc(TXT, &c, &val)) 380 | return false; 381 | if (c == ']') { 382 | if (n < 8) 383 | buffer[n] = '\0'; 384 | if (!Match) 385 | return true; /*any section is ok */ 386 | Normalise(buffer, buffer); /*the right section? */ 387 | if (strncmp(buffer, TXT->Section, 8) == 0) 388 | return true; 389 | break; /*not the right section */ 390 | } 391 | if (!(val & (NAME | NUMBER))) 392 | break; /*not a section */ 393 | if (n < 8) 394 | buffer[n] = c; 395 | } 396 | } 397 | while (1) { /*look for end of line */ 398 | if (!TXTgetc(TXT, &c, &val)) 399 | return false; 400 | if (val & NEWLINE) 401 | break; 402 | } 403 | } 404 | } 405 | 406 | /* 407 | ** find the section boundaries, from current position in file 408 | */ 409 | static bool TXTboundSection(struct TXTFILE *TXT) 410 | { 411 | int16_t c = 0, val = 0; 412 | if (!TXTfindSection(TXT, true)) 413 | return false; 414 | TXT->SectionStart = TXT->Lines + 1; 415 | /*check that we don't read twice the same section */ 416 | if (TXT->SectionEnd > TXT->SectionStart) 417 | Bug("TR51", "TxtBdS"); 418 | if (TXTfindSection(TXT, false)) 419 | TXT->SectionEnd = TXT->Lines - 1; 420 | else 421 | TXT->SectionEnd = TXT->Lines; 422 | /* set pointer to first section line */ 423 | fseek(TXT->fp, 0, SEEK_SET); 424 | TXT->Lines = 1; /*start in line 1 */ 425 | while (TXT->Lines < TXT->SectionStart) { 426 | if (!TXTgetc(TXT, &c, &val)) 427 | return false; 428 | } 429 | return true; 430 | } 431 | 432 | bool TXTseekSection(struct TXTFILE * TXT, const char *section) 433 | { 434 | if (!TXTok) 435 | Bug("TR61", "TxtSks"); 436 | /*seek begin of file */ 437 | TXT->SectionStart = 0; 438 | TXT->SectionEnd = 0; 439 | Normalise(TXT->Section, section); 440 | fseek(TXT->fp, 0L, SEEK_SET); 441 | TXT->Lines = 1; /*start in line 1 */ 442 | /*skipping comments, look for a line with 443 | [section] */ 444 | return TXTboundSection(TXT); 445 | } 446 | 447 | /*read a texture definition*/ 448 | /*return false if read End*/ 449 | bool TXTreadTexDef(struct TXTFILE * TXT, char name[8], int16_t * szx, 450 | int16_t * szy) 451 | { 452 | if (!TXTok) 453 | Bug("TR71", "TxtTxd"); 454 | if (!TXTskipComment(TXT)) 455 | return false; /*End */ 456 | if (!TXTread(TXT, name, NAME | NUMBER)) 457 | ProgError("TR73", "%s(%ld): expecting identifier", 458 | fname(TXT->pathname), (long) TXT->Lines); 459 | Normalise(name, name); 460 | *szx = TXTreadShort(TXT); 461 | *szy = TXTreadShort(TXT); 462 | return true; 463 | } 464 | 465 | /*read a patch def. Return false if could not find '*' */ 466 | bool TXTreadPatchDef(struct TXTFILE * TXT, char name[8], int16_t * ofsx, 467 | int16_t * ofsy) 468 | { 469 | if (!TXTok) 470 | Bug("TR81", "TxtRpd"); 471 | if (!TXTskipComment(TXT)) 472 | return false; 473 | if (!TXTcheckStartPatch(TXT)) 474 | return false; /*not a patch line */ 475 | if (!TXTread(TXT, name, NAME | NUMBER)) 476 | ProgError("TR83", "%s(%ld): expecting identifier", 477 | fname(TXT->pathname), (long) TXT->Lines); 478 | Normalise(name, name); 479 | *ofsx = TXTreadShort(TXT); 480 | *ofsy = TXTreadShort(TXT); 481 | return true; 482 | } 483 | 484 | bool TXTentryParse(char *name, char *filenam, int16_t * x, int16_t * y, 485 | bool * repeat, struct TXTFILE * TXT, bool XY) 486 | { 487 | int16_t c = 0, val = 0; 488 | bool comment; 489 | int16_t xx = INVALIDINT, yy = INVALIDINT; 490 | if (!TXTreadIdent(TXT, name)) 491 | return false; 492 | /* skip the equal */ 493 | if (!TXTgetc(TXT, &c, &val)) 494 | return false; 495 | if (c != '=') 496 | TXTungetc(TXT); 497 | /* read integer */ 498 | if (XY) { 499 | xx = TXTreadOptionalShort(TXT); 500 | yy = TXTreadOptionalShort(TXT); 501 | } 502 | Normalise(filenam, name); 503 | TXTreadOptionalName(TXT, filenam); 504 | if (XY) { 505 | if (xx == INVALIDINT) 506 | xx = TXTreadOptionalShort(TXT); 507 | if (yy == INVALIDINT) 508 | yy = TXTreadOptionalShort(TXT); 509 | } 510 | *repeat = TXTreadOptionalRepeat(TXT); 511 | *x = xx; 512 | *y = yy; 513 | for (comment = false;;) { 514 | if (!TXTgetc(TXT, &c, &val)) 515 | break; 516 | if (val & NEWLINE) 517 | break; 518 | if (val & COMMENT) { /*eat commentaries */ 519 | comment = true; 520 | continue; 521 | } 522 | if (val & SPACE) { /*eat space */ 523 | continue; 524 | } 525 | if (!comment) 526 | ProgError("TR87", "%s(%ld): bad entry format", 527 | fname(TXT->pathname), (long) TXT->Lines); 528 | } 529 | return true; 530 | } 531 | 532 | /* 533 | ** For any Writing of text files 534 | */ 535 | struct TXTFILE *TXTopenW(const char *file) 536 | { /*open, and init if needed */ 537 | struct TXTFILE *TXT; 538 | size_t pathname_len; 539 | 540 | /*characters */ 541 | if (!TXTok) 542 | TXTinit(); 543 | pathname_len = strlen(file); 544 | TXT = (struct TXTFILE *) Malloc(sizeof(struct TXTFILE) + pathname_len); 545 | /*some inits */ 546 | strcpy(TXT->pathname, file); 547 | TXT->Lines = 1; /*start in line 1 */ 548 | TXT->SectionStart = 0; 549 | TXT->SectionEnd = 0; 550 | TXT->fp = fopen(file, FOPEN_RT); 551 | if (TXT->fp == NULL) { 552 | TXT->fp = fopen(file, FOPEN_WT); 553 | } else { 554 | fclose(TXT->fp); 555 | TXT->fp = fopen(file, FOPEN_AT); 556 | Warning("TW03", "%s: already exists, appending to it", 557 | fname(file)); 558 | } 559 | if (TXT->fp == NULL) 560 | ProgError("TW05", "%s: %s", fname(file), strerror(errno)); 561 | return TXT; 562 | } 563 | 564 | void TXTcloseW(struct TXTFILE *TXT) 565 | { 566 | if (TXT == &TXTdummy) 567 | return; 568 | if (!TXTok) 569 | Bug("TW91", "TxtClo"); 570 | fclose(TXT->fp); 571 | free(TXT); 572 | } 573 | 574 | /* 575 | ** To write entries 576 | */ 577 | void TXTaddSection(struct TXTFILE *TXT, const char *def) 578 | { 579 | if (TXT == &TXTdummy) 580 | return; 581 | if (!TXTok) 582 | Bug("TW11", "TxtAdS"); 583 | fprintf(TXT->fp, "[%.8s]\n", def); 584 | } 585 | 586 | void TXTaddEntry(struct TXTFILE *TXT, const char *name, 587 | const char *filenam, int16_t x, int16_t y, bool repeat, 588 | bool XY) 589 | { 590 | if (TXT == &TXTdummy) 591 | return; 592 | if (!TXTok) 593 | Bug("TW21", "TxtAdE"); 594 | fprintf(TXT->fp, "%.8s", name); 595 | if (filenam != NULL) 596 | fprintf(TXT->fp, "\t%.8s", filenam); 597 | if (XY) 598 | fprintf(TXT->fp, "\t%d\t%d", x, y); 599 | if (repeat) 600 | fprintf(TXT->fp, "\t*"); 601 | fprintf(TXT->fp, "\n"); 602 | } 603 | 604 | void TXTaddComment(struct TXTFILE *TXT, const char *text) 605 | { 606 | if (TXT == &TXTdummy) 607 | return; 608 | if (!TXTok) 609 | Bug("TW31", "TxtAdC"); 610 | fprintf(TXT->fp, "# %.256s\n", text); 611 | } 612 | 613 | void TXTaddEmptyLine(struct TXTFILE *TXT) 614 | { 615 | if (TXT == &TXTdummy) 616 | return; 617 | if (!TXTok) 618 | Bug("TW41", "TxtAdL"); 619 | putc('\n', TXT->fp); 620 | } 621 | -------------------------------------------------------------------------------- /src/text.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ® Olivier Montanuy, 3 | André Majorel, 4 | contributors to the DeuTex project. 5 | 6 | DeuTex incorporates code derived from DEU 5.21 that was put in the 7 | public domain in 1994 by Raphaël Quinet and Brendon Wyber. 8 | 9 | SPDX-License-Identifier: GPL-2.0-or-later 10 | */ 11 | 12 | /* 13 | ** simplified TEXT parsing 14 | */ 15 | struct TXTFILE { 16 | FILE *fp; 17 | int16_t Lines; 18 | int16_t LastChar; 19 | int16_t SectionStart; 20 | int16_t SectionEnd; 21 | char Section[8]; 22 | char pathname[1]; 23 | }; 24 | /* A special instance of struct TXTFILE that is treated by the 25 | TXT*() output functions as the equivalent of /dev/null. 26 | Writing into it is a no-op. This convention is _not_ 27 | supported by the input functions ! */ 28 | extern struct TXTFILE TXTdummy; 29 | 30 | /* 31 | ** For any Reading of TEXT files 32 | */ 33 | void TXTinit(void); 34 | struct TXTFILE *TXTopenR(const char *file, int silent); 35 | void TXTcloseR(struct TXTFILE *TXT); 36 | /* 37 | ** To read entries 38 | */ 39 | bool TXTskipComment(struct TXTFILE *TXT); 40 | bool TXTseekSection(struct TXTFILE *TXT, const char *def); 41 | bool TXTentryParse(char *name, char *filenam, int16_t * x, int16_t * y, 42 | bool * repeat, struct TXTFILE *TXT, bool XY); 43 | /* 44 | ** To read textures 45 | */ 46 | bool TXTreadTexDef(struct TXTFILE *TXT, char name[8], int16_t * szx, 47 | int16_t * szy); 48 | bool TXTreadPatchDef(struct TXTFILE *TXT, char name[8], int16_t * ofsx, 49 | int16_t * ofsy); 50 | /* 51 | ** To read PC sounds 52 | */ 53 | int16_t TXTreadShort(struct TXTFILE *TXT); 54 | 55 | /* 56 | ** For any Writing of text files 57 | */ 58 | struct TXTFILE *TXTopenW(const char *file); /*open, and init if needed */ 59 | void TXTcloseW(struct TXTFILE *TXT); 60 | /* 61 | ** To write entries 62 | */ 63 | void TXTaddSection(struct TXTFILE *TXT, const char *def); 64 | void TXTaddEntry(struct TXTFILE *TXT, const char *name, const char 65 | *filenam, int16_t x, int16_t y, bool repeat, bool XY); 66 | void TXTaddComment(struct TXTFILE *TXT, const char *text); 67 | void TXTaddEmptyLine(struct TXTFILE *TXT); 68 | -------------------------------------------------------------------------------- /src/texture.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ® Olivier Montanuy, 3 | André Majorel, 4 | contributors to the DeuTex project. 5 | 6 | DeuTex incorporates code derived from DEU 5.21 that was put in the 7 | public domain in 1994 by Raphaël Quinet and Brendon Wyber. 8 | 9 | SPDX-License-Identifier: GPL-2.0-or-later 10 | */ 11 | 12 | /** PNM module **/ 13 | void PNMinit(char *buffer, int32_t size); 14 | int16_t PNMindexOfPatch(char *patch); 15 | /* check if patch exists. for ident.c */ 16 | int32_t PNMwritePNAMEtoWAD(struct WADINFO *info); 17 | /* write PNAME entry in Wad */ 18 | void PNMfree(void); 19 | int16_t PNMgetNbOfPatch(void); 20 | /* compose.c get nb of patches */ 21 | void PNMgetPatchName(char name[8], int16_t index); 22 | /* compose.c returns name of patch, from index */ 23 | bool PNMisNew(int16_t idx); 24 | /* compose.c is the patch not in IWAD? */ 25 | /** TXU module **/ 26 | void TXUinit(void); /*requires the the patches are init */ 27 | void TXUfree(void); 28 | bool TXUexist(char *Name); 29 | /*does it exist? */ 30 | int32_t TXUwriteTEXTUREtoWAD(struct WADINFO *info); 31 | /*write the TEXTURE entry in Wad */ 32 | void TXUreadTEXTURE(const char *texture1_name, const char *Data, 33 | int32_t DataSz, const char *Patch, int32_t PatchSz, 34 | bool Redefn); 35 | /*read texture from raw data = TEXTURE entry */ 36 | void TXUwriteTexFile(const char *file); 37 | /*write the TEXTURE entry to a file */ 38 | void TXUreadTexFile(const char *file, bool Redefn); 39 | /*checkif the composition of textues in okay */ 40 | bool TXUcheckTex(int16_t npatch, int16_t * PszX); 41 | /*declare a fake texure. to list textures */ 42 | void TXUfakeTex(char Name[8]); 43 | /*list all defined textures */ 44 | void TXUlistTex(void); 45 | -------------------------------------------------------------------------------- /src/tools.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ® Olivier Montanuy, 3 | André Majorel, 4 | contributors to the DeuTex project. 5 | 6 | DeuTex incorporates code derived from DEU 5.21 that was put in the 7 | public domain in 1994 by Raphaël Quinet and Brendon Wyber. 8 | 9 | SPDX-License-Identifier: GPL-2.0-or-later 10 | */ 11 | 12 | /* 13 | ** This code should contain all the tricky O/S related 14 | ** functions. If you're porting DeuTex, look here! 15 | */ 16 | 17 | #include "deutex.h" 18 | #include 19 | #include 20 | #include "tools.h" 21 | 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #ifdef _WIN32 31 | #define WIN32_LEAN_AND_MEAN 32 | #include 33 | #endif 34 | 35 | static const char hex_digit[16] = "0123456789ABCDEF"; 36 | 37 | /* 38 | ** Get a file time stamp. (date of last modification) 39 | */ 40 | int32_t Get_File_Time(const char *path) 41 | { 42 | int32_t time; 43 | struct stat statbuf; 44 | stat(path, &statbuf); 45 | time = statbuf.st_ctime; 46 | return time; 47 | } 48 | 49 | /* 50 | ** Set a file time stamp. 51 | */ 52 | void Set_File_Time(const char *path, int32_t time) 53 | { 54 | struct utimbuf stime; 55 | stime.modtime = stime.actime = time; 56 | utime(path, &stime); 57 | } 58 | 59 | /* 60 | ** Copy memory 61 | */ 62 | void Memcpy(void *dest, const void *src, long n) 63 | { 64 | if (n < 0) 65 | Bug("MM21", "MovInf"); /*move inf to zero */ 66 | if (n == 0) 67 | return; 68 | memcpy((char *) dest, (char *) src, (size_t) n); 69 | } 70 | 71 | /* 72 | ** Set memory 73 | */ 74 | void Memset(void *dest, char car, long n) 75 | { 76 | if (n < 0) 77 | Bug("MM11", "MStInf"); /*set inf to zero */ 78 | if (n == 0) 79 | return; 80 | memset(dest, car, (size_t) n); 81 | } 82 | 83 | /* 84 | ** Allocate memory 85 | */ 86 | /* actually, this is (size - 1) */ 87 | void *Malloc(long size) 88 | { 89 | void *ret; 90 | if (size < 1) { 91 | Warning("MM02", "Attempt to allocate %ld bytes", size); 92 | size = 1; 93 | } 94 | if ((size_t) size != size) 95 | ProgError("MM03", 96 | "Tried to allocate %ld b but couldn't; use another compiler", 97 | size); 98 | ret = malloc((size_t) size); 99 | if (ret == NULL) 100 | ProgError("MM04", "Out of memory (needed %ld bytes)", size); 101 | return ret; 102 | } 103 | 104 | /* 105 | ** Reallocate memory 106 | */ 107 | void *Realloc(void *old, long size) 108 | { 109 | void *ret; 110 | 111 | if (size < 1) { 112 | Warning("MM05", "Attempt to allocate %ld bytes", size); 113 | size = 1; 114 | } 115 | if ((size_t) size != size) 116 | ProgError("MM06", 117 | "Tried to realloc %ld b but couldn't; use another compiler", 118 | size); 119 | ret = realloc(old, (size_t) size); 120 | if (ret == NULL) 121 | ProgError("MM07", "Out of memory (needed %ld bytes)", size); 122 | return ret; 123 | } 124 | 125 | /* 126 | ** Use only lower case file names 127 | */ 128 | void ToLowerCase(char *file) 129 | { 130 | int16_t i; 131 | for (i = 0; (i < 128) && (file[i] != '\0'); i++) 132 | file[i] = tolower((((int16_t) file[i]) & 0xFF)); 133 | } 134 | 135 | static void NameDir(char file[128], const char *path, const char *dir, const 136 | char *sdir) 137 | { 138 | file[0] = '.'; 139 | file[1] = '\0'; 140 | if (path != NULL) 141 | if (strlen(path) > 0) { 142 | strncpy(file, path, 80); 143 | } 144 | if (dir != NULL) 145 | if (strlen(dir) > 0) { 146 | strcat(file, "/"); 147 | strncat(file, dir, 12); 148 | } 149 | if (sdir != NULL) 150 | if (strlen(sdir) > 0) { 151 | strcat(file, "/"); 152 | strncat(file, sdir, 12); 153 | } 154 | ToLowerCase(file); 155 | } 156 | 157 | /* 158 | ** Create directory if it does not exists 159 | */ 160 | void MakeDir(char file[128], const char *path, const char *dir, const char 161 | *sdir) 162 | { 163 | NameDir(file, path, dir, sdir); 164 | #ifdef _WIN32 165 | CreateDirectory(file, NULL); 166 | #else 167 | mkdir(file, (mode_t) 0777); 168 | #endif 169 | } 170 | 171 | /* 172 | ** Create a file name, by concatenation 173 | ** returns true if file exists false otherwise 174 | */ 175 | bool MakeFileName(char file[128], const char *path, const char *dir, const 176 | char *sdir, const char *name, const char *extens) 177 | { 178 | FILE *fp; 179 | char name2[8]; /* AYM 1999-01-13: keep checker happy */ 180 | /* deal with VILE strange name 181 | ** replace the VILE\ 182 | ** by VILE^ 183 | */ 184 | Normalise(name2, name); 185 | 186 | /* Replace backslash as it is an illegal character on 187 | Windows. */ 188 | switch (name2[4]) { 189 | case '\\': 190 | name2[4] = '^'; 191 | break; 192 | } 193 | switch (name2[6]) { 194 | case '\\': 195 | name2[6] = '^'; 196 | break; 197 | } 198 | NameDir(file, path, dir, sdir); 199 | /* 200 | ** file name 201 | */ 202 | strcat(file, "/"); 203 | strncat(file, name2, 8); 204 | strcat(file, "."); 205 | strncat(file, extens, 4); 206 | ToLowerCase(file); 207 | /* 208 | ** check if file exists 209 | */ 210 | fp = fopen(file, FOPEN_RB); 211 | if (fp != NULL) { 212 | fclose(fp); 213 | return true; 214 | } 215 | return false; 216 | } 217 | 218 | /* 219 | ** Get the root name of a WAD file 220 | */ 221 | void GetNameOfWAD(char name[8], const char *path) 222 | { 223 | int16_t n, nam, len; 224 | len = (int16_t) strlen(path); 225 | /*find end of DOS or Unix path */ 226 | for (nam = n = 0; n < len; n++) 227 | switch (path[n]) { 228 | #ifdef _WIN32 229 | case '\\': 230 | #endif 231 | case '/': 232 | nam = n + 1; 233 | } 234 | /*find root name */ 235 | /* FIXME AYM 1999-06-09: Do we really have to truncate to 8 ? */ 236 | for (n = 0; n < 8; n++) { 237 | switch (path[nam + n]) { 238 | case '.': 239 | case '\0': 240 | case '\n': 241 | name[n] = '\0'; 242 | return; 243 | default: 244 | name[n] = toupper(path[nam + n]); 245 | break; 246 | } 247 | } 248 | return; 249 | } 250 | 251 | /*****************************************************/ 252 | 253 | /* convert 8 byte string to upper case, 0 padded*/ 254 | void Normalise(char dest[8], const char *src) 255 | { /*strupr */ 256 | int16_t n; 257 | bool pad = false; 258 | char c = 'A'; 259 | for (n = 0; n < 8; n++) { 260 | c = pad ? '\0' : src[n]; 261 | if (c == '\0') 262 | pad = true; 263 | else 264 | c = (isprint(c)) ? toupper(c) : '*'; 265 | dest[n] = c; 266 | } 267 | } 268 | 269 | /* 270 | ** Output auxilliary functions 271 | */ 272 | 273 | /* 274 | * fnameofs - return string containing file name and offset 275 | * 276 | * Not reentrant (returns pointer on static buffer). 277 | * FIXME: should encode non-printable characters. 278 | * FIXME: should have shortening heuristic (E.G. print only basename). 279 | */ 280 | char *fnameofs(const char *name, long ofs) 281 | { 282 | static char buf[81]; 283 | *buf = '\0'; 284 | strncat(buf, name, sizeof buf - 12); 285 | sprintf(buf + strlen(buf), "(%06lXh)", ofs); 286 | return buf; 287 | } 288 | 289 | /* 290 | * fname - return string containing file name 291 | * 292 | * Not reentrant (returns pointer on static buffer). 293 | * FIXME: should encode non-printable characters. 294 | * FIXME: should have shortening heuristic (E.G. print only basename). 295 | */ 296 | char *fname(const char *name) 297 | { 298 | static char buf[81]; 299 | *buf = '\0'; 300 | strncat(buf, name, sizeof buf - 1); 301 | return buf; 302 | } 303 | 304 | /* 305 | * lump_name - return string containing lump name 306 | * 307 | * Partially reentrant (returns pointer on one of two 308 | * static buffers). The string is guaranteed to have at 309 | * most 32 characters and to contain only graphic 310 | * characters. 311 | */ 312 | char *lump_name(const char *name) 313 | { 314 | static char buf1[9]; 315 | static char buf2[9]; 316 | static int buf_toggle = 0; 317 | const char *const name_end = name + 8; 318 | char *buf = buf_toggle ? buf2 : buf1; 319 | char *p = buf; 320 | 321 | buf_toggle = !buf_toggle; 322 | if (*name == '\0') 323 | strcpy(buf, "(empty)"); 324 | else { 325 | for (; *name != '\0' && name < name_end; name++) { 326 | if (isgraph((unsigned char) *name)) 327 | *p++ = toupper((unsigned char) *name); 328 | else { 329 | *p++ = '\\'; 330 | *p++ = 'x'; 331 | *p++ = ((unsigned char) *name) >> 4; 332 | *p++ = *name & 0x0f; 333 | } 334 | } 335 | *p = '\0'; 336 | } 337 | return buf; 338 | } 339 | 340 | /* 341 | * short_dump - return string containing hex dump of buffer 342 | * 343 | * Not reentrant (returns pointer on static buffer). Length 344 | * is silently limited to 16 bytes. 345 | */ 346 | char *short_dump(const char *data, size_t size) 347 | { 348 | #define MAX_BYTES 16 349 | static char buf[3 * MAX_BYTES]; 350 | char *b = buf; 351 | size_t n; 352 | 353 | for (n = 0; n < size && n < MAX_BYTES; n++) { 354 | if (n > 0) 355 | *b++ = ' '; 356 | *b++ = hex_digit[((unsigned char) data[n]) >> 4]; 357 | *b++ = hex_digit[((unsigned char) data[n]) & 0x0f]; 358 | } 359 | *b++ = '\0'; 360 | return buf; 361 | } 362 | 363 | /* 364 | * quotechar - return the safe representation of a char 365 | * 366 | * Not reentrant (returns pointer on static buffer). The string is 367 | * guaranteed to be exactly three characters long and not contain 368 | * any control or non-ASCII characters. 369 | */ 370 | const char *quotechar(char c) 371 | { 372 | static char buf[4]; 373 | 374 | if (c >= 32 && c <= 126) { 375 | buf[0] = '"'; 376 | buf[1] = c; 377 | buf[2] = '"'; 378 | buf[3] = '\0'; 379 | } else { 380 | buf[0] = hex_digit[((unsigned char) c) >> 4]; 381 | buf[1] = hex_digit[((unsigned char) c) & 0x0f]; 382 | buf[2] = 'h'; 383 | buf[3] = '\0'; 384 | } 385 | return buf; 386 | } 387 | 388 | /* 389 | ** Output and Error handling 390 | */ 391 | static bool asFile = false; 392 | static int16_t Verbosity = 2; 393 | 394 | void PrintVerbosity(int16_t level) 395 | { 396 | Verbosity = (level < 0) ? 0 : (level > 4) ? 4 : level; 397 | } 398 | 399 | void PrintExit(void) 400 | { 401 | if (asFile) { 402 | fclose(stdout); 403 | fclose(stderr); 404 | } 405 | } 406 | 407 | void ActionDummy(void) 408 | { 409 | return; 410 | } 411 | 412 | static void (*Action) (void) = ActionDummy; 413 | void ProgErrorCancel(void) 414 | { 415 | Action = ActionDummy; 416 | } 417 | 418 | void ProgErrorAction(void (*action) (void)) 419 | { 420 | Action = action; 421 | } 422 | 423 | void ProgError(const char *code, const char *fmt, ...) 424 | { 425 | va_list args; 426 | 427 | fflush(stdout); 428 | fprintf(stderr, "E %s ", code); 429 | va_start(args, fmt); 430 | vfprintf(stderr, fmt, args); 431 | va_end(args); 432 | va_start(args, fmt); 433 | va_end(args); 434 | fputc('\n', stderr); 435 | (*Action) (); /* execute error handler */ 436 | PrintExit(); 437 | exit(2); 438 | } 439 | 440 | /* 441 | * nf_err - non fatal error message 442 | */ 443 | void nf_err(const char *code, const char *fmt, ...) 444 | { 445 | va_list args; 446 | 447 | fflush(stdout); 448 | fprintf(stderr, "%c %s ", MSGCLASS_ERR, code); 449 | va_start(args, fmt); 450 | vfprintf(stderr, fmt, args); 451 | va_end(args); 452 | va_start(args, fmt); 453 | va_end(args); 454 | fputc('\n', stderr); 455 | fflush(stderr); 456 | } 457 | 458 | void Bug(const char *code, const char *fmt, ...) 459 | { 460 | va_list args; 461 | 462 | fflush(stdout); 463 | fprintf(stderr, "%c %s ", MSGCLASS_BUG, code); 464 | va_start(args, fmt); 465 | vfprintf(stderr, fmt, args); 466 | va_end(args); 467 | va_start(args, fmt); 468 | va_end(args); 469 | fputc('\n', stderr); 470 | fputs("Please report that bug\n", stderr); 471 | /* CloseWadFiles(); */ 472 | PrintExit(); 473 | exit(3); 474 | } 475 | 476 | void Warning(const char *code, const char *fmt, ...) 477 | { 478 | va_list args; 479 | 480 | fflush(stdout); 481 | fprintf(stderr, "%c %s ", MSGCLASS_WARN, code); 482 | va_start(args, fmt); 483 | vfprintf(stderr, fmt, args); 484 | va_end(args); 485 | va_start(args, fmt); 486 | va_end(args); 487 | fputc('\n', stderr); 488 | } 489 | 490 | void LimitedWarn(int *left, const char *code, const char *fmt, ...) 491 | { 492 | if (left == NULL || (left != NULL && *left > 0)) { 493 | va_list args; 494 | 495 | fflush(stdout); 496 | fprintf(stderr, "%c %s ", MSGCLASS_WARN, code); 497 | va_start(args, fmt); 498 | vfprintf(stderr, fmt, args); 499 | va_end(args); 500 | va_start(args, fmt); 501 | va_end(args); 502 | fputc('\n', stderr); 503 | } 504 | if (left != NULL) 505 | (*left)--; 506 | } 507 | 508 | void LimitedEpilog(int *left, const char *code, const char *fmt, ...) 509 | { 510 | if (left != NULL && *left < 0) { 511 | fflush(stdout); 512 | if (fmt != NULL) { 513 | va_list args; 514 | fprintf(stderr, "%c %s ", MSGCLASS_WARN, code); 515 | va_start(args, fmt); 516 | vfprintf(stderr, fmt, args); 517 | va_end(args); 518 | va_start(args, fmt); 519 | va_end(args); 520 | } 521 | fprintf(stderr, "%d warnings omitted\n", -*left); 522 | } 523 | } 524 | 525 | void Output(const char *fmt, ...) 526 | { 527 | va_list args; 528 | 529 | va_start(args, fmt); 530 | vfprintf(stdout, fmt, args); 531 | va_end(args); 532 | va_start(args, fmt); 533 | va_end(args); 534 | } 535 | 536 | void Info(const char *code, const char *fmt, ...) 537 | { 538 | if (Verbosity >= 1) { 539 | va_list args; 540 | 541 | fprintf(stdout, "%c %s ", MSGCLASS_INFO, code); 542 | va_start(args, fmt); 543 | vfprintf(stdout, fmt, args); 544 | va_end(args); 545 | va_start(args, fmt); 546 | va_end(args); 547 | fputc('\n', stdout); 548 | } 549 | } 550 | 551 | void Phase(const char *code, const char *fmt, ...) 552 | { 553 | if (Verbosity >= 2) { 554 | va_list args; 555 | 556 | fprintf(stdout, "%c %s ", MSGCLASS_INFO, code); 557 | va_start(args, fmt); 558 | vfprintf(stdout, fmt, args); 559 | va_end(args); 560 | va_start(args, fmt); 561 | va_end(args); 562 | fputc('\n', stdout); 563 | } 564 | } 565 | 566 | void Detail(const char *code, const char *fmt, ...) 567 | { 568 | if (Verbosity >= 3) { 569 | va_list args; 570 | 571 | fprintf(stdout, "%c %s ", MSGCLASS_INFO, code); 572 | va_start(args, fmt); 573 | vfprintf(stdout, fmt, args); 574 | va_end(args); 575 | va_start(args, fmt); 576 | va_end(args); 577 | fputc('\n', stdout); 578 | } 579 | } 580 | -------------------------------------------------------------------------------- /src/tools.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ® Olivier Montanuy, 3 | André Majorel, 4 | contributors to the DeuTex project. 5 | 6 | DeuTex incorporates code derived from DEU 5.21 that was put in the 7 | public domain in 1994 by Raphaël Quinet and Brendon Wyber. 8 | 9 | SPDX-License-Identifier: GPL-2.0-or-later 10 | */ 11 | #ifndef TOOLS_H 12 | #define TOOLS_H 13 | 14 | #define MSGCLASS_INFO 'i' 15 | #define MSGCLASS_WARN 'w' 16 | #define MSGCLASS_ERR 'E' 17 | #define MSGCLASS_BUG 'B' 18 | 19 | void PrintCopyright(void); 20 | void print_version(void); 21 | void NoCommandGiven(void); 22 | 23 | char *fnameofs(const char *name, long ofs); 24 | char *fname(const char *name); 25 | char *lump_name(const char *name); 26 | char *short_dump(const char *data, size_t size); 27 | const char *quotechar(char c); 28 | 29 | void PrintVerbosity(int16_t level); 30 | void PrintExit(void); 31 | 32 | void ProgErrorCancel(void); 33 | void ProgErrorAction(void (*action) (void)); 34 | void ProgError(const char *code, const char *fmt, ...); /*fatal error. halt. */ 35 | void nf_err(const char *code, const char *fmt, ...); /* Non-fatal error */ 36 | void Bug(const char *code, const char *fmt, ...); /*fatal bug. halt */ 37 | void Warning(const char *code, const char *str, ...); /*I'm not happy */ 38 | void LimitedWarn(int *left, const char *code, const char *fmt, ...); 39 | void LimitedEpilog(int *left, const char *code, const char *fmt, ...); 40 | void Output(const char *fmt, ...); /*command text output */ 41 | void Info(const char *code, const char *fmt, ...); /*useful indications */ 42 | void Phase(const char *code, const char *fmt, ...); /*phase of executions */ 43 | void Detail(const char *code, const char *fmt, ...); /*technical details */ 44 | 45 | void ToLowerCase(char *file); 46 | void MakeDir(char file[128], const char *path, const char *dir, const char 47 | *sdir); 48 | bool MakeFileName(char file[128], const char *path, const char *dir, const 49 | char *sdir, const char *name, const char *extens); 50 | void GetNameOfWAD(char name[8], const char *path); 51 | int32_t Get_File_Time(const char *path); 52 | void Set_File_Time(const char *path, int32_t time); 53 | void Memcpy(void *dest, const void *src, long n); 54 | void Memset(void *dest, char car, long n); 55 | void *Malloc(long size); 56 | void *Realloc(void *old, long size); 57 | 58 | void Normalise(char dest[8], const char *src); 59 | 60 | void Progress(void); 61 | void ProgressEnds(void); 62 | 63 | #endif //TOOLS_H 64 | -------------------------------------------------------------------------------- /src/usedidx.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ® Olivier Montanuy, 3 | André Majorel, 4 | contributors to the DeuTex project. 5 | 6 | DeuTex incorporates code derived from DEU 5.21 that was put in the 7 | public domain in 1994 by Raphaël Quinet and Brendon Wyber. 8 | 9 | SPDX-License-Identifier: GPL-2.0-or-later 10 | */ 11 | /* 12 | * usedidx.c 13 | * Palette index usage statistics (cf -usedidx) 14 | */ 15 | 16 | #include "deutex.h" 17 | #include "tools.h" 18 | #include "usedidx.h" 19 | 20 | /* 21 | * usedidx_begin_lump 22 | * Call this before the first call to usedidx_count for 23 | * a given lump. 24 | */ 25 | void usedidx_begin_lump(cusage_t * cusage, const char *name) 26 | { 27 | int i; 28 | 29 | if (cusage == NULL) 30 | Bug("UI10", "usedidx_begin_lump: null cusage"); 31 | cusage->lump_uses = Malloc(NCOLOURS * sizeof *cusage->lump_uses); 32 | for (i = 0; i < NCOLOURS; i++) 33 | cusage->lump_uses[i] = 0; 34 | memcpy(cusage->lump_name, name, sizeof cusage->lump_name); 35 | } 36 | 37 | /* 38 | * usedidx_pixel 39 | * Call this for each pixel for a given lump. 40 | */ 41 | void usedidx_pixel(cusage_t * cusage, unsigned char idx) 42 | { 43 | cusage->lump_uses[idx]++; 44 | } 45 | 46 | /* 47 | * usedidx_end_lump 48 | * Call this when you're done with a given lump. 49 | */ 50 | void usedidx_end_lump(cusage_t * cusage) 51 | { 52 | int i; 53 | if (cusage == NULL) 54 | Bug("UI20", "usedidx_end_lump: null cusage"); 55 | for (i = 0; i < NCOLOURS; i++) { 56 | if (cusage->lump_uses[i] > 0) { 57 | if (cusage->uses[i] == 0) 58 | memcpy(cusage->where_first[i], cusage->lump_name, 59 | sizeof cusage->where_first[i]); 60 | cusage->uses[i] += cusage->lump_uses[i]; 61 | cusage->nlumps[i]++; 62 | } 63 | } 64 | free(cusage->lump_uses); 65 | } 66 | 67 | /* 68 | * usedidx_rectangle 69 | * Call this to update the cusage_t structure for a whole 70 | * array of pixels at once. The array is supposed not to 71 | * contain transparent pixels so this is not for pictures; 72 | * it's for flats and rectangular graphic lumps (E.G. 73 | * 320x200 "TITLE" from Heretic or Hexen and 10x12 74 | * "GNUM[0-9]" from Doom alpha.) 75 | */ 76 | void usedidx_rectangle(const char *buf, long buf_size, const char *name, 77 | cusage_t * cusage) 78 | { 79 | const unsigned char *p = (const unsigned char *) buf; 80 | const unsigned char *pmax = p + buf_size; 81 | unsigned long *uses = Malloc(NCOLOURS * sizeof *uses); 82 | int i; 83 | 84 | for (i = 0; i < NCOLOURS; i++) 85 | uses[i] = 0; 86 | for (; p < pmax; p++) 87 | uses[*p]++; 88 | for (i = 0; i < NCOLOURS; i++) { 89 | if (uses[i] > 0) { 90 | if (cusage->uses[i] == 0) 91 | memcpy(cusage->where_first[i], name, 8); 92 | cusage->uses[i] += uses[i]; 93 | cusage->nlumps[i]++; 94 | } 95 | } 96 | free(uses); 97 | } 98 | -------------------------------------------------------------------------------- /src/usedidx.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ® Olivier Montanuy, 3 | André Majorel, 4 | contributors to the DeuTex project. 5 | 6 | DeuTex incorporates code derived from DEU 5.21 that was put in the 7 | public domain in 1994 by Raphaël Quinet and Brendon Wyber. 8 | 9 | SPDX-License-Identifier: GPL-2.0-or-later 10 | */ 11 | /* 12 | * usedidx.h 13 | * Palette index usage statistics (cf -usedidx) 14 | */ 15 | 16 | #ifndef DT_USEDIDX_H 17 | #define DT_USEDIDX_H 18 | 19 | /* This block is used in conjunction with -usedidx. Avoid 20 | messing with its fields directly. Use the API instead. */ 21 | struct cusage_s { 22 | /* Per-lump: */ 23 | char lump_name[8]; 24 | unsigned long *lump_uses; 25 | 26 | /* Totals: */ 27 | unsigned long uses[NCOLOURS]; 28 | unsigned long nlumps[NCOLOURS]; 29 | char where_first[NCOLOURS][8]; 30 | }; 31 | 32 | void usedidx_begin_lump(cusage_t * cusage, const char *name); 33 | void usedidx_pixel(cusage_t * cusage, unsigned char idx); 34 | void usedidx_end_lump(cusage_t * cusage); 35 | void usedidx_rectangle(const char *buf, long buf_size, const char *name, 36 | cusage_t * cusage); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /src/wadio.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ® Olivier Montanuy, 3 | André Majorel, 4 | contributors to the DeuTex project. 5 | 6 | DeuTex incorporates code derived from DEU 5.21 that was put in the 7 | public domain in 1994 by Raphaël Quinet and Brendon Wyber. 8 | 9 | SPDX-License-Identifier: GPL-2.0-or-later 10 | */ 11 | /* 12 | * wadio.c 13 | * Wad low level I/O routines, without error checking. 14 | * AYM 1999-03-06 15 | */ 16 | 17 | #include "deutex.h" 18 | #include 19 | #include "endianio.h" 20 | #include "wadio.h" 21 | 22 | /* 23 | * wad_read_i16 24 | * wad_read_i32 25 | * wad_write_i16 26 | * wad_write_i32 27 | * 28 | * Read and write 16-bit and 32-bit signed integers, with 29 | * the defined endianness. By default, all wad I/O is done 30 | * in little-endian mode. 31 | * 32 | * Return 0 on success, non-zero on failure. 33 | */ 34 | int (*wad_write_i16) (FILE *, int16_t) = fwrite_i16_le; 35 | int (*wad_write_i32) (FILE *, int32_t) = fwrite_i32_le; 36 | int (*wad_read_i16) (FILE *, int16_t *) = fread_i16_le; 37 | int (*wad_read_i32) (FILE *, int32_t *) = fread_i32_le; 38 | 39 | /* 40 | * set_input_wad_endianness 41 | * set_output_wad_endianness 42 | * 43 | * Define the endianness to use for wad input and output 44 | * respectively. Normally, all wads are little-endian. 45 | */ 46 | void set_output_wad_endianness(int big_endian) 47 | { 48 | wad_write_i16 = big_endian ? fwrite_i16_be : fwrite_i16_le; 49 | wad_write_i32 = big_endian ? fwrite_i32_be : fwrite_i32_le; 50 | } 51 | 52 | void set_input_wad_endianness(int big_endian) 53 | { 54 | wad_read_i16 = big_endian ? fread_i16_be : fread_i16_le; 55 | wad_read_i32 = big_endian ? fread_i32_be : fread_i32_le; 56 | } 57 | 58 | /* 59 | * wad_read_name 60 | * wad_write_name 61 | * 62 | * Read and write a directory name to a wad. The name is 63 | * truncated to 8 characters, upper-cased, terminated and 64 | * padded to 8 with NULs. 65 | * 66 | * Return 0 on success, non-zero on failure. 67 | */ 68 | int wad_read_name(FILE * fd, char name[8]) 69 | { 70 | size_t n; 71 | int end = 0; 72 | for (n = 0; n < 8; n++) { 73 | int c = getc(fd); 74 | name[n] = end ? '\0' : toupper(c); 75 | if (c == '\0') 76 | end = 1; 77 | } 78 | return feof(fd) || ferror(fd); 79 | } 80 | 81 | int wad_write_name(FILE * fd, const char *name) 82 | { 83 | size_t n; 84 | for (n = 0; n < 8 && name[n]; n++) 85 | putc(toupper(name[n]), fd); 86 | for (; n < 8; n++) 87 | putc('\0', fd); 88 | return ferror(fd); 89 | } 90 | -------------------------------------------------------------------------------- /src/wadio.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright ® Olivier Montanuy, 3 | André Majorel, 4 | contributors to the DeuTex project. 5 | 6 | DeuTex incorporates code derived from DEU 5.21 that was put in the 7 | public domain in 1994 by Raphaël Quinet and Brendon Wyber. 8 | 9 | SPDX-License-Identifier: GPL-2.0-or-later 10 | */ 11 | /* 12 | * wadio.h 13 | * Wad low level I/O routines 14 | * AYM 1999-03-06 15 | */ 16 | 17 | void set_output_wad_endianness(int big_endian); 18 | void set_input_wad_endianness(int big_endian); 19 | extern int (*wad_write_i16) (FILE *, int16_t); 20 | extern int (*wad_write_i32) (FILE *, int32_t); 21 | extern int (*wad_read_i16) (FILE *, int16_t *); 22 | extern int (*wad_read_i32) (FILE *, int32_t *); 23 | int wad_read_name(FILE * fd, char name[8]); 24 | int wad_write_name(FILE * fd, const char *name); 25 | --------------------------------------------------------------------------------