From 24d4f0b45c4c8a9e0b4f4b64d3274abd10875f30 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Fri, 24 Feb 2006 05:17:19 +0000 Subject: [PATCH] Initial commit of zdbsp. SVN r12 (trunk) --- COPYING | 340 ++++++++ Unused/nodebuild.cpp-nogl | 1146 +++++++++++++++++++++++++ Unused/nodebuild.h-nogl | 98 +++ Unused/rejectbuilder.cpp | 215 +++++ Unused/rejectbuilder.cpp-nogl | 330 ++++++++ Unused/rejectbuilder.cpp-slow | 278 ++++++ Unused/rejectbuilder.h | 211 +++++ Unused/rejectbuilder.h-nogl | 77 ++ Unused/rejectbuilder.h-slow | 40 + Unused/vis.cpp | 580 +++++++++++++ Unused/visflow.cpp | 1345 +++++++++++++++++++++++++++++ blockmapbuilder.cpp | 455 ++++++++++ blockmapbuilder.h | 18 + doomdata.h | 177 ++++ getopt.c | 1053 +++++++++++++++++++++++ getopt.h | 133 +++ getopt1.c | 189 +++++ main.cpp | 524 ++++++++++++ nodebuild.cpp | 1108 ++++++++++++++++++++++++ nodebuild.h | 189 +++++ nodebuild_events.cpp | 407 +++++++++ nodebuild_extract.cpp | 349 ++++++++ nodebuild_gl.cpp | 388 +++++++++ nodebuild_utility.cpp | 500 +++++++++++ poly_bad.png | Bin 0 -> 471 bytes poly_mov.png | Bin 0 -> 458 bytes poly_new.png | Bin 0 -> 457 bytes processor.cpp | 1268 ++++++++++++++++++++++++++++ processor.h | 95 +++ resource.h | 20 + resource.rc | 103 +++ tarray.h | 207 +++++ templates.h | 202 +++++ view.cpp | 1049 +++++++++++++++++++++++ wad.cpp | 423 ++++++++++ wad.h | 107 +++ workdata.h | 29 + zdbsp.dsp | 398 +++++++++ zdbsp.dsw | 29 + zdbsp.h | 134 +++ zdbsp.html | 476 +++++++++++ zdbsp.sln | 21 + zdbsp.vcproj | 334 ++++++++ zipbin.bat | 1 + zipsrc.bat | 4 + zlib/adler32.c | 74 ++ zlib/compress.c | 79 ++ zlib/crc32.c | 311 +++++++ zlib/crc32.h | 441 ++++++++++ zlib/deflate.c | 1502 +++++++++++++++++++++++++++++++++ zlib/deflate.h | 326 +++++++ zlib/trees.c | 1215 ++++++++++++++++++++++++++ zlib/trees.h | 128 +++ zlib/zconf.h | 323 +++++++ zlib/zlib.h | 1200 ++++++++++++++++++++++++++ zlib/zutil.c | 319 +++++++ zlib/zutil.h | 258 ++++++ 57 files changed, 21226 insertions(+) create mode 100644 COPYING create mode 100644 Unused/nodebuild.cpp-nogl create mode 100644 Unused/nodebuild.h-nogl create mode 100644 Unused/rejectbuilder.cpp create mode 100644 Unused/rejectbuilder.cpp-nogl create mode 100644 Unused/rejectbuilder.cpp-slow create mode 100644 Unused/rejectbuilder.h create mode 100644 Unused/rejectbuilder.h-nogl create mode 100644 Unused/rejectbuilder.h-slow create mode 100644 Unused/vis.cpp create mode 100644 Unused/visflow.cpp create mode 100644 blockmapbuilder.cpp create mode 100644 blockmapbuilder.h create mode 100644 doomdata.h create mode 100644 getopt.c create mode 100644 getopt.h create mode 100644 getopt1.c create mode 100644 main.cpp create mode 100644 nodebuild.cpp create mode 100644 nodebuild.h create mode 100644 nodebuild_events.cpp create mode 100644 nodebuild_extract.cpp create mode 100644 nodebuild_gl.cpp create mode 100644 nodebuild_utility.cpp create mode 100644 poly_bad.png create mode 100644 poly_mov.png create mode 100644 poly_new.png create mode 100644 processor.cpp create mode 100644 processor.h create mode 100644 resource.h create mode 100644 resource.rc create mode 100644 tarray.h create mode 100644 templates.h create mode 100644 view.cpp create mode 100644 wad.cpp create mode 100644 wad.h create mode 100644 workdata.h create mode 100644 zdbsp.dsp create mode 100644 zdbsp.dsw create mode 100644 zdbsp.h create mode 100644 zdbsp.html create mode 100644 zdbsp.sln create mode 100644 zdbsp.vcproj create mode 100644 zipbin.bat create mode 100644 zipsrc.bat create mode 100644 zlib/adler32.c create mode 100644 zlib/compress.c create mode 100644 zlib/crc32.c create mode 100644 zlib/crc32.h create mode 100644 zlib/deflate.c create mode 100644 zlib/deflate.h create mode 100644 zlib/trees.c create mode 100644 zlib/trees.h create mode 100644 zlib/zconf.h create mode 100644 zlib/zlib.h create mode 100644 zlib/zutil.c create mode 100644 zlib/zutil.h diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Unused/nodebuild.cpp-nogl b/Unused/nodebuild.cpp-nogl new file mode 100644 index 0000000..ab164c7 --- /dev/null +++ b/Unused/nodebuild.cpp-nogl @@ -0,0 +1,1146 @@ +#include +#include +#include +#include +#include +#include + +#include "zdbsp.h" +#include "nodebuild.h" +#include "templates.h" + +static const int PO_LINE_START = 1; +static const int PO_LINE_EXPLICIT = 5; + +#define Printf printf +#define STACK_ARGS + +#if 0 +#define D(x) x +#else +#define D(x) do{}while(0) +#endif + +#if 0 +#define P(x) x +#else +#define P(x) do{}while(0) +#endif + +FNodeBuilder::FNodeBuilder (FLevel &level, + TArray &polyspots, TArray &anchors, + const char *name) + : Level (level), SegsStuffed (0), MapName (name) +{ + FindUsedVertices (Level.Vertices, Level.NumVertices); + MakeSegsFromSides (); + FindPolyContainers (polyspots, anchors); + GroupSegPlanes (); + BuildTree (); +} + +void FNodeBuilder::FindUsedVertices (WideVertex *oldverts, int max) +{ + size_t *map = (size_t *)alloca (max*sizeof(size_t)); + int i; + FPrivVert newvert; + + memset (&map[0], -1, sizeof(size_t)*max); + + newvert.segs = NO_INDEX; + for (i = 0; i < Level.NumLines; ++i) + { + int v1 = Level.Lines[i].v1; + int v2 = Level.Lines[i].v2; + + if (map[v1] == (size_t)-1) + { + newvert.x = oldverts[v1].x; + newvert.y = oldverts[v1].y; + map[v1] = Vertices.Push (newvert); + } + if (map[v2] == (size_t)-1) + { + newvert.x = oldverts[v2].x; + newvert.y = oldverts[v2].y; + map[v2] = Vertices.Push (newvert); + } + + Level.Lines[i].v1 = map[v1]; + Level.Lines[i].v2 = map[v2]; + } +} + +void FNodeBuilder::MakeSegsFromSides () +{ + FPrivSeg seg; + int i, j; + + seg.next = NO_INDEX; + seg.loopnum = 0; + seg.offset = 0; + + for (i = 0; i < Level.NumLines; ++i) + { + if (Level.Lines[i].sidenum[0] != NO_INDEX) + { + WORD backside; + + seg.linedef = i; + seg.sidedef = Level.Lines[i].sidenum[0]; + backside = Level.Lines[i].sidenum[1]; + seg.frontsector = Level.Sides[seg.sidedef].sector; + seg.backsector = backside != NO_INDEX ? Level.Sides[backside].sector : -1; + seg.v1 = Level.Lines[i].v1; + seg.v2 = Level.Lines[i].v2; + seg.nextforvert = Vertices[seg.v1].segs; + seg.angle = PointToAngle (Vertices[seg.v2].x-Vertices[seg.v1].x, + Vertices[seg.v2].y-Vertices[seg.v1].y); + j = (int)Segs.Push (seg); + Vertices[seg.v1].segs = j; + } + else + { + printf ("Linedef %d does not have a front side.\n", i); + } + + if (Level.Lines[i].sidenum[1] != NO_INDEX) + { + WORD backside; + + seg.linedef = i; + seg.sidedef = Level.Lines[i].sidenum[1]; + backside = Level.Lines[i].sidenum[0]; + seg.frontsector = Level.Sides[seg.sidedef].sector; + seg.backsector = backside != NO_INDEX ? Level.Sides[backside].sector : -1; + seg.v1 = Level.Lines[i].v2; + seg.v2 = Level.Lines[i].v1; + seg.nextforvert = Vertices[seg.v1].segs; + seg.angle = PointToAngle (Vertices[seg.v2].x-Vertices[seg.v1].x, + Vertices[seg.v2].y-Vertices[seg.v1].y); + j = (int)Segs.Push (seg); + Vertices[seg.v1].segs = j; + } + } +} + +void FNodeBuilder::GroupSegPlanes () +{ + const int bucketbits = 12; + FPrivSeg *buckets[1<next = i+1; + seg->hashnext = NULL; + } + + Segs[Segs.Size()-1].next = NO_INDEX; + + for (i = planenum = 0; i < (int)Segs.Size(); ++i) + { + FPrivSeg *seg = &Segs[i]; + fixed_t x1 = Vertices[seg->v1].x; + fixed_t y1 = Vertices[seg->v1].y; + fixed_t x2 = Vertices[seg->v2].x; + fixed_t y2 = Vertices[seg->v2].y; + angle_t ang = PointToAngle (x2 - x1, y2 - y1); + + if (ang >= 1<<31) + ang += 1<<31; + + FPrivSeg *check = buckets[ang >>= 31-bucketbits]; + + while (check != NULL) + { + fixed_t cx1 = Vertices[check->v1].x; + fixed_t cy1 = Vertices[check->v1].y; + fixed_t cdx = Vertices[check->v2].x - cx1; + fixed_t cdy = Vertices[check->v2].y - cy1; + if (PointOnSide (x1, y1, cx1, cy1, cdx, cdy) == 0 && + PointOnSide (x2, y2, cx1, cy1, cdx, cdy) == 0) + { + break; + } + check = check->hashnext; + } + if (check != NULL) + { + seg->planenum = check->planenum; + } + else + { + seg->hashnext = buckets[ang]; + buckets[ang] = seg; + seg->planenum = planenum++; + } + } + + D(Printf ("%d planes from %d segs\n", planenum, Segs.Size())); + + planenum = (planenum+7)/8; + PlaneChecked.Reserve (planenum); +} + +void FNodeBuilder::FindPolyContainers (TArray &spots, TArray &anchors) +{ + int loop = 1; + + for (size_t i = 0; i < spots.Size(); ++i) + { + FPolyStart *spot = &spots[i]; + fixed_t bbox[4]; + + if (GetPolyExtents (spot->polynum, bbox)) + { + FPolyStart *anchor; + + size_t j; + + for (j = 0; j < anchors.Size(); ++j) + { + anchor = &anchors[j]; + if (anchor->polynum == spot->polynum) + { + break; + } + } + + if (j < anchors.Size()) + { + vertex_t mid; + vertex_t center; + + mid.x = bbox[BOXLEFT] + (bbox[BOXRIGHT]-bbox[BOXLEFT])/2; + mid.y = bbox[BOXBOTTOM] + (bbox[BOXTOP]-bbox[BOXBOTTOM])/2; + + center.x = mid.x - anchor->x + spot->x; + center.y = mid.y - anchor->y + spot->y; + + // Scan right for the seg closest to the polyobject's center after it + // gets moved to its start spot. + fixed_t closestdist = FIXED_MAX; + long closestseg = 0; + + P(Printf ("start %d,%d -- center %d, %d\n", spot->x>>16, spot->y>>16, center.x>>16, center.y>>16)); + + for (size_t j = 0; j < Segs.Size(); ++j) + { + FPrivSeg *seg = &Segs[j]; + FPrivVert *v1 = &Vertices[seg->v1]; + FPrivVert *v2 = &Vertices[seg->v2]; + fixed_t dy = v2->y - v1->y; + + if (dy == 0) + { // Horizontal, so skip it + continue; + } + if ((v1->y < center.y && v2->y < center.y) || (v1->y > center.y && v2->y > center.y)) + { // Not crossed + continue; + } + + fixed_t dx = v2->x - v1->x; + + if (PointOnSide (center.x, center.y, v1->x, v1->y, dx, dy) <= 0) + { + fixed_t t = DivScale30 (center.y - v1->y, dy); + fixed_t sx = v1->x + MulScale30 (dx, t); + fixed_t dist = sx - spot->x; + + if (dist < closestdist && dist >= 0) + { + closestdist = dist; + closestseg = (long)j; + } + } + } + if (closestseg >= 0) + { + loop = MarkLoop (closestseg, loop); + P(Printf ("Found polyobj in sector %d (loop %d)\n", Segs[closestseg].frontsector, + Segs[closestseg].loopnum)); + } + } + } + } +} + +bool FNodeBuilder::GetPolyExtents (int polynum, fixed_t bbox[4]) +{ + size_t i; + + bbox[BOXLEFT] = bbox[BOXBOTTOM] = FIXED_MAX; + bbox[BOXRIGHT] = bbox[BOXTOP] = FIXED_MIN; + + // Try to find a polyobj marked with a start line + for (i = 0; i < Segs.Size(); ++i) + { + if (Level.Lines[Segs[i].linedef].special == PO_LINE_START && + Level.Lines[Segs[i].linedef].args[0] == polynum) + { + break; + } + } + + if (i < Segs.Size()) + { + vertex_t start; + size_t vert; + + vert = Segs[i].v1; + + start.x = Vertices[vert].x; + start.y = Vertices[vert].y; + + do + { + AddSegToBBox (bbox, &Segs[i]); + vert = Segs[i].v2; + i = Vertices[vert].segs; + } while (i != NO_INDEX && (Vertices[vert].x != start.x || Vertices[vert].y != start.y)); + + return true; + } + + // Try to find a polyobj marked with explicit lines + bool found = false; + + for (i = 0; i < Segs.Size(); ++i) + { + if (Level.Lines[Segs[i].linedef].special == PO_LINE_EXPLICIT && + Level.Lines[Segs[i].linedef].args[0] == polynum) + { + AddSegToBBox (bbox, &Segs[i]); + found = true; + } + } + return found; +} + +void FNodeBuilder::AddSegToBBox (fixed_t bbox[4], const FPrivSeg *seg) +{ + FPrivVert *v1 = &Vertices[seg->v1]; + FPrivVert *v2 = &Vertices[seg->v2]; + + if (v1->x < bbox[BOXLEFT]) bbox[BOXLEFT] = v1->x; + if (v1->x > bbox[BOXRIGHT]) bbox[BOXRIGHT] = v1->x; + if (v1->y < bbox[BOXBOTTOM]) bbox[BOXBOTTOM] = v1->y; + if (v1->y > bbox[BOXTOP]) bbox[BOXTOP] = v1->y; + + if (v2->x < bbox[BOXLEFT]) bbox[BOXLEFT] = v2->x; + if (v2->x > bbox[BOXRIGHT]) bbox[BOXRIGHT] = v2->x; + if (v2->y < bbox[BOXBOTTOM]) bbox[BOXBOTTOM] = v2->y; + if (v2->y > bbox[BOXTOP]) bbox[BOXTOP] = v2->y; +} + +int FNodeBuilder::MarkLoop (int firstseg, int loopnum) +{ + int seg; + int sec = Segs[firstseg].frontsector; + + if (Segs[firstseg].loopnum != 0) + { // already marked + return loopnum; + } + + seg = firstseg; + + do + { + FPrivSeg *s1 = &Segs[seg]; + + s1->loopnum = loopnum; + + P(Printf ("Mark seg %d (%d,%d)-(%d,%d)\n", seg, + Vertices[s1->v1].x>>16, Vertices[s1->v1].y>>16, + Vertices[s1->v2].x>>16, Vertices[s1->v2].y>>16)); + + int bestseg = NO_INDEX; + int tryseg = Vertices[s1->v2].segs; + angle_t bestang = ANGLE_MAX; + angle_t ang1 = s1->angle; + + while (tryseg != NO_INDEX) + { + FPrivSeg *s2 = &Segs[tryseg]; + + if (s2->frontsector == sec) + { + angle_t ang2 = s2->angle + (1<<31); + angle_t angdiff = ang2 - ang1; + + if (angdiff < bestang && angdiff > 0) + { + bestang = angdiff; + bestseg = tryseg; + } + } + tryseg = s2->nextforvert; + } + + seg = bestseg; + } while (seg != NO_INDEX && Segs[seg].loopnum == 0); + + return loopnum + 1; +} + +void FNodeBuilder::BuildTree () +{ + fixed_t bbox[4]; + + printf (" BSP: 0.0%%\r"); + CreateNode (0, bbox); + printf (" BSP: 100.0%%\n"); +} + +int FNodeBuilder::CreateNode (WORD set, fixed_t bbox[4]) +{ + node_t node; + int skip, count, selstat; + + count = CountSegs (set); + skip = count / MaxSegs; + + if ((selstat = SelectSplitter (set, node, skip, true)) > 0 || + (skip > 0 && (selstat = SelectSplitter (set, node, 1, true)) > 0) || + (selstat < 0 && (SelectSplitter (set, node, skip, false) > 0) || + (skip > 0 && SelectSplitter (set, node, 1, false))) || + CheckSubsector (set, node, count)) + { + // Create a normal node + WORD set1, set2; + + SplitSegs (set, node, set1, set2); + D(PrintSet (1, set1)); + D(Printf ("(%d,%d) delta (%d,%d)\n", node.x>>16, node.y>>16, node.dx>>16, node.dy>>16)); + D(PrintSet (2, set2)); + node.children[0] = CreateNode (set1, node.bbox[0]); + node.children[1] = CreateNode (set2, node.bbox[1]); + bbox[BOXTOP] = MAX (node.bbox[0][BOXTOP], node.bbox[1][BOXTOP]); + bbox[BOXBOTTOM] = MIN (node.bbox[0][BOXBOTTOM], node.bbox[1][BOXBOTTOM]); + bbox[BOXLEFT] = MIN (node.bbox[0][BOXLEFT], node.bbox[1][BOXLEFT]); + bbox[BOXRIGHT] = MAX (node.bbox[0][BOXRIGHT], node.bbox[1][BOXRIGHT]); + return (int)Nodes.Push (node); + } + else + { + return NF_SUBSECTOR | CreateSubsector (set, bbox); + } +} + +int FNodeBuilder::CreateSubsector (WORD set, fixed_t bbox[4]) +{ + MapSubsector sub; + + bbox[BOXTOP] = bbox[BOXRIGHT] = INT_MIN; + bbox[BOXBOTTOM] = bbox[BOXLEFT] = INT_MAX; + + D(Printf ("Subsector from set %d\n", set)); + + assert (set != NO_INDEX); + + sub.firstline = (WORD)SegList.Size(); + + while (set != NO_INDEX) + { + USegPtr ptr; + + ptr.SegPtr = &Segs[set]; + AddSegToBBox (bbox, ptr.SegPtr); + SegList.Push (ptr); + set = ptr.SegPtr->next; + } + sub.numlines = (WORD)(SegList.Size() - sub.firstline); + + // Sort segs by linedef for special effects + qsort (&SegList[sub.firstline], sub.numlines, sizeof(int), SortSegs); + + // Convert seg pointers into indices + for (size_t i = sub.firstline; i < SegList.Size(); ++i) + { + SegList[i].SegNum = SegList[i].SegPtr - &Segs[0]; + } + + SegsStuffed += sub.numlines; + if ((SegsStuffed & ~63) != ((SegsStuffed - sub.numlines) & ~63)) + { + int percent = (int)(SegsStuffed * 1000.0 / Segs.Size()); + printf (" BSP: %3d.%d%%\r", percent/10, percent%10); + } + + D(Printf ("bbox (%d,%d)-(%d,%d)\n", bbox[BOXLEFT]>>16, bbox[BOXBOTTOM]>>16, bbox[BOXRIGHT]>>16, bbox[BOXTOP]>>16)); + + return (int)Subsectors.Push (sub); +} + +int STACK_ARGS FNodeBuilder::SortSegs (const void *a, const void *b) +{ + const FPrivSeg *x = ((const USegPtr *)a)->SegPtr; + const FPrivSeg *y = ((const USegPtr *)b)->SegPtr; + + // Segs with the same sector on the back and front belong at the end of the list. + // This is so that the subsector does not inherit its sector from them. + + if (x->frontsector == x->backsector && y->frontsector != y->backsector) + { + return 1; + } + else if (y->frontsector == y->backsector && x->frontsector != x->backsector) + { + return -1; + } + else if (x->linedef == y->linedef) + { + return x - y; + } + else + { + return x->linedef - y->linedef; + } +} + +int FNodeBuilder::CountSegs (WORD set) const +{ + int count = 0; + + while (set != NO_INDEX) + { + count++; + set = Segs[set].next; + } + return count; +} + +// Given a set of segs, checks to make sure they all belong to a single +// sector. If so, false is returned, and they become a subsector. If not, +// a splitter is synthesized, and true is returned to continue processing +// down this branch of the tree. + +bool FNodeBuilder::CheckSubsector (WORD set, node_t &node, int setsize) +{ + int sec; + int seg; + + sec = -1; + seg = set; + + do + { + if (Segs[seg].frontsector != sec&& + // Segs with the same front and back sectors are allowed to reside + // in a subsector with segs from a different sector, because the + // only effect they can have on the display is to place masked + // mid textures. + Segs[seg].frontsector != Segs[seg].backsector) + { + if (sec == -1) + { + sec = Segs[seg].frontsector; + } + else + { + break; + } + } + seg = Segs[seg].next; + } while (seg != NO_INDEX); + + if (seg == NO_INDEX) + { // It's a valid subsector + return false; + } + + D(Printf("Need to synthesize a splitter for set %d\n", set)); + + // If there are only two segs in the set, and they form two sides + // of a triangle, the splitter should pass through their shared + // point and the (imaginary) third side of the triangle + if (setsize == 2) + { + FPrivVert *v1, *v2, *v3; + + if (Vertices[Segs[set].v2] == Vertices[Segs[seg].v1]) + { + v1 = &Vertices[Segs[set].v1]; + v2 = &Vertices[Segs[seg].v2]; + v3 = &Vertices[Segs[set].v2]; + } + else if (Vertices[Segs[set].v1] == Vertices[Segs[seg].v2]) + { + v1 = &Vertices[Segs[seg].v1]; + v2 = &Vertices[Segs[set].v2]; + v3 = &Vertices[Segs[seg].v2]; + } + else + { + v1 = v2 = v3 = NULL; + } + if (v1 != NULL) + { + node.x = v3->x; + node.y = v3->y; + node.dx = v1->x + (v2->x-v1->x)/2 - node.x; + node.dy = v1->y + (v2->y-v1->y)/2 - node.y; + return Heuristic (node, set, false) != 0; + } + } + + bool nosplit = true; + int firsthit = seg; + + do + { + seg = firsthit; + do + { + if (Segs[seg].frontsector != sec) + { + node.x = Vertices[Segs[set].v1].x; + node.y = Vertices[Segs[set].v1].y; + node.dx = Vertices[Segs[seg].v2].x - node.x; + node.dy = Vertices[Segs[seg].v2].y - node.y; + + if (Heuristic (node, set, nosplit) != 0) + { + return true; + } + + node.dx = Vertices[Segs[seg].v1].x - node.x; + node.dy = Vertices[Segs[seg].v1].y - node.y; + + if (Heuristic (node, set, nosplit) != 0) + { + return true; + } + } + + seg = Segs[seg].next; + } while (seg != NO_INDEX); + } while ((nosplit ^= 1) == 0); + + // Give up. + return false; +} + +// Splitters are chosen to coincide with segs in the given set. To reduce the +// number of segs that need to be considered as splitters, segs are grouped into +// according to the planes that they lie on. Because one seg on the plane is just +// as good as any other seg on the plane at defining a split, only one seg from +// each unique plane needs to be considered as a splitter. A result of 0 means +// this set is a convex region. A result of -1 means that there were possible +// splitters, but they all split segs we want to keep intact. +int FNodeBuilder::SelectSplitter (WORD set, node_t &node, int step, bool nosplit) +{ + int stepleft; + int bestvalue; + WORD bestseg; + WORD seg; + bool nosplitters = false; + + bestvalue = 0; + bestseg = NO_INDEX; + + seg = set; + stepleft = 0; + + memset (&PlaneChecked[0], 0, PlaneChecked.Size()); + + while (seg != NO_INDEX) + { + FPrivSeg *pseg = &Segs[seg]; + + if (--stepleft <= 0) + { + int l = pseg->planenum >> 3; + int r = 1 << (pseg->planenum & 7); + + if ((PlaneChecked[l] & r) == 0) + { + PlaneChecked[l] |= r; + + stepleft = step; + node.x = Vertices[pseg->v1].x; + node.y = Vertices[pseg->v1].y; + node.dx = Vertices[pseg->v2].x - node.x; + node.dy = Vertices[pseg->v2].y - node.y; + + int value = Heuristic (node, set, nosplit); + + D(Printf ("Seg %d (%4d,%4d)-(%4d,%4d) scores %d\n", seg, node.x>>16, node.y>>16, + (node.x+node.dx)>>16, (node.y+node.dy)>>16, value)); + + if (value > bestvalue) + { + bestvalue = value; + bestseg = seg; + } + else if (value < 0) + { + nosplitters = true; + } + } + else + { + pseg = pseg; + } + } + + seg = pseg->next; + } + + if (bestseg == NO_INDEX) + { // No lines split any others into two sets, so this is a convex region. + D(Printf ("set %d, step %d, nosplit %d has no good splitter (%d)\n", set, step, nosplit, nosplitters)); + return nosplitters ? -1 : 0; + } + + D(Printf ("split seg %d in set %d, score %d, step %d, nosplit %d\n", bestseg, set, bestvalue, step, nosplit)); + + node.x = Vertices[Segs[bestseg].v1].x; + node.y = Vertices[Segs[bestseg].v1].y; + node.dx = Vertices[Segs[bestseg].v2].x - node.x; + node.dy = Vertices[Segs[bestseg].v2].y - node.y; + return 1; +} + +// Given a splitter (node), returns a score based on how "good" the resulting +// split in a set of segs is. Higher scores are better. -1 means this splitter +// splits something it shouldn't and will only be returned if honorNoSplit is +// true. A score of 0 means that the splitter does not split any of the segs +// in the set. + +int FNodeBuilder::Heuristic (node_t &node, WORD set, bool honorNoSplit) +{ + int score = 0; + int segsInSet = 0; + int counts[2] = { 0, 0 }; + WORD i = set; + int sidev1, sidev2; + int side; + bool splitter = false; + size_t max, m2, p, q; + + Touched.Clear (); + Colinear.Clear (); + + while (i != NO_INDEX) + { + const FPrivSeg *test = &Segs[i]; + + switch ((side = ClassifyLine (node, test, sidev1, sidev2))) + { + case 0: // Seg is on only one side of the partition + case 1: + // If we don't split this line, but it abuts the splitter, also reject it. + // The "right" thing to do in this case is to only reject it if there is + // another nosplit seg from the same sector at this vertex. Note that a line + // that lies exactly on top of the splitter is okay. + if (test->loopnum && honorNoSplit && (sidev1 == 0 || sidev2 == 0)) + { + if ((sidev1 | sidev2) != 0) + { + max = Touched.Size(); + for (p = 0; p < max; ++p) + { + if (Touched[p] == test->loopnum) + { + break; + } + } + if (p == max) + { + Touched.Push (test->loopnum); + } + } + else + { + max = Colinear.Size(); + for (p = 0; p < max; ++p) + { + if (Colinear[p] == test->loopnum) + { + break; + } + } + if (p == max) + { + Colinear.Push (test->loopnum); + } + } + } + + counts[side]++; + score += SplitCost; // Add some weight to the score for unsplit lines + break; + + default: // Seg is cut by the partition + // If we are not allowed to split this seg, reject this splitter + if (test->loopnum) + { + if (honorNoSplit) + { + D(Printf ("Splits seg %d\n", i)); + return -1; + } + else + { + splitter = true; + } + } + + counts[0]++; + counts[1]++; + break; + } + + segsInSet++; + i = test->next; + } + + // If this line is outside all the others, return a special score + if (counts[0] == 0 || counts[1] == 0) + { + return 0; + } + + // If this splitter intersects any vertices of segs that should not be split, + // check if it is also colinear with another seg from the same sector. If it + // is, the splitter is okay. If not, it should be rejected. Why? Assuming that + // polyobject containers are convex (which they should be), a splitter that + // is colinear with one of the sector's segs and crosses the vertex of another + // seg of that sector must be crossing the container's corner and does not + // actually split the container. + + max = Touched.Size (); + m2 = Colinear.Size (); + + // If honorNoSplit is false, then both these lists will be empty. + + // If the splitter touches some vertices without being colinear to any, we + // can skip further checks and reject this right away. + if (m2 == 0 && max > 0) + { + return -1; + } + + for (p = 0; p < max; ++p) + { + int look = Touched[p]; + for (q = 0; q < m2; ++q) + { + if (look == Colinear[q]) + { + break; + } + } + if (q == m2) + { // Not a good one + return -1; + } + } + + // Doom maps are primarily axis-aligned lines, so it's usually a good + // idea to prefer axis-aligned splitters over diagonal ones. Doom originally + // had special-casing for orthogonal lines, so they performed better. ZDoom + // does not care about the line's direction, so this is merely a choice to + // try and improve the final tree. + + if ((node.dx == 0) || (node.dy == 0)) + { + // If we have to split a seg we would prefer to keep unsplit, give + // extra precedence to orthogonal lines so that the polyobjects + // outside the entrance to MAP06 in Hexen MAP02 display properly. + if (splitter) + { + score += segsInSet*8; + } + else + { + score += segsInSet/AAPreference; + } + } + + score += (counts[0] + counts[1]) - abs(counts[0] - counts[1]); + + return score; +} + +int FNodeBuilder::ClassifyLine (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2) +{ + const FPrivVert *v1 = &Vertices[seg->v1]; + const FPrivVert *v2 = &Vertices[seg->v2]; + sidev1 = PointOnSide (v1->x, v1->y, node.x, node.y, node.dx, node.dy); + sidev2 = PointOnSide (v2->x, v2->y, node.x, node.y, node.dx, node.dy); + + if ((sidev1 | sidev2) == 0) + { // seg is coplanar with the splitter, so use its orientation to determine + // which child it ends up in. If it faces the same direction as the splitter, + // it goes in front. Otherwise, it goes in back. + + if (node.dx != 0) + { + if ((node.dx > 0 && v2->x > v1->x) || (node.dx < 0 && v2->x < v1->x)) + { + return 0; + } + else + { + return 1; + } + } + else + { + if ((node.dy > 0 && v2->y > v1->y) || (node.dy < 0 && v2->y < v1->y)) + { + return 0; + } + else + { + return 1; + } + } + } + else if (sidev1 <= 0 && sidev2 <= 0) + { + return 0; + } + else if (sidev1 >= 0 && sidev2 >= 0) + { + return 1; + } + return -1; +} + +void FNodeBuilder::SplitSegs (WORD set, node_t &node, WORD &outset0, WORD &outset1) +{ + fixed_t dx, dy; + + outset0 = NO_INDEX; + outset1 = NO_INDEX; + + while (set != NO_INDEX) + { + FPrivSeg *seg = &Segs[set]; + int next = seg->next; + + int sidev1, sidev2, side; + + side = ClassifyLine (node, seg, sidev1, sidev2); + + switch (side) + { + case 0: + seg->next = outset0; + outset0 = set; + break; + + case 1: + seg->next = outset1; + outset1 = set; + break; + + default: + fixed_t frac; + FPrivVert newvert; + FPrivSeg newseg; + size_t vertnum; + int seg2; + + if (seg->loopnum) + { + Printf (" Split seg %d (%d,%d)-(%d,%d) of sector %d in loop %d\n", + set, + Vertices[seg->v1].x>>16, Vertices[seg->v1].y>>16, + Vertices[seg->v2].x>>16, Vertices[seg->v2].y>>16, + seg->frontsector, seg->loopnum); + } + + frac = InterceptVector (node, *seg); + newvert.x = Vertices[seg->v1].x; + newvert.y = Vertices[seg->v1].y; + newvert.x = newvert.x + MulScale30 (frac, dx = Vertices[seg->v2].x - newvert.x); + newvert.y = newvert.y + MulScale30 (frac, dy = Vertices[seg->v2].y - newvert.y); + vertnum = Vertices.Push (newvert); + + newseg = *seg; + newseg.offset += fixed_t (sqrt (double(dx)*double(dx)+double(dy)*double(dy))); + if (sidev1 > 0) + { + newseg.v1 = vertnum; + seg->v2 = vertnum; + } + else + { + seg->v1 = vertnum; + newseg.v2 = vertnum; + } + + seg2 = (int)Segs.Push (newseg); + + Segs[seg2].next = outset0; + outset0 = seg2; + Segs[set].next = outset1; + outset1 = set; + break; + } + + set = next; + } +} + +fixed_t FNodeBuilder::InterceptVector (const node_t &splitter, const FPrivSeg &seg) +{ + double v2x = (double)Vertices[seg.v1].x; + double v2y = (double)Vertices[seg.v1].y; + double v2dx = (double)Vertices[seg.v2].x - v2x; + double v2dy = (double)Vertices[seg.v2].y - v2y; + double v1dx = (double)splitter.dx; + double v1dy = (double)splitter.dy; + + double den = v1dy*v2dx - v1dx*v2dy; + + if (den == 0.0) + return 0; // parallel + + double v1x = (double)splitter.x; + double v1y = (double)splitter.y; + + double num = (v1x - v2x)*v1dy + (v2y - v1y)*v1dx; + double frac = num / den; + + return (fixed_t)(1073741824.0*frac); +} + +inline int FNodeBuilder::PointOnSide (int x, int y, int x1, int y1, int dx, int dy) +{ + int foo = DMulScale32 (y-y1, dx, x1-x, dy); + return abs(foo) < 4 ? 0 : foo; +} + +void FNodeBuilder::PrintSet (int l, WORD set) +{ + Printf ("set %d: ", l); + for (; set != NO_INDEX; set = Segs[set].next) + { + Printf ("%d(%d:%ld,%ld-%ld,%ld) ", set, Segs[set].frontsector, + Vertices[Segs[set].v1].x>>16, Vertices[Segs[set].v1].y>>16, + Vertices[Segs[set].v2].x>>16, Vertices[Segs[set].v2].y>>16); + } + Printf ("*\n"); +} + +void FNodeBuilder::GetVertices (WideVertex *&verts, int &count) +{ + count = Vertices.Size (); + verts = new WideVertex[count]; + + for (int i = 0; i < count; ++i) + { + verts[i].x = Vertices[i].x; + verts[i].y = Vertices[i].y; + } +} + +void FNodeBuilder::GetSegs (MapSeg *&segs, int &count) +{ + count = SegList.Size (); + segs = new MapSeg[count]; + + for (int i = 0; i < count; ++i) + { + const FPrivSeg *org = &Segs[SegList[i].SegNum]; + + segs[i].v1 = org->v1; + segs[i].v2 = org->v2; + segs[i].angle = short(org->angle >> 16); + segs[i].linedef = org->linedef; + segs[i].side = Level.Lines[org->linedef].sidenum[1] == org->sidedef ? 1 : 0; + segs[i].offset = org->offset >> 16; + } +} + +void FNodeBuilder::GetSubsectors (MapSubsector *&ssecs, int &count) +{ + count = Subsectors.Size (); + ssecs = new MapSubsector[count]; + + for (int i = 0; i < count; ++i) + { + ssecs[i].numlines = Subsectors[i].numlines; + ssecs[i].firstline = Subsectors[i].firstline; + } +} + +void FNodeBuilder::GetNodes (MapNode *&nodes, int &count) +{ + count = Nodes.Size (); + nodes = new MapNode[count]; + + for (int i = 0; i < count; ++i) + { + nodes[i].x = Nodes[i].x >> 16; + nodes[i].y = Nodes[i].y >> 16; + nodes[i].dx = Nodes[i].dx >> 16; + nodes[i].dy = Nodes[i].dy >> 16; + for (int j = 0; j < 2; ++j) + { + for (int k = 0; k < 4; ++k) + { + nodes[i].bbox[j][k] = Nodes[i].bbox[j][k] >> 16; + } + nodes[i].children[j] = Nodes[i].children[j]; + } + } +} + +/* +#include "z_zone.h" + +void FNodeBuilder::Create (node_t *&nodes, int &numnodes, + seg_t *&segs, int &numsegs, + subsector_t *&subsectors, int &numsubsectors, + vertex_t *&vertices, int &numvertices) +{ + int i; + + numnodes = (int)Nodes.Size(); + nodes = (node_t *)Z_Malloc (numnodes*sizeof(node_t), PU_LEVEL, 0); + numsegs = (int)SegList.Size(); + segs = (seg_t *)Z_Malloc (numsegs*sizeof(seg_t), PU_LEVEL, 0); + numsubsectors = (int)Subsectors.Size(); + subsectors = (subsector_t *)Z_Malloc (numsubsectors*sizeof(subsector_t), PU_LEVEL, 0); + numvertices = (int)Vertices.Size(); + vertices = (vertex_t *)Z_Malloc (numvertices*sizeof(vertex_t), PU_LEVEL, 0); + + memcpy (nodes, &Nodes[0], numnodes*sizeof(node_t)); + memcpy (subsectors, &Subsectors[0], numsubsectors*sizeof(subsector_t)); + + for (i = 0; i < numvertices; ++i) + { + vertices[i].x = Vertices[i].x; + vertices[i].y = Vertices[i].y; + } + + for (i = 0; i < numsegs; ++i) + { + FPrivSeg *org = &Segs[SegList[i].SegNum]; + seg_t *out = &segs[i]; + + out->backsector = org->backsector; + out->frontsector = org->frontsector; + out->linedef = org->linedef; + out->sidedef = org->sidedef; + out->v1 = vertices + org->v1; + out->v2 = vertices + org->v2; + } + + for (i = 0; i < Level.NumLines; ++i) + { + Level.Lines[i].v1 = vertices + (size_t)Level.Lines[i].v1; + Level.Lines[i].v2 = vertices + (size_t)Level.Lines[i].v2; + } +} +*/ \ No newline at end of file diff --git a/Unused/nodebuild.h-nogl b/Unused/nodebuild.h-nogl new file mode 100644 index 0000000..4e89ce6 --- /dev/null +++ b/Unused/nodebuild.h-nogl @@ -0,0 +1,98 @@ +#include "doomdata.h" +#include "workdata.h" +#include "tarray.h" + +class FNodeBuilder +{ + struct FPrivSeg + { + int v1, v2; + int sidedef; + int linedef; + int frontsector; + int backsector; + int next; + int nextforvert; + int loopnum; // loop number for split avoidance (0 means splitting is okay) + angle_t angle; + fixed_t offset; + + int planenum; + FPrivSeg *hashnext; + }; + struct FPrivVert + { + fixed_t x, y; + WORD segs; // segs that use this vertex as v1 + + bool operator== (const FPrivVert &other) + { + return x == other.x && y == other.y; + } + }; + union USegPtr + { + int SegNum; + FPrivSeg *SegPtr; + }; +public: + struct FPolyStart + { + int polynum; + fixed_t x, y; + }; + + FNodeBuilder (FLevel &level, + TArray &polyspots, TArray &anchors, + const char *name); + + void GetVertices (WideVertex *&verts, int &count); + void GetSegs (MapSeg *&segs, int &count); + void GetNodes (MapNode *&nodes, int &count); + void GetSubsectors (MapSubsector *&ssecs, int &count); + +private: + TArray Nodes; + TArray Subsectors; + TArray Segs; + TArray Vertices; + TArray SegList; + TArray PlaneChecked; + + TArray Touched; // Loops a splitter touches on a vertex + TArray Colinear; // Loops with edges colinear to a splitter + + FLevel &Level; + + // Progress meter stuff + int SegsStuffed; + const char *MapName; + + void FindUsedVertices (WideVertex *vertices, int max); + void BuildTree (); + void MakeSegsFromSides (); + void GroupSegPlanes (); + void FindPolyContainers (TArray &spots, TArray &anchors); + bool GetPolyExtents (int polynum, fixed_t bbox[4]); + int MarkLoop (int firstseg, int loopnum); + void AddSegToBBox (fixed_t bbox[4], const FPrivSeg *seg); + int CreateNode (WORD set, fixed_t bbox[4]); + int CreateSubsector (WORD set, fixed_t bbox[4]); + bool CheckSubsector (WORD set, node_t &node, int setsize); + int SelectSplitter (WORD set, node_t &node, int step, bool nosplit); + void SplitSegs (WORD set, node_t &node, WORD &outset0, WORD &outset1); + int Heuristic (node_t &node, WORD set, bool honorNoSplit); + int ClassifyLine (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2); + int CountSegs (WORD set) const; + + static int SortSegs (const void *a, const void *b); + + // < 0 : in front of line + // == 0 : on line + // > 0 : behind line + + inline int PointOnSide (int x, int y, int x1, int y1, int dx, int dy); + fixed_t InterceptVector (const node_t &splitter, const FPrivSeg &seg); + + void PrintSet (int l, WORD set); +}; diff --git a/Unused/rejectbuilder.cpp b/Unused/rejectbuilder.cpp new file mode 100644 index 0000000..6ac1476 --- /dev/null +++ b/Unused/rejectbuilder.cpp @@ -0,0 +1,215 @@ +// The old code used the same algorithm as DoomBSP: +// +// Represent each sector by its bounding box. Then for each pair of +// sectors, see if any chains of one-sided lines can walk from one +// side of the convex hull for that pair to the other side. +// +// It works, but it's far from being perfect. It's quite easy for +// this algorithm to consider two sectors as being visible from +// each other when they are really not. But it won't erroneously +// flag two sectors as obstructed when they're really not, and that's +// the only thing that really matters when building a REJECT lump. +// +// Because that was next to useless, I scrapped that code and adapted +// Quake's vis utility to work in a 2D world. Since this is basically vis, +// it depends on GL nodes being present to function. As the usefulness +// of a REJECT lump is debatable, I have chosen to not compile this module +// in with ZDBSP. Save yourself some space and run ZDBSP with the -r option. + +#include +#include + +#include "zdbsp.h" +#include "nodebuild.h" +#include "rejectbuilder.h" +#include "templates.h" + +bool MergeVis=1; + +FRejectBuilder::FRejectBuilder (FLevel &level) + : Level (level), testlevel (2), totalvis (0) +{ + LoadPortals (); + + if (MergeVis) + { + MergeLeaves (); + MergeLeafPortals (); + } + + CountActivePortals (); + CalcVis (); + + BuildReject (); +} + +FRejectBuilder::~FRejectBuilder () +{ +} + +BYTE *FRejectBuilder::GetReject () +{ + WORD *sectormap; + int i, j; + + int rejectSize = (Level.NumSectors*Level.NumSectors + 7) / 8; + BYTE *reject = new BYTE[rejectSize]; + memset (reject, 0xff, rejectSize); + + int pvs_size = (Level.NumGLSubsectors * Level.NumGLSubsectors) + 7 / 8; + Level.GLPVS = new BYTE[pvs_size]; + Level.GLPVSSize = pvs_size; + memset (Level.GLPVS, 0, pvs_size); + + sectormap = new WORD[Level.NumGLSubsectors]; + for (i = 0; i < Level.NumGLSubsectors; ++i) + { + const MapSegGLEx *seg = &Level.GLSegs[Level.GLSubsectors[i].firstline]; + sectormap[i] = Level.Sides[Level.Lines[seg->linedef].sidenum[seg->side]].sector; + } + + for (i = 0; i < Level.NumGLSubsectors; ++i) + { + int rowpvs = i*Level.NumGLSubsectors; + int rowrej = sectormap[i]*Level.NumSectors; + BYTE *bytes = visBytes + i*leafbytes; + for (j = 0; j < Level.NumGLSubsectors; ++j) + { + if (bytes[j>>3] & (1<<(j&7))) + { + int mark = rowpvs + j; + Level.GLPVS[mark>>3] |= 1<<(mark&7); + + mark = rowrej + sectormap[j]; + reject[mark>>3] &= ~(1<<(mark&7)); + } + } + } + + return reject; +} + +void FRejectBuilder::BuildReject () +{ +} + +inline const WideVertex *FRejectBuilder::GetVertex (WORD vertnum) +{ + return &Level.Vertices[vertnum]; +} + +FRejectBuilder::FLeaf::FLeaf () + : numportals (0), merged (-1), portals (NULL) +{ +} + +FRejectBuilder::FLeaf::~FLeaf () +{ + if (portals != NULL) + { + delete[] portals; + } +} + +int FRejectBuilder::PointOnSide (const FPoint &point, const FLine &line) +{ + return FNodeBuilder::PointOnSide (point.x, point.y, line.x, line.y, line.dx, line.dy); +} + +void FRejectBuilder::LoadPortals () +{ + WORD *segleaf; + int i, j, k, max; + VPortal *p; + FLeaf *l; + FWinding *w; + + portalclusters = Level.NumGLSubsectors; + + for (numportals = 0, i = 0; i < Level.NumGLSegs; ++i) + { + if (Level.GLSegs[i].partner != DWORD_MAX) + { + ++numportals; + } + } + + // these counts should take advantage of 64 bit systems automatically + leafbytes = ((portalclusters+63)&~63)>>3; + leaflongs = leafbytes/sizeof(long); + + portalbytes = ((numportals+63)&~63)>>3; + portallongs = portalbytes/sizeof(long); + + portals = new VPortal[numportals]; + memset (portals, 0, numportals*sizeof(VPortal)); + + leafs = new FLeaf[portalclusters]; + + numVisBytes = portalclusters*leafbytes; + visBytes = new BYTE[numVisBytes]; + + segleaf = new WORD[Level.NumGLSegs]; + for (i = 0; i < Level.NumGLSubsectors; ++i) + { + j = Level.GLSubsectors[i].firstline; + max = j + Level.GLSubsectors[i].numlines; + + for (; j < max; ++j) + { + segleaf[j] = i; + } + } + + p = portals; + l = leafs; + for (i = 0; i < Level.NumGLSubsectors; ++i, ++l) + { + j = Level.GLSubsectors[i].firstline; + max = j + Level.GLSubsectors[i].numlines; + + // Count portals in this leaf + for (; j < max; ++j) + { + if (Level.GLSegs[j].partner != DWORD_MAX) + { + ++l->numportals; + } + } + + if (l->numportals == 0) + { + continue; + } + + l->portals = new VPortal *[l->numportals]; + + for (k = 0, j = Level.GLSubsectors[i].firstline; j < max; ++j) + { + const MapSegGLEx *seg = &Level.GLSegs[j]; + + if (seg->partner == DWORD_MAX) + { + continue; + } + + // create portal from seg + l->portals[k++] = p; + + w = &p->winding; + w->points[0] = GetVertex (seg->v1); + w->points[1] = GetVertex (seg->v2); + + p->hint = seg->linedef != NO_INDEX; + p->line.x = w->points[1].x; + p->line.y = w->points[1].y; + p->line.dx = w->points[0].x - p->line.x; + p->line.dy = w->points[0].y - p->line.y; + p->leaf = segleaf[seg->partner]; + + p++; + } + } + + delete[] segleaf; +} diff --git a/Unused/rejectbuilder.cpp-nogl b/Unused/rejectbuilder.cpp-nogl new file mode 100644 index 0000000..9cb6e4b --- /dev/null +++ b/Unused/rejectbuilder.cpp-nogl @@ -0,0 +1,330 @@ +// This is the same algorithm used by DoomBSP: +// +// Represent each sector by its bounding box. Then for each pair of +// sectors, see if any chains of one-sided lines can walk from one +// side of the convex hull for that pair to the other side. +// +// It works, but it's far from being perfect. It's quite easy for +// this algorithm to consider two sectors as being visible from +// each other when they are really not. But it won't erroneously +// flag two sectors as obstructed when they're really not, and that's +// the only thing that really matters when building a REJECT lump. + +#include +#include + +#include "rejectbuilder.h" +#include "templates.h" + +FRejectBuilder::FRejectBuilder (FLevel &level) + : Level (level), BlockChains (NULL) +{ + RejectSize = (Level.NumSectors*Level.NumSectors + 7) / 8; + Reject = new BYTE[RejectSize]; + memset (Reject, 0, RejectSize); + + FindSectorBounds (); + FindBlockChains (); + BuildReject (); +} + +FRejectBuilder::~FRejectBuilder () +{ + FBlockChain *chain, *next; + + chain = BlockChains; + while (chain != NULL) + { + next = chain->Next; + delete chain; + chain = next; + } +} + +BYTE *FRejectBuilder::GetReject () +{ + return Reject; +} + +void FRejectBuilder::FindSectorBounds () +{ + int i; + + SectorBounds = new BBox[Level.NumSectors]; + + for (i = 0; i < Level.NumSectors; ++i) + { + SectorBounds[i].Bounds[LEFT] = SectorBounds[i].Bounds[BOTTOM] = INT_MAX; + SectorBounds[i].Bounds[RIGHT] = SectorBounds[i].Bounds[TOP] = INT_MIN; + } + + for (i = 0; i < Level.NumLines; ++i) + { + if (Level.Lines[i].sidenum[0] != NO_INDEX) + { + int secnum = Level.Sides[Level.Lines[i].sidenum[0]].sector; + SectorBounds[secnum].AddPt (Level.Vertices[Level.Lines[i].v1]); + SectorBounds[secnum].AddPt (Level.Vertices[Level.Lines[i].v2]); + } + if (Level.Lines[i].sidenum[1] != NO_INDEX) + { + int secnum = Level.Sides[Level.Lines[i].sidenum[1]].sector; + SectorBounds[secnum].AddPt (Level.Vertices[Level.Lines[i].v1]); + SectorBounds[secnum].AddPt (Level.Vertices[Level.Lines[i].v2]); + } + } +} + +void FRejectBuilder::FindBlockChains () +{ + bool *marked = new bool[Level.NumLines]; + WORD *nextForVert = new WORD[Level.NumLines]; + WORD *firstLine = new WORD[Level.NumVertices]; + int i, j, k; + FBlockChain *chain; + FPoint pt; + TArray pts; + + memset (nextForVert, 0xff, Level.NumLines*sizeof(*nextForVert)); + memset (firstLine, 0xff, Level.NumVertices*sizeof(*firstLine)); + memset (marked, 0, Level.NumLines*sizeof(*marked)); + + for (i = 0; i < Level.NumLines; ++i) + { + if (Level.Lines[i].sidenum[0] == NO_INDEX || Level.Lines[i].sidenum[1] != NO_INDEX) + { + marked[i] = true; + } + else + { + nextForVert[Level.Lines[i].v1] = firstLine[Level.Lines[i].v1]; + firstLine[Level.Lines[i].v1] = i; + } + } + + for (i = 0; i < Level.NumLines; ++i) + { + if (marked[i]) + { + continue; + } + + pt.x = Level.Vertices[Level.Lines[i].v1].x >> FRACBITS; + pt.y = Level.Vertices[Level.Lines[i].v1].y >> FRACBITS; + pts.Clear (); + pts.Push (pt); + chain = new FBlockChain; + chain->Bounds(LEFT) = chain->Bounds(RIGHT) = pt.x; + chain->Bounds(TOP) = chain->Bounds(BOTTOM) = pt.y; + + for (j = i; j != NO_INDEX; ) + { + marked[j] = true; + pt.x = Level.Vertices[Level.Lines[j].v2].x >> FRACBITS; + pt.y = Level.Vertices[Level.Lines[j].v2].y >> FRACBITS; + pts.Push (pt); + chain->Bounds.AddPt (pt); + + k = firstLine[Level.Lines[j].v2]; + if (k == NO_INDEX) + { + break; + } + if (nextForVert[k] == NO_INDEX) + { + j = marked[k] ? NO_INDEX : k; + } + else + { + int best = NO_INDEX; + angle_t bestang = ANGLE_MAX; + angle_t ang1 = PointToAngle (Level.Vertices[Level.Lines[j].v2].x - Level.Vertices[Level.Lines[j].v1].x, + Level.Vertices[Level.Lines[j].v2].y - Level.Vertices[Level.Lines[j].v1].y) + (1 << 31); + + while (k != NO_INDEX) + { + if (!marked[k]) + { + angle_t ang2 = PointToAngle (Level.Vertices[Level.Lines[k].v2].x - Level.Vertices[Level.Lines[k].v1].x, + Level.Vertices[Level.Lines[k].v2].y - Level.Vertices[Level.Lines[k].v1].y) + (1 << 31); + angle_t angdiff = ang2 - ang1; + + if (angdiff < bestang && angdiff > 0) + { + bestang = angdiff; + best = k; + } + } + k = nextForVert[k]; + } + + j = best; + } + } + + chain->NumPoints = pts.Size(); + chain->Points = new FPoint[chain->NumPoints]; + memcpy (chain->Points, &pts[0], chain->NumPoints*sizeof(*chain->Points)); + chain->Next = BlockChains; + BlockChains = chain; + } +} + +void FRejectBuilder::HullSides (const BBox &box1, const BBox &box2, FPoint sides[4]) +{ + static const int vertSides[4][2] = { + { LEFT, BOTTOM }, + { LEFT, TOP }, + { RIGHT, TOP }, + { RIGHT, BOTTOM } + }; + static const int stuffSpots[4] = { 0, 3, 2, 1 }; + + const int *boxp1, *boxp2; + + boxp1 = box2.Bounds; + boxp2 = box1.Bounds; + + for (int mainBox = 2; mainBox != 0; ) + { + const int *stuffs = stuffSpots + (--mainBox)*2; + int outerEdges[4]; + + outerEdges[LEFT] = boxp1[LEFT] <= boxp2[LEFT]; + outerEdges[TOP] = boxp1[TOP] >= boxp2[TOP]; + outerEdges[RIGHT] = boxp1[RIGHT] >= boxp2[RIGHT]; + outerEdges[BOTTOM] = boxp1[BOTTOM] <= boxp2[BOTTOM]; + + for (int vertex = 0; vertex < 4; ++vertex) + { + if (outerEdges[(vertex-1)&3] != outerEdges[vertex]) + { + FPoint *pt = &sides[stuffs[outerEdges[vertex]]]; + pt->x = boxp1[vertSides[vertex][0]]; + pt->y = boxp1[vertSides[vertex][1]]; + } + } + + boxp1 = box1.Bounds; + boxp2 = box2.Bounds; + } +} + +int FRejectBuilder::PointOnSide (const FPoint *pt, const FPoint &lpt1, const FPoint &lpt2) +{ + return (pt->y - lpt1.y) * (lpt2.x - lpt1.x) >= (pt->x - lpt1.x) * (lpt2.y - lpt1.y); +} + +bool FRejectBuilder::ChainBlocks (const FBlockChain *chain, + const BBox *hullBounds, const FPoint *hullPts) +{ + int startSide, side, i; + + if (chain->Bounds[LEFT] > hullBounds->Bounds[RIGHT] || + chain->Bounds[RIGHT] < hullBounds->Bounds[LEFT] || + chain->Bounds[TOP] < hullBounds->Bounds[BOTTOM] || + chain->Bounds[BOTTOM] > hullBounds->Bounds[TOP]) + { + return false; + } + + startSide = -1; + + for (i = 0; i < chain->NumPoints; ++i) + { + const FPoint *pt = &chain->Points[i]; + + if (PointOnSide (pt, hullPts[1], hullPts[2])) + { + startSide = -1; + continue; + } + if (PointOnSide (pt, hullPts[3], hullPts[0])) + { + startSide = -1; + continue; + } + if (PointOnSide (pt, hullPts[0], hullPts[1])) + { + side = 0; + } + else if (PointOnSide (pt, hullPts[2], hullPts[3])) + { + side = 1; + } + else + { + continue; + } + if (startSide == -1 || startSide == side) + { + startSide = side; + } + else + { + return true; + } + } + + return false; +} + +void FRejectBuilder::BuildReject () +{ + int s1, s2; + + for (s1 = 0; s1 < Level.NumSectors-1; ++s1) + { + printf (" Reject: %3d%%\r", s1*100/Level.NumSectors); + for (s2 = s1 + 1; s2 < Level.NumSectors; ++s2) + { + BBox HullBounds; + FPoint HullPts[4]; + const BBox *sb1, *sb2; + + sb1 = &SectorBounds[s1]; + sb2 = &SectorBounds[s2]; + + int pos = s1*Level.NumSectors + s2; + if (Reject[pos>>3] & (1<<(pos&7))) + { + continue; + } + + // Overlapping and touching sectors are considered to always + // see each other. + if (sb1->Bounds[LEFT] <= sb2->Bounds[RIGHT] && + sb1->Bounds[RIGHT] >= sb2->Bounds[LEFT] && + sb1->Bounds[TOP] >= sb2->Bounds[BOTTOM] && + sb1->Bounds[BOTTOM] <= sb2->Bounds[TOP]) + { + continue; + } + + HullBounds(LEFT) = MIN (sb1->Bounds[LEFT], sb2->Bounds[LEFT]); + HullBounds(RIGHT) = MAX (sb1->Bounds[RIGHT], sb2->Bounds[RIGHT]); + HullBounds(BOTTOM) = MIN (sb1->Bounds[BOTTOM], sb2->Bounds[BOTTOM]); + HullBounds(TOP) = MAX (sb1->Bounds[TOP], sb2->Bounds[TOP]); + + HullSides (*sb1, *sb2, HullPts); + + for (FBlockChain *chain = BlockChains; chain != NULL; chain = chain->Next) + { + if (ChainBlocks (chain, &HullBounds, HullPts)) + { + break; + } + } + + if (chain == NULL) + { + continue; + } + + Reject[pos>>3] |= 1 << (pos & 7); + pos = s2*Level.NumSectors + s1; + Reject[pos>>3] |= 1 << (pos & 7); + } + } + printf (" Reject: 100%%\n"); +} diff --git a/Unused/rejectbuilder.cpp-slow b/Unused/rejectbuilder.cpp-slow new file mode 100644 index 0000000..069fca2 --- /dev/null +++ b/Unused/rejectbuilder.cpp-slow @@ -0,0 +1,278 @@ +// This is the same algorithm used by DoomBSP: +// +// Represent each sector by its bounding box. Then for each pair of +// sectors, see if any chains of one-sided lines can walk from one +// side of the convex hull for that pair to the other side. +// +// It works, but it's far from being perfect. It's quite easy for +// this algorithm to consider two sectors as being visible from +// each other when they are really not. But it won't erroneously +// flag two sectors as obstructed when they're really not, and that's +// the only thing that really matters when building a REJECT lump. + +#include +#include + +#include "zdbsp.h" +#include "nodebuild.h" +#include "rejectbuilder.h" +#include "templates.h" + +FRejectBuilder::FRejectBuilder (FLevel &level) + : Level (level) +{ + int i; + + i = Level.NumGLSubsectors * Level.NumGLSubsectors; + SubSeeMatrix = new BYTE[i]; + memset (SubSeeMatrix, 0, i); + + SegSubsectors = new WORD[Level.NumGLSegs]; + for (i = 0; i < Level.NumGLSubsectors; ++i) + { + for (int j = 0; j < Level.GLSubsectors[i].numlines; ++j) + { + SegSubsectors[j + Level.GLSubsectors[i].firstline] = i; + } + } + + BuildReject (); +} + +FRejectBuilder::~FRejectBuilder () +{ + delete[] SubSeeMatrix; + delete[] SegSubsectors; +} + +BYTE *FRejectBuilder::GetReject () +{ + int i, j; + + int rejectSize = (Level.NumSectors*Level.NumSectors + 7) / 8; + BYTE *reject = new BYTE[rejectSize]; + memset (reject, 0xff, rejectSize); + + int pvs_size = (Level.NumGLSubsectors * Level.NumGLSubsectors) + 7 / 8; + Level.GLPVS = new BYTE[pvs_size]; + Level.GLPVSSize = pvs_size; + memset (Level.GLPVS, 0, pvs_size); + + for (i = 0; i < Level.NumGLSubsectors; ++i) + { + int row = i*Level.NumGLSubsectors; + int sector1 = + Level.Sides[ + Level.Lines[ + Level.GLSegs[ + Level.GLSubsectors[i].firstline].linedef] + .sidenum[ + Level.GLSegs[ + Level.GLSubsectors[i].firstline].side]] + .sector; + int srow = sector1*Level.NumSectors; + for (j = 0; j < Level.NumGLSubsectors; ++j) + { + if (SubSeeMatrix[row + j]) + { + int sector2 = + Level.Sides[ + Level.Lines[ + Level.GLSegs[ + Level.GLSubsectors[j].firstline].linedef] + .sidenum[ + Level.GLSegs[ + Level.GLSubsectors[j].firstline].side]] + .sector; + int l = (srow + sector2) >> 3; + int r = (srow + sector2) & 7; + reject[l] &= ~(1 << r); + + l = (row + j) >> 3; + r = (row + j) & 7; + Level.GLPVS[l] |= 1 << r; + } + } + } + + return reject; +} + +void FRejectBuilder::BuildReject () +{ + int s1, s2; + + for (s1 = 0; s1 < Level.NumGLSubsectors; ++s1) + { + //printf (" Reject: %3d%%\r",s1*100/Level.NumGLSubsectors); + printf ("%d/%d\r", s1, Level.NumGLSubsectors); + + // A subsector can always see itself + SourceRow = s1 * Level.NumGLSubsectors; + SubSeeMatrix[SourceRow + s1] = 1; + + WORD pusher = s1; + + for (s2 = 0; s2 < Level.GLSubsectors[s1].numlines; ++s2) + { + int segnum = s2 + Level.GLSubsectors[s1].firstline; + const MapSegGL *seg = &Level.GLSegs[segnum]; + if (seg->partner == NO_INDEX) + { + continue; + } + + SourceSeg = segnum; + SegRow = segnum * Level.NumGLSegs; + + TracePath (s1, seg); + } + } + printf (" Reject: 100%%\n"); +} + +inline const WideVertex *FRejectBuilder::GetVertex (WORD vertnum) +{ + if (vertnum & 0x8000) + { + return &Level.GLVertices[vertnum & 0x7fff]; + } + else + { + return &Level.Vertices[vertnum]; + } +} + +void FRejectBuilder::TracePath (int subsector, const MapSegGL *window) +{ + // Neighboring subsectors can always see each other + SubSeeMatrix[SourceRow + SegSubsectors[window->partner]] = 1; + const MapSubsector *backsub = &Level.GLSubsectors[SegSubsectors[window->partner]]; + + Portal source; + + source.Subsector = backsub; + source.Left = GetVertex (window->v1); + source.Right = GetVertex (window->v2); + + PortalStack.Clear (); + PortalStack.Push (source); + + fixed_t wdx = source.Right->x - source.Left->x; + fixed_t wdy = source.Right->y - source.Left->y; + +// printf ("start window %d\n", window - Level.GLSegs); + + for (int i = 0; i < backsub->numlines; ++i) + { + int segnum = backsub->firstline + i; + + if (segnum == window->partner) + { + continue; + } + + const MapSegGL *cseg = &Level.GLSegs[segnum]; + + if (cseg->partner == NO_INDEX) + { + continue; + } + + const WideVertex *cv1 = GetVertex (cseg->v1); + const WideVertex *cv2 = GetVertex (cseg->v2); + + if (FNodeBuilder::PointOnSide (cv1->x, cv1->y, source.Left->x, source.Left->y, wdx, wdy) == 0 && + FNodeBuilder::PointOnSide (cv2->x, cv2->y, source.Left->x, source.Left->y, wdx, wdy) == 0) + { + continue; + } + + TracePathDeep (cseg); + } +} + +void FRejectBuilder::TracePathDeep (const MapSegGL *window) +{ + SubSeeMatrix[SourceRow + SegSubsectors[window->partner]] = 1; + const MapSubsector *backsub = &Level.GLSubsectors[SegSubsectors[window->partner]]; + size_t j; + + for (j = PortalStack.Size(); j-- > 0; ) + { + if (PortalStack[j].Subsector == backsub) + { + return; + } + } + + Portal entrance; + + entrance.Subsector = backsub; + entrance.Left = GetVertex (window->v1); + entrance.Right = GetVertex (window->v2); + PortalStack.Push (entrance); + + fixed_t wdx = entrance.Right->x - entrance.Left->x; + fixed_t wdy = entrance.Right->y - entrance.Left->y; + + //printf ("deep through %d\n", window - Level.GLSegs); + + for (int i = 0; i < backsub->numlines; ++i) + { + int segnum = backsub->firstline + i; + + if (segnum == window->partner) + { + continue; + } + + const MapSegGL *cseg = &Level.GLSegs[segnum]; + + if (cseg->partner == NO_INDEX) + { + continue; + } + + const WideVertex *cv1 = GetVertex (cseg->v1); + const WideVertex *cv2 = GetVertex (cseg->v2); + + if (FNodeBuilder::PointOnSide (cv1->x, cv1->y, entrance.Left->x, entrance.Left->y, wdx, wdy) <= 0 && + FNodeBuilder::PointOnSide (cv2->x, cv2->y, entrance.Left->x, entrance.Left->y, wdx, wdy) <= 0) + { + continue; + } + + fixed_t leftx = PortalStack[0].Left->x; + fixed_t lefty = PortalStack[0].Left->y; + fixed_t rightx = PortalStack[0].Right->x; + fixed_t righty = PortalStack[0].Right->y; + + fixed_t leftdx = cv1->x - leftx; + fixed_t leftdy = cv1->y - lefty; + fixed_t rightdx = rightx - cv2->x; + fixed_t rightdy = righty - cv2->y; + + if (FNodeBuilder::PointOnSide (cv1->x, cv1->y, rightx, righty, rightdx, rightdy) >= 0 || + FNodeBuilder::PointOnSide (cv2->x, cv2->y, leftx, lefty, leftdx, leftdy) >= 0) + { + continue; + } + + for (j = PortalStack.Size(); j-- > 1; ) + { + if (FNodeBuilder::PointOnSide (PortalStack[j].Left->x, PortalStack[j].Left->y, + rightx, righty, rightdx, rightdy) >= 0 || + FNodeBuilder::PointOnSide (PortalStack[j].Right->x, PortalStack[j].Right->y, + leftx, lefty, leftdx, leftdy) >= 0) + { + break; + } + } + if (j == 0) + { + TracePathDeep (cseg); + } + } + PortalStack.Pop (entrance); +} diff --git a/Unused/rejectbuilder.h b/Unused/rejectbuilder.h new file mode 100644 index 0000000..bcec125 --- /dev/null +++ b/Unused/rejectbuilder.h @@ -0,0 +1,211 @@ +// A slight adaptation of Quake's vis utility. + +#include "zdbsp.h" +#include "tarray.h" +#include "doomdata.h" + +class FRejectBuilder +{ +public: + FRejectBuilder (FLevel &level); + ~FRejectBuilder (); + + BYTE *GetReject (); + +private: + void BuildReject (); + void LoadPortals (); + inline const WideVertex *GetVertex (WORD vertnum); + FLevel &Level; + +/* Looky! Stuff from vis.h: */ + +// seperator caching helps a bit +//#define SEPERATORCACHE + +// can't have more seperators than the max number of points on a winding + static const int MAX_SEPERATORS = 2; + static const int MAX_PORTALS = 65536*2/3; + static const int MAX_MAP_LEAFS = 32768; + static const int MAX_PORTALS_ON_LEAF = MAX_PORTALS; + + struct FPoint + { + fixed_t x, y; + + const FPoint &operator= (const WideVertex *other) + { + x = other->x; + y = other->y; + return *this; + } + }; + + struct FLine : public FPoint + { + fixed_t dx, dy; + + void Flip () + { + x += dx; + y += dy; + dx = -dx; + dy = -dy; + } + }; + + struct FWinding + { + FPoint points[2]; + }; + + struct FPassage + { + FPassage *next; + BYTE cansee[1]; //all portals that can be seen through this passage + }; + + enum VStatus + { + STAT_None, + STAT_Working, + STAT_Done + }; + + struct VPortal + { + bool removed; + bool hint; + FLine line; // neighbor is on front/right side + int leaf; // neighbor + + FWinding winding; + VStatus status; + BYTE *portalfront; // [portals], preliminary + BYTE *portalflood; // [portals], intermediate + BYTE *portalvis; // [portals], final + + int nummightsee; // bit count on portalflood for sort + FPassage *passages; // there are just as many passages as there + // are portals in the leaf this portal leads to + }; + + struct FLeaf + { + FLeaf (); + ~FLeaf (); + + int numportals; + int merged; + VPortal **portals; + }; + + struct PStack + { + BYTE mightsee[MAX_PORTALS/8]; // bit string + PStack *next; + FLeaf *leaf; + VPortal *portal; // portal exiting + FWinding *source; + FWinding *pass; + + FWinding windings[3]; // source, pass, temp in any order + bool freewindings[3]; + + FLine portalline; + int depth; + #ifdef SEPERATORCACHE + FLine seperators[2][MAX_SEPERATORS]; + int numseperators[2]; + #endif + }; + + struct FThreadData + { + VPortal *base; + int c_chains; + PStack pstack_head; + }; + + int numportals; + int portalclusters; + + VPortal *portals; + FLeaf *leafs; + + int c_portaltest, c_portalpass, c_portalcheck; + int c_portalskip, c_leafskip; + int c_vistest, c_mighttest; + int c_chains; + + int testlevel; + + BYTE *uncompressed; + + int leafbytes, leaflongs; + int portalbytes, portallongs; + + int numVisBytes; + BYTE *visBytes; + + int totalvis; + + void LeafFlow (int leafnum); + + void BasePortalVis (int portalnum); + void BetterPortalVis (int portalnum); + void PortalFlow (int portalnum); + void PassagePortalFlow (int portalnum); + void CreatePassages (int portalnum); + void PassageFlow (int portalnum); + + VPortal *sorted_portals[65536]; + + static int CountBits (BYTE *bits, int numbits); + + void SortPortals (); + static int PComp (const void *a, const void *b); + int LeafVectorFromPortalVector (BYTE *portalbits, BYTE *leafbits); + void ClusterMerge (int leafnum); + + void PassageMemory (); + void CalcFastVis (); + void CalcVis (); + void CalcPortalVis (); + void CalcPassagePortalVis (); + + int CountActivePortals (); + + bool pacifier; + int workcount; + int dispatch; + int oldf; + int oldcount; + + void RunThreadsOnIndividual (int workcnt, bool showpacifire, void (FRejectBuilder::*func)(int)); + int GetThreadWork (); + + void CheckStack (FLeaf *leaf, FThreadData *thread); + + FWinding *AllocStackWinding (PStack *stack) const; + void FreeStackWinding (FWinding *w, PStack *stack) const; + + FWinding *VisChopWinding (FWinding *in, PStack *stack, FLine *split); + FWinding *ClipToSeperators (FWinding *source, FWinding *pass, FWinding *target, bool flipclip, PStack *stack); + void RecursiveLeafFlow (int leafnum, FThreadData *thread, PStack *prevstack); + void RecursivePassageFlow (VPortal *portal, FThreadData *thread, PStack *prevstack); + void RecursivePassagePortalFlow (VPortal *portal, FThreadData *thread, PStack *prevstack); + FWinding *PassageChopWinding (FWinding *in, FWinding *out, FLine *split); + int AddSeperators (FWinding *source, FWinding *pass, bool flipclip, FLine *seperators, int maxseperators); + void SimpleFlood (VPortal *srcportal, int leafnum); + void RecursiveLeafBitFlow (int leafnum, BYTE *mightsee, BYTE *cansee); + + int PointOnSide (const FPoint &point, const FLine &line); + + bool TryMergeLeaves (int l1num, int l2num); + void UpdatePortals (); + void MergeLeaves (); + FWinding *TryMergeWinding (FWinding *f1, FWinding *f2, const FLine &line); + void MergeLeafPortals (); + bool Winding_PlanesConcave (const FWinding *w1, const FWinding *w2, const FLine &line1, const FLine &line2); +}; diff --git a/Unused/rejectbuilder.h-nogl b/Unused/rejectbuilder.h-nogl new file mode 100644 index 0000000..4f21d88 --- /dev/null +++ b/Unused/rejectbuilder.h-nogl @@ -0,0 +1,77 @@ +#include "zdbsp.h" +#include "tarray.h" +#include "doomdata.h" + +class FRejectBuilder +{ + struct FPoint + { + int x, y; + }; + + struct BBox + { + int Bounds[4]; + + const int &operator[] (int index) const + { + return Bounds[index]; + } + int &operator() (int index) + { + return Bounds[index]; + } + void AddPt (int x, int y) + { + if (x < Bounds[LEFT]) Bounds[LEFT] = x; + if (x > Bounds[RIGHT]) Bounds[RIGHT] = x; + if (y < Bounds[BOTTOM]) Bounds[BOTTOM] = y; + if (y > Bounds[TOP]) Bounds[TOP] = y; + } + void AddPt (WideVertex vert) + { + AddPt (vert.x >> FRACBITS, vert.y >> FRACBITS); + } + void AddPt (FPoint pt) + { + AddPt (pt.x, pt.y); + } + }; + + struct FBlockChain + { + FBlockChain() : Points(0) {} + ~FBlockChain() { if (Points) delete[] Points; } + + BBox Bounds; + FPoint *Points; + int NumPoints; + FBlockChain *Next; + }; + + enum { LEFT, TOP, RIGHT, BOTTOM }; + friend struct BBox; + +public: + FRejectBuilder (FLevel &level); + ~FRejectBuilder (); + + BYTE *GetReject (); + +private: + void FindSectorBounds (); + void FindBlockChains (); + void HullSides (const BBox &box1, const BBox &box2, FPoint sides[4]); + bool ChainBlocks (const FBlockChain *chain, const BBox *hullBounds, const FPoint *hullPts); + void BuildReject (); + + inline int PointOnSide (const FPoint *pt, const FPoint &lpt1, const FPoint &lpt2); + + BBox *SectorBounds; + BYTE *Reject; + + FLevel &Level; + int RejectSize; + + FBlockChain *BlockChains; +}; \ No newline at end of file diff --git a/Unused/rejectbuilder.h-slow b/Unused/rejectbuilder.h-slow new file mode 100644 index 0000000..2375ef1 --- /dev/null +++ b/Unused/rejectbuilder.h-slow @@ -0,0 +1,40 @@ +#include "zdbsp.h" +#include "tarray.h" +#include "doomdata.h" + +class FRejectBuilder +{ +public: + FRejectBuilder (FLevel &level); + ~FRejectBuilder (); + + BYTE *GetReject (); + +private: + struct Portal + { + const MapSubsector *Subsector; + const WideVertex *Left; + const WideVertex *Right; + }; + enum ESegSeeStatus + { + MIGHT_SEE, + CAN_SEE, + CANNOT_SEE + }; + + void BuildReject (); + void TracePath (int subsector, const MapSegGL *window); + void TracePathDeep (const MapSegGL *window); + inline const WideVertex *GetVertex (WORD vertnum); + + BYTE *SubSeeMatrix; + WORD *SegSubsectors; + TArray PortalStack; + + FLevel &Level; + + int SourceRow; + int SourceSeg, SegRow; +}; diff --git a/Unused/vis.cpp b/Unused/vis.cpp new file mode 100644 index 0000000..58ee613 --- /dev/null +++ b/Unused/vis.cpp @@ -0,0 +1,580 @@ +// An adaptation of the Quake vis utility. + +#include +#include + +#include "zdbsp.h" +#include "nodebuild.h" +#include "rejectbuilder.h" +#include "templates.h" + +bool FastVis=0; +bool NoPassageVis=1; +bool NoSort; + +//============================================================================= + +/* +============= +GetThreadWork + +============= +*/ +int FRejectBuilder::GetThreadWork () +{ + int r; + int f; + + if (dispatch == workcount) + { + return -1; + } + + if (pacifier) + { + if (dispatch >= workcount-1) + { + fprintf (stderr, "100%%\n"); + } + else if (oldcount < 0 || dispatch - oldcount > 200) + { + oldcount = dispatch; + f = 100*dispatch / workcount; + if (f != oldf) + { + oldf = f; + fprintf (stderr, "% 3d%%\b\b\b\b", f); + } + } + } + + r = dispatch++; + + return r; +} +void FRejectBuilder::RunThreadsOnIndividual (int workcnt, bool showpacifier, void(FRejectBuilder::*func)(int)) +{ + int work; + + pacifier = showpacifier; + workcount = workcnt; + dispatch = 0; + oldf = -1; + oldcount = -1; + + while (-1 != (work = GetThreadWork ())) + { + (this->*func) (work); + } +} + +//============================================================================= + +/* +============= +SortPortals + +Sorts the portals from the least complex, so the later ones can reuse +the earlier information. +============= +*/ +int FRejectBuilder::PComp (const void *a, const void *b) +{ + return (*(VPortal **)a)->nummightsee - (*(VPortal **)b)->nummightsee; +} +void FRejectBuilder::SortPortals () +{ + for (int i = 0; i < numportals; i++) + { + sorted_portals[i] = &portals[i]; + } + + if (!NoSort) + { + qsort (sorted_portals, numportals, sizeof(sorted_portals[0]), PComp); + } +} + + +/* +============== +LeafVectorFromPortalVector +============== +*/ +int FRejectBuilder::LeafVectorFromPortalVector (BYTE *portalbits, BYTE *leafbits) +{ + int i, j, leafnum; + VPortal *p; + int c_leafs; + + + for (i = 0; i < numportals; i++) + { + if (portalbits[i>>3] & (1<<(i&7)) ) + { + p = portals+i; + leafbits[p->leaf>>3] |= (1<<(p->leaf&7)); + //printf ("pleaf: from %d to %d\n", i, p->leaf); + } + } + + for (j = 0; j < portalclusters; j++) + { + leafnum = j; + while (leafs[leafnum].merged >= 0) + { + leafnum = leafs[leafnum].merged; + } + //if the merged leaf is visible then the original leaf is visible + if (leafbits[leafnum>>3] & (1<<(leafnum&7))) + { + leafbits[j>>3] |= (1<<(j&7)); + } + } + + c_leafs = CountBits (leafbits, portalclusters); + + return c_leafs; +} + + +/* +=============== +ClusterMerge + +Merges the portal visibility for a leaf +=============== +*/ +void FRejectBuilder::ClusterMerge (int leafnum) +{ + FLeaf *leaf; + BYTE portalvector[MAX_PORTALS/8]; + BYTE uncompressed[MAX_MAP_LEAFS/8]; + int i, j; + int numvis, mergedleafnum; + VPortal *p; + int pnum; + + // OR together all the portalvis bits + + mergedleafnum = leafnum; + while (leafs[mergedleafnum].merged >= 0) + { + mergedleafnum = leafs[mergedleafnum].merged; + } + + memset (portalvector, 0, portalbytes); + leaf = &leafs[mergedleafnum]; + for (i = 0; i < leaf->numportals; i++) + { + p = leaf->portals[i]; + if (p->removed) + continue; + + if (p->status != STAT_Done) + { + throw exception("portal not done"); + } + for (j = 0; j < portallongs; j++) + { + ((long *)portalvector)[j] |= ((long *)p->portalvis)[j]; + } + pnum = p - portals; + portalvector[pnum>>3] |= 1<<(pnum&7); + } + + memset (uncompressed, 0, leafbytes); + + // convert portal bits to leaf bits + uncompressed[mergedleafnum>>3] |= (1<<(mergedleafnum&7)); + numvis = LeafVectorFromPortalVector (portalvector, uncompressed); + + totalvis += numvis; + + //printf ("cluster %4i : %4i visible\n", leafnum, numvis); + + memcpy (visBytes + leafnum*leafbytes, uncompressed, leafbytes); +} + + +/* +================== +CalcPortalVis +================== +*/ +void FRejectBuilder::CalcPortalVis () +{ +#ifdef MREDEBUG + _printf("%6d portals out of %d", 0, numportals); + //get rid of the counter + RunThreadsOnIndividual (numportals, false, PortalFlow); +#else + RunThreadsOnIndividual (numportals, true, PortalFlow); +#endif + +} + +/* +================== +CalcPassagePortalVis +================== +*/ + +void FRejectBuilder::CalcPassagePortalVis () +{ + PassageMemory(); + +#ifdef MREDEBUG + _printf("%6d portals out of %d", 0, numportals); + RunThreadsOnIndividual (numportals, false, CreatePassages); + _printf("\n"); + _printf("%6d portals out of %d", 0, numportals); + RunThreadsOnIndividual (numportals, false, PassagePortalFlow); + _printf("\n"); +#else + RunThreadsOnIndividual (numportals, true, CreatePassages); + printf (" Vis 3: "); + RunThreadsOnIndividual (numportals, true, PassagePortalFlow); +#endif +} + +/* +================== +CalcFastVis +================== +*/ +void FRejectBuilder::CalcFastVis () +{ + // fastvis just uses mightsee for a very loose bound + for (int i = 0; i < numportals; i++) + { + portals[i].portalvis = portals[i].portalflood; + portals[i].status = STAT_Done; + } +} + +/* +================== +CalcVis +================== +*/ +void FRejectBuilder::CalcVis () +{ + printf (" Vis 1: "); + RunThreadsOnIndividual (numportals, true, BasePortalVis); + +// RunThreadsOnIndividual (numportals, true, BetterPortalVis); + + SortPortals (); + + printf (" Vis 2: "); + if (FastVis) + CalcFastVis(); + else if (NoPassageVis) + CalcPortalVis (); + else + CalcPassagePortalVis(); + // + // assemble the leaf vis lists by oring and compressing the portal lists + // + printf ("creating leaf vis...\n"); + for (int i = 0; i < portalclusters; i++) + { + ClusterMerge (i); + } + + printf ("Average clusters visible: %i\n", totalvis / portalclusters); +} + +/* +============= +Winding_PlanesConcave +============= +*/ +bool FRejectBuilder::Winding_PlanesConcave (const FWinding *w1, const FWinding *w2, + const FLine &line1, const FLine &line2) +{ + // check if one of the points of winding 1 is at the front of the line of winding 2 + if (PointOnSide (w1->points[0], line2) < 0 || + PointOnSide (w1->points[1], line2) < 0) + { + return true; + } + + // check if one of the points of winding 2 is at the front of the line of winding 1 + if (PointOnSide (w2->points[0], line1) < 0 || + PointOnSide (w2->points[1], line1) < 0) + { + return true; + } + + return false; +} + +/* +============ +TryMergeLeaves +============ +*/ +bool FRejectBuilder::TryMergeLeaves (int l1num, int l2num) +{ + int i, j, numportals; + FLeaf *l1, *l2; + VPortal *p1, *p2; + VPortal *portals[MAX_PORTALS_ON_LEAF]; + + l1 = &leafs[l1num]; + for (i = 0; i < l1->numportals; i++) + { + p1 = l1->portals[i]; + if (p1->leaf == l2num) continue; + l2 = &leafs[l2num]; + for (j = 0; j < l2->numportals; j++) + { + p2 = l2->portals[j]; + if (p2->leaf == l1num) continue; + // + if (Winding_PlanesConcave (&p1->winding, &p2->winding, p1->line, p2->line)) + return false; + } + } + l1 = &leafs[l1num]; + l2 = &leafs[l2num]; + numportals = 0; + //the leaves can be merged now + for (i = 0; i < l1->numportals; i++) + { + p1 = l1->portals[i]; + if (p1->leaf == l2num) + { + p1->removed = true; + continue; + } + portals[numportals++] = p1; + } + for (j = 0; j < l2->numportals; j++) + { + p2 = l2->portals[j]; + if (p2->leaf == l1num) + { + p2->removed = true; + continue; + } + portals[numportals++] = p2; + } + delete[] l1->portals; + l1->portals = NULL; + l1->numportals = 0; + delete[] l2->portals; + l2->portals = new VPortal *[numportals]; + for (i = 0; i < numportals; i++) + { + l2->portals[i] = portals[i]; + } + l2->numportals = numportals; + l1->merged = l2num; + return true; +} + +/* +============ +UpdatePortals +============ +*/ +void FRejectBuilder::UpdatePortals () +{ + int i; + VPortal *p; + + for (i = 0; i < numportals; i++) + { + p = &portals[i]; + if (!p->removed) + { + while (leafs[p->leaf].merged >= 0) + { + p->leaf = leafs[p->leaf].merged; + } + } + } +} + +/* +============ +MergeLeaves + +try to merge leaves but don't merge through hint splitters +============ +*/ +void FRejectBuilder::MergeLeaves () +{ + int i, j, nummerges, totalnummerges; + FLeaf *leaf; + VPortal *p; + + totalnummerges = 0; + do + { + printf ("."); + nummerges = 0; + for (i = 0; i < portalclusters; i++) + { + leaf = &leafs[i]; + //if this leaf is merged already + if (leaf->merged >= 0) + continue; + // + for (j = 0; j < leaf->numportals; j++) + { + p = leaf->portals[j]; + // + if (p->removed) + continue; + //never merge through hint portals + if (p->hint) + continue; + if (TryMergeLeaves(i, p->leaf)) + { + UpdatePortals(); + nummerges++; + break; + } + } + } + totalnummerges += nummerges; + } while (nummerges); + printf("\r%6d leaves merged\n", totalnummerges); +} + +/* +============ +TryMergeWinding +============ +*/ + +FRejectBuilder::FWinding *FRejectBuilder::TryMergeWinding (FWinding *f1, FWinding *f2, const FLine &line) +{ + static FWinding result; + int i, j; + + // + // find a common point + // + for (i = 0; i < 2; ++i) + { + for (j = 0; j < 2; ++j) + { + if (f1->points[i].x == f2->points[j].x && + f1->points[i].y == f2->points[j].y) + { + goto found; + } + } + } + + // no shared point + return NULL; + +found: + // + // if the lines are colinear, the point can be removed + // + if (PointOnSide (f2->points[0], line) != 0 || + PointOnSide (f2->points[1], line) != 0) + { // not colinear + return NULL; + } + + // + // build the new segment + // + if (i == 0) + { + result.points[0] = f2->points[!j]; + result.points[1] = f1->points[1]; + } + else + { + result.points[0] = f1->points[0]; + result.points[1] = f2->points[!j]; + } + return &result; +} + +/* +============ +MergeLeafPortals +============ +*/ +void FRejectBuilder::MergeLeafPortals () +{ + int i, j, k, nummerges, hintsmerged; + FLeaf *leaf; + VPortal *p1, *p2; + FWinding *w; + + nummerges = 0; + hintsmerged = 0; + for (i = 0; i < portalclusters; i++) + { + leaf = &leafs[i]; + if (leaf->merged >= 0) continue; + for (j = 0; j < leaf->numportals; j++) + { + p1 = leaf->portals[j]; + if (p1->removed) + continue; + for (k = j+1; k < leaf->numportals; k++) + { + p2 = leaf->portals[k]; + if (p2->removed) + continue; + if (p1->leaf == p2->leaf) + { + w = TryMergeWinding (&p1->winding, &p2->winding, p1->line); + if (w) + { + p1->winding = *w; + if (p1->hint && p2->hint) + hintsmerged++; + p1->hint |= p2->hint; + p2->removed = true; + nummerges++; + i--; + break; + } + } + } + if (k < leaf->numportals) + break; + } + } + printf("%6d portals merged\n", nummerges); + printf("%6d hint portals merged\n", hintsmerged); +} + +/* +============ +CountActivePortals +============ +*/ +int FRejectBuilder::CountActivePortals () +{ + int num, hints, j; + VPortal *p; + + num = 0; + hints = 0; + for (j = 0; j < numportals; j++) + { + p = portals + j; + if (p->removed) + continue; + if (p->hint) + hints++; + num++; + } + printf("%6d of %d active portals\n", num, numportals); + printf("%6d hint portals\n", hints); + return num; +} diff --git a/Unused/visflow.cpp b/Unused/visflow.cpp new file mode 100644 index 0000000..17caefa --- /dev/null +++ b/Unused/visflow.cpp @@ -0,0 +1,1345 @@ +// An adaptation of the Quake vis utility. + +#include +#include + +#include "zdbsp.h" +#include "nodebuild.h" +#include "rejectbuilder.h" +#include "templates.h" + +enum { SIDE_FRONT, SIDE_BACK, SIDE_ON }; + +/* + + each portal will have a list of all possible to see from first portal + + if (!thread->portalmightsee[portalnum]) + + portal mightsee + + for p2 = all other portals in leaf + get sperating planes + for all portals that might be seen by p2 + mark as unseen if not present in seperating plane + flood fill a new mightsee + save as passagemightsee + + + void CalcMightSee (leaf_t *leaf, +*/ + +int FRejectBuilder::CountBits (BYTE *bits, int numbits) +{ + int i; + int c; + + c = 0; + for (i=0 ; i>3] & (1<<(i&7)) ) + c++; + + return c; +} + +int c_fullskip; +int c_portalskip, c_leafskip; +int c_vistest, c_mighttest; + +int c_chop, c_nochop; + +int active; + +void FRejectBuilder::CheckStack (FLeaf *leaf, FThreadData *thread) +{ + PStack *p, *p2; + + for (p = thread->pstack_head.next; p != NULL; p = p->next) + { +// _printf ("="); + if (p->leaf == leaf) + throw exception("CheckStack: leaf recursion"); + for (p2 = thread->pstack_head.next; p2 != p; p2 = p2->next) + if (p2->leaf == p->leaf) + throw exception("CheckStack: late leaf recursion"); + } +// _printf ("\n"); +} + + +FRejectBuilder::FWinding *FRejectBuilder::AllocStackWinding (PStack *stack) const +{ + for (int i = 0; i < 3; i++) + { + if (stack->freewindings[i]) + { + stack->freewindings[i] = false; + return &stack->windings[i]; + } + } + + throw exception("AllocStackWinding: failed"); +} + +void FRejectBuilder::FreeStackWinding (FWinding *w, PStack *stack) const +{ + int i; + + i = w - stack->windings; + + if (i < 0 || i > 2) + return; // not from local + + if (stack->freewindings[i]) + throw exception("FreeStackWinding: already free"); + stack->freewindings[i] = true; +} + +/* +============== +VisChopWinding + +============== +*/ +FRejectBuilder::FWinding *FRejectBuilder::VisChopWinding (FWinding *in, PStack *stack, FLine *split) +{ + int side1, side2; + FPoint mid; + FWinding *neww; + + // determine sides for each point + side1 = PointOnSide (in->points[0], *split); + side2 = PointOnSide (in->points[1], *split); + + if (side1 <= 0 && side2 <= 0) + { // completely on front side + return in; + } + + if (side1 >= 0 && side2 >= 0) + { // completely on back side + FreeStackWinding (in, stack); + return NULL; + } + + neww = AllocStackWinding (stack); + + // generate a split point + double v2x = (double)in->points[0].x; + double v2y = (double)in->points[0].y; + double v2dx = (double)in->points[1].x - v2x; + double v2dy = (double)in->points[1].y - v2y; + double v1dx = (double)split->dx; + double v1dy = (double)split->dy; + + double den = v1dy*v2dx - v1dx*v2dy; + + if (den == 0.0) + { // parallel + return in; + } + + double v1x = (double)split->x; + double v1y = (double)split->y; + + double num = (v1x - v2x)*v1dy + (v2y - v1y)*v1dx; + double frac = num / den; + + mid.x = in->points[0].x + fixed_t(v2dx * frac); + mid.y = in->points[0].y + fixed_t(v2dy * frac); + + if (side1 <= 0) + { + neww->points[0] = in->points[0]; + neww->points[1] = mid; + } + else + { + neww->points[0] = mid; + neww->points[1] = in->points[1]; + } + + // free the original winding + FreeStackWinding (in, stack); + + return neww; +} + +/* +============== +ClipToSeperators + +Source, pass, and target are an ordering of portals. + +Generates seperating planes canidates by taking two points from source and one +point from pass, and clips target by them. + +If target is totally clipped away, that portal can not be seen through. + +Normal clip keeps target on the same side as pass, which is correct if the +order goes source, pass, target. If the order goes pass, source, target then +flipclip should be set. +============== +*/ +FRejectBuilder::FWinding *FRejectBuilder::ClipToSeperators + (FWinding *source, FWinding *pass, FWinding *target, bool flipclip, PStack *stack) +{ + int i, j; + FLine line; + int d; + bool fliptest; + + // check all combinations + for (i = 0; i < 2; i++) + { + // find a vertex of pass that makes a line that puts all of the + // vertexes of pass on the front side and all of the vertexes of + // source on the back side + for (j = 0; j < 2; j++) + { + line.x = source->points[i].x; + line.y = source->points[i].y; + line.dx = pass->points[j].x - line.x; + line.dy = pass->points[j].y - line.y; + + // + // find out which side of the generated seperating line has the + // source portal + // + fliptest = false; + d = PointOnSide (source->points[!i], line); + if (d > 0) + { // source is on the back side, so we want all + // pass and target on the front side + fliptest = false; + } + else if (d < 0) + { // source in on the front side, so we want all + // pass and target on the back side + fliptest = true; + } + else + { // colinear with source portal + continue; + } + + // + // flip the line if the source portal is backwards + // + if (fliptest) + { + line.Flip (); + } + + // + // if all of the pass portal points are now on the front side, + // this is the seperating line + // + d = PointOnSide (pass->points[!j], line); + if (d >= 0) + { // == 0: colinear with seperating plane + // > 0: points on back side; not a seperating plane + continue; + } + + // + // flip the line if we want the back side + // + if (flipclip) + { + line.Flip (); + } + +#ifdef SEPERATORCACHE + stack->seperators[flipclip][stack->numseperators[flipclip]] = line; + if (++stack->numseperators[flipclip] >= MAX_SEPERATORS) + throw exception("MAX_SEPERATORS"); +#endif + + // + // clip target by the seperating plane + // + target = VisChopWinding (target, stack, &line); + if (!target) + { // target is not visible + return NULL; + } + + break; // optimization by Antony Suter + } + } + + return target; +} + +/* +================== +RecursiveLeafFlow + +Flood fill through the leafs +If src_portal is NULL, this is the originating leaf +================== +*/ +void FRejectBuilder::RecursiveLeafFlow (int leafnum, FThreadData *thread, PStack *prevstack) +{ + PStack stack; + VPortal *p; + FLine backline; + FLeaf *leaf; + int i, j; + long *test, *might, *prevmight, *vis, more; + int pnum; + + thread->c_chains++; + + leaf = &leafs[leafnum]; +// CheckStack (leaf, thread); + + prevstack->next = &stack; + + stack.next = NULL; + stack.leaf = leaf; + stack.portal = NULL; + stack.depth = prevstack->depth + 1; + +#ifdef SEPERATORCACHE + stack.numseperators[0] = 0; + stack.numseperators[1] = 0; +#endif + + might = (long *)stack.mightsee; + vis = (long *)thread->base->portalvis; + + // check all portals for flowing into other leafs + for (i = 0; i < leaf->numportals; i++) + { + p = leaf->portals[i]; + if (p->removed) + continue; + pnum = p - portals; + + /* MrE: portal trace debug code + { + int portaltrace[] = {13, 16, 17, 37}; + pstack_t *s; + + s = &thread->pstack_head; + for (j = 0; s->next && j < sizeof(portaltrace)/sizeof(int) - 1; j++, s = s->next) + { + if (s->portal->num != portaltrace[j]) + break; + } + if (j >= sizeof(portaltrace)/sizeof(int) - 1) + { + if (p->num == portaltrace[j]) + n = 0; //traced through all the portals + } + } + */ + + if ( ! (prevstack->mightsee[pnum >> 3] & (1<<(pnum&7)) ) ) + { + continue; // can't possibly see it + } + + // if the portal can't see anything we haven't already seen, skip it + if (p->status == STAT_Done) + { + test = (long *)p->portalvis; + } + else + { + test = (long *)p->portalflood; + } + + more = 0; + prevmight = (long *)prevstack->mightsee; + for (j = 0; j < portallongs; j++) + { + might[j] = prevmight[j] & test[j]; + more |= (might[j] & ~vis[j]); + } + + if (!more && + (thread->base->portalvis[pnum>>3] & (1<<(pnum&7))) ) + { // can't see anything new + continue; + } + + // get line of portal and point into the neighbor leaf + backline = stack.portalline = p->line; + backline.Flip (); + +// c_portalcheck++; + + stack.portal = p; + stack.next = NULL; + stack.freewindings[0] = true; + stack.freewindings[1] = true; + stack.freewindings[2] = true; + + stack.pass = VisChopWinding (&p->winding, &stack, &thread->pstack_head.portalline); + if (!stack.pass) + { + continue; + } + + stack.source = VisChopWinding (prevstack->source, &stack, &backline); + if (!stack.source) + { + continue; + } + + if (!prevstack->pass) + { // the second leaf can only be blocked if coplanar + + // mark the portal as visible + thread->base->portalvis[pnum>>3] |= (1<<(pnum&7)); + + RecursiveLeafFlow (p->leaf, thread, &stack); + continue; + } + +#ifdef SEPERATORCACHE + if (stack.numseperators[0]) + { + for (n = 0; n < stack.numseperators[0]; n++) + { + stack.pass = VisChopWinding (stack.pass, &stack, &stack.seperators[0][n]); + if (!stack.pass) + break; // target is not visible + } + if (n < stack.numseperators[0]) + continue; + } + else + { + stack.pass = ClipToSeperators (prevstack->source, prevstack->pass, stack.pass, false, &stack); + } +#else + stack.pass = ClipToSeperators (stack.source, prevstack->pass, stack.pass, false, &stack); +#endif + if (!stack.pass) + continue; + +#ifdef SEPERATORCACHE + if (stack.numseperators[1]) + { + for (n = 0; n < stack.numseperators[1]; n++) + { + stack.pass = VisChopWinding (stack.pass, &stack, &stack.seperators[1][n]); + if (!stack.pass) + break; // target is not visible + } + } + else + { + stack.pass = ClipToSeperators (prevstack->pass, prevstack->source, stack.pass, true, &stack); + } +#else + stack.pass = ClipToSeperators (prevstack->pass, stack.source, stack.pass, true, &stack); +#endif + if (!stack.pass) + continue; + + // mark the portal as visible + thread->base->portalvis[pnum>>3] |= (1<<(pnum&7)); + + // flow through it for real + RecursiveLeafFlow (p->leaf, thread, &stack); + // + stack.next = NULL; + } +} + +/* +=============== +PortalFlow + +generates the portalvis bit vector +=============== +*/ +void FRejectBuilder::PortalFlow (int portalnum) +{ + FThreadData data; + int i; + VPortal *p; + int c_might, c_can; + +#ifdef MREDEBUG + printf("\r%6d", portalnum); +#endif + + p = sorted_portals[portalnum]; + + if (p->removed) + { + p->status = STAT_Done; + return; + } + + if (p->nummightsee == 0) + { + p->status = STAT_Done; + return; + } + + p->status = STAT_Working; + + c_might = p->nummightsee;//CountBits (p->portalflood, numportals); + + memset (&data, 0, sizeof(data)); + data.base = p; + + data.pstack_head.portal = p; + data.pstack_head.source = &p->winding; + data.pstack_head.portalline = p->line; + data.pstack_head.depth = 0; + for (i = 0; i < portallongs; i++) + { + ((long *)data.pstack_head.mightsee)[i] = ((long *)p->portalflood)[i]; + } + + RecursiveLeafFlow (p->leaf, &data, &data.pstack_head); + + p->status = STAT_Done; + + c_can = CountBits (p->portalvis, numportals); + + //printf ("portal:%4i mightsee:%4i cansee:%4i (%i chains)\n", + // (int)(p - portals), c_might, c_can, data.c_chains); +} + +/* +================== +RecursivePassageFlow +================== +*/ +void FRejectBuilder::RecursivePassageFlow (VPortal *portal, FThreadData *thread, PStack *prevstack) +{ + PStack stack; + VPortal *p; + FLeaf *leaf; + FPassage *passage, *nextpassage; + int i, j; + long *might, *vis, *prevmight, *cansee, *portalvis, more; + int pnum; + +// thread->c_chains++; + + leaf = &leafs[portal->leaf]; +// CheckStack (leaf, thread); + + prevstack->next = &stack; + + stack.next = NULL; +// stack.leaf = leaf; +// stack.portal = NULL; + stack.depth = prevstack->depth + 1; + + vis = (long *)thread->base->portalvis; + + passage = portal->passages; + nextpassage = passage; + // check all portals for flowing into other leafs + for (i = 0; i < leaf->numportals; i++, passage = nextpassage) + { + p = leaf->portals[i]; + if (p->removed) + continue; + nextpassage = passage->next; + pnum = p - portals; + + if ( ! (prevstack->mightsee[pnum >> 3] & (1<<(pnum&7)) ) ) + continue; // can't possibly see it + + prevmight = (long *)prevstack->mightsee; + cansee = (long *)passage->cansee; + might = (long *)stack.mightsee; + memcpy(might, prevmight, portalbytes); + portalvis = (p->status == STAT_Done) ? (long *)p->portalvis : (long *)p->portalflood; + more = 0; + for (j = 0; j < portallongs; j++) + { + if (*might) + { + *might &= *cansee++ & *portalvis++; + more |= (*might & ~vis[j]); + } + else + { + cansee++; + portalvis++; + } + might++; + } + + if (!more && + (thread->base->portalvis[pnum>>3] & (1<<(pnum&7))) ) + { // can't see anything new + continue; + } + +// stack.portal = p; + // mark the portal as visible + thread->base->portalvis[pnum>>3] |= (1<<(pnum&7)); + // flow through it for real + RecursivePassageFlow(p, thread, &stack); + // + stack.next = NULL; + } +} + +/* +=============== +PassageFlow +=============== +*/ +void FRejectBuilder::PassageFlow (int portalnum) +{ + FThreadData data; + int i; + VPortal *p; +// int c_might, c_can; + +#ifdef MREDEBUG + printf("\r%6d", portalnum); +#endif + + p = sorted_portals[portalnum]; + + if (p->removed) + { + p->status = STAT_Done; + return; + } + + p->status = STAT_Working; + +// c_might = CountBits (p->portalflood, numportals); + + memset (&data, 0, sizeof(data)); + data.base = p; + + data.pstack_head.portal = p; + data.pstack_head.source = &p->winding; + data.pstack_head.portalline = p->line; + data.pstack_head.depth = 0; + for (i = 0; i < portallongs; i++) + { + ((long *)data.pstack_head.mightsee)[i] = ((long *)p->portalflood)[i]; + } + + RecursivePassageFlow (p, &data, &data.pstack_head); + + p->status = STAT_Done; + + /* + c_can = CountBits (p->portalvis, numportals); + + qprintf ("portal:%4i mightsee:%4i cansee:%4i (%i chains)\n", + (int)(p - portals), c_might, c_can, data.c_chains); + */ +} + +/* +================== +RecursivePassagePortalFlow +================== +*/ +void FRejectBuilder::RecursivePassagePortalFlow (VPortal *portal, FThreadData *thread, PStack *prevstack) +{ + PStack stack; + VPortal *p; + FLeaf *leaf; + FLine backline; + FPassage *passage, *nextpassage; + int i, j; + long *might, *vis, *prevmight, *cansee, *portalvis, more; + int pnum; + +// thread->c_chains++; + + leaf = &leafs[portal->leaf]; +// CheckStack (leaf, thread); + + prevstack->next = &stack; + + stack.next = NULL; + stack.leaf = leaf; + stack.portal = NULL; + stack.depth = prevstack->depth + 1; + +#ifdef SEPERATORCACHE + stack.numseperators[0] = 0; + stack.numseperators[1] = 0; +#endif + + vis = (long *)thread->base->portalvis; + + passage = portal->passages; + nextpassage = passage; + // check all portals for flowing into other leafs + for (i = 0; i < leaf->numportals; i++, passage = nextpassage) + { + p = leaf->portals[i]; + if (p->removed) + continue; + nextpassage = passage->next; + pnum = p - portals; + + if ( ! (prevstack->mightsee[pnum >> 3] & (1<<(pnum&7)) ) ) + continue; // can't possibly see it + + prevmight = (long *)prevstack->mightsee; + cansee = (long *)passage->cansee; + might = (long *)stack.mightsee; + memcpy(might, prevmight, portalbytes); + portalvis = (p->status == STAT_Done) ? (long *) p->portalvis : (long *) p->portalflood; + more = 0; + for (j = 0; j < portallongs; j++) + { + if (*might) + { + *might &= *cansee++ & *portalvis++; + more |= (*might & ~vis[j]); + } + else + { + cansee++; + portalvis++; + } + might++; + } + + if (!more && + (thread->base->portalvis[pnum>>3] & (1<<(pnum&7))) ) + { // can't see anything new + continue; + } + + // get line of portal, point front into the neighbor leaf + backline = stack.portalline = p->line; + backline.Flip (); + +// c_portalcheck++; + + stack.portal = p; + stack.next = NULL; + stack.freewindings[0] = true; + stack.freewindings[1] = true; + stack.freewindings[2] = true; + + stack.pass = VisChopWinding (&p->winding, &stack, &thread->pstack_head.portalline); + if (!stack.pass) + continue; + + stack.source = VisChopWinding (prevstack->source, &stack, &backline); + if (!stack.source) + continue; + + if (!prevstack->pass) + { // the second leaf can only be blocked if colinear + + // mark the portal as visible + thread->base->portalvis[pnum>>3] |= (1<<(pnum&7)); + + RecursivePassagePortalFlow (p, thread, &stack); + continue; + } + +#ifdef SEPERATORCACHE + if (stack.numseperators[0]) + { + for (n = 0; n < stack.numseperators[0]; n++) + { + stack.pass = VisChopWinding (stack.pass, &stack, &stack.seperators[0][n]); + if (!stack.pass) + break; // target is not visible + } + if (n < stack.numseperators[0]) + continue; + } + else + { + stack.pass = ClipToSeperators (prevstack->source, prevstack->pass, stack.pass, false, &stack); + } +#else + stack.pass = ClipToSeperators (stack.source, prevstack->pass, stack.pass, false, &stack); +#endif + if (!stack.pass) + continue; + +#ifdef SEPERATORCACHE + if (stack.numseperators[1]) + { + for (n = 0; n < stack.numseperators[1]; n++) + { + stack.pass = VisChopWinding (stack.pass, &stack, &stack.seperators[1][n]); + if (!stack.pass) + break; // target is not visible + } + } + else + { + stack.pass = ClipToSeperators (prevstack->pass, prevstack->source, stack.pass, true, &stack); + } +#else + stack.pass = ClipToSeperators (prevstack->pass, stack.source, stack.pass, true, &stack); +#endif + if (!stack.pass) + continue; + + // mark the portal as visible + thread->base->portalvis[pnum>>3] |= (1<<(pnum&7)); + // flow through it for real + RecursivePassagePortalFlow(p, thread, &stack); + // + stack.next = NULL; + } +} + +/* +=============== +PassagePortalFlow +=============== +*/ +void FRejectBuilder::PassagePortalFlow (int portalnum) +{ + FThreadData data; + int i; + VPortal *p; +// int c_might, c_can; + +#ifdef MREDEBUG + printf("\r%6d", portalnum); +#endif + + p = sorted_portals[portalnum]; + + if (p->removed) + { + p->status = STAT_Done; + return; + } + + p->status = STAT_Working; + +// c_might = CountBits (p->portalflood, numportals); + + memset (&data, 0, sizeof(data)); + data.base = p; + + data.pstack_head.portal = p; + data.pstack_head.source = &p->winding; + data.pstack_head.portalline = p->line; + data.pstack_head.depth = 0; + for (i = 0; i < portallongs; i++) + { + ((long *)data.pstack_head.mightsee)[i] = ((long *)p->portalflood)[i]; + } + + RecursivePassagePortalFlow (p, &data, &data.pstack_head); + + p->status = STAT_Done; + + /* + c_can = CountBits (p->portalvis, numportals); + + qprintf ("portal:%4i mightsee:%4i cansee:%4i (%i chains)\n", + (int)(p - portals), c_might, c_can, data.c_chains); + */ +} + +FRejectBuilder::FWinding *FRejectBuilder::PassageChopWinding (FWinding *in, FWinding *out, FLine *split) +{ + int side1, side2; + FPoint mid; + FWinding *neww; + + // determine sides for each point + side1 = PointOnSide (in->points[0], *split); + side2 = PointOnSide (in->points[1], *split); + + if (side1 <= 0 && side2 <= 0) + { // completely on front side + return in; + } + + if (side1 >= 0 && side2 >= 0) + { // completely on back side + return NULL; + } + + neww = out; + + // generate a split point + double v2x = (double)in->points[0].x; + double v2y = (double)in->points[0].y; + double v2dx = (double)in->points[1].x - v2x; + double v2dy = (double)in->points[1].y - v2y; + double v1dx = (double)split->dx; + double v1dy = (double)split->dy; + + double den = v1dy*v2dx - v1dx*v2dy; + + if (den == 0.0) + { // parallel + return in; + } + + double v1x = (double)split->x; + double v1y = (double)split->y; + + double num = (v1x - v2x)*v1dy + (v2y - v1y)*v1dx; + double frac = num / den; + + mid.x = in->points[0].x + fixed_t(v2dx * frac); + mid.y = in->points[0].y + fixed_t(v2dy * frac); + + if (side1 <= 0) + { + neww->points[0] = in->points[0]; + neww->points[1] = mid; + } + else + { + neww->points[0] = mid; + neww->points[1] = in->points[1]; + } + + return neww; +} + +/* +=============== +AddSeperators +=============== +*/ +int FRejectBuilder::AddSeperators (FWinding *source, FWinding *pass, bool flipclip, + FLine *seperators, int maxseperators) +{ + int i, j; + FLine line; + int d; + int numseperators; + bool fliptest; + + numseperators = 0; + // check all combinations + for (i = 0; i < 2; i++) + { + // find a vertex of pass that makes a plane that puts all of the + // vertexes of pass on the front side and all of the vertexes of + // source on the back side + for (j = 0; j < 2; j++) + { + line.x = source->points[i].x; + line.y = source->points[i].y; + line.dx = pass->points[j].x - line.x; + line.dy = pass->points[j].y - line.y; + + // + // find out which side of the generated seperating plane has the + // source portal + // + fliptest = false; + d = PointOnSide (source->points[!i], line); + if (d > 0) + { // source is on the back side, so we want all + // pass and target on the front side + fliptest = false; + } + else if (d < 0) + { // source in on the front side, so we want all + // pass and target on the back side + fliptest = true; + } + else + { // colinear with source portal + continue; + } + + // + // flip the line if the source portal is backwards + // + if (fliptest) + { + line.Flip (); + } + + // + // if all of the pass portal points are now on the positive side, + // this is the seperating plane + // + d = PointOnSide (pass->points[!j], line); + if (d >= 0) + { // == 0: colinear with seperating plane + // > 0: points on back side; not a seperating plane + continue; + } + + // + // flip the line if we want the back side + // + if (flipclip) + { + line.Flip (); + } + + if (numseperators >= maxseperators) + throw exception("max seperators"); + seperators[numseperators] = line; + numseperators++; + break; + } + } + return numseperators; +} + +/* +=============== +CreatePassages + +MrE: create passages from one portal to all the portals in the leaf the portal leads to + every passage has a cansee bit string with all the portals that can be + seen through the passage +=============== +*/ +void FRejectBuilder::CreatePassages (int portalnum) +{ + int i, j, k, numseperators, numsee; + VPortal *portal, *p, *target; + FLeaf *leaf; + FPassage *passage, *lastpassage; + FLine seperators[MAX_SEPERATORS*2]; + FWinding in, out, *res; + +#ifdef MREDEBUG + printf("\r%6d", portalnum); +#endif + + portal = sorted_portals[portalnum]; + + if (portal->removed) + { + portal->status = STAT_Done; + return; + } + + lastpassage = NULL; + leaf = &leafs[portal->leaf]; + for (i = 0; i < leaf->numportals; i++) + { + target = leaf->portals[i]; + if (target->removed) + continue; + + passage = (FPassage *) malloc(sizeof(FPassage) + portalbytes); + memset(passage, 0, sizeof(FPassage) + portalbytes); + numseperators = AddSeperators(&portal->winding, &target->winding, false, seperators, MAX_SEPERATORS*2); + numseperators += AddSeperators(&target->winding, &portal->winding, true, &seperators[numseperators], MAX_SEPERATORS*2-numseperators); + + passage->next = NULL; + if (lastpassage) + lastpassage->next = passage; + else + portal->passages = passage; + lastpassage = passage; + + numsee = 0; + //create the passage->cansee + for (j = 0; j < numportals; j++) + { + p = &portals[j]; + if (p->removed) + continue; + if ( ! (target->portalflood[j >> 3] & (1<<(j&7)) ) ) + continue; + if ( ! (portal->portalflood[j >> 3] & (1<<(j&7)) ) ) + continue; + for (k = 0; k < numseperators; k++) + { + //check if completely on the back of the seperator line + if (PointOnSide (p->line, seperators[k]) > 0) + { + FPoint pt2 = p->line; + pt2.x += p->line.dx; + pt2.y += p->line.dy; + if (PointOnSide (pt2, seperators[k]) > 0) + { + break; + } + } + } + if (k < numseperators) + { + continue; + } + memcpy(&in, &p->winding, sizeof(FWinding)); + for (k = 0; k < numseperators; k++) + { + res = PassageChopWinding(&in, &out, &seperators[k]); + if (res == &out) + memcpy(&in, &out, sizeof(FWinding)); + if (res == NULL) + break; + } + if (k < numseperators) + continue; + passage->cansee[j >> 3] |= (1<<(j&7)); + numsee++; + } + } +} + +void FRejectBuilder::PassageMemory () +{ + int i, j, totalmem, totalportals; + VPortal *portal, *target; + FLeaf *leaf; + + totalmem = 0; + totalportals = 0; + for (i = 0; i < numportals; i++) + { + portal = sorted_portals[i]; + if (portal->removed) + continue; + leaf = &leafs[portal->leaf]; + for (j = 0; j < leaf->numportals; j++) + { + target = leaf->portals[j]; + if (target->removed) + continue; + totalmem += sizeof(FPassage) + portalbytes; + totalportals++; + } + } + printf("\n%7i average number of passages per leaf\n", totalportals / numportals); + printf("%7i MB required passage memory\n", totalmem >> 10 >> 10); +} + +/* +=============================================================================== + +This is a rough first-order aproximation that is used to trivially reject some +of the final calculations. + + +Calculates portalfront and portalflood bit vectors + +thinking about: + +typedef struct passage_s +{ + struct passage_s *next; + struct portal_s *to; + struct sep_s *seperators; + byte *mightsee; +} passage_t; + +typedef struct portal_s +{ + struct passage_s *passages; + int leaf; // leaf portal faces into +} portal_s; + +leaf = portal->leaf +clear +for all portals + + +calc portal visibility + clear bit vector + for all passages + passage visibility + + +for a portal to be visible to a passage, it must be on the front of +all seperating planes, and both portals must be behind the new portal + +=============================================================================== +*/ + +int c_flood, c_vis; + + +/* +================== +SimpleFlood + +================== +*/ +void FRejectBuilder::SimpleFlood (VPortal *srcportal, int leafnum) +{ + int i; + FLeaf *leaf; + VPortal *p; + int pnum; + + leaf = &leafs[leafnum]; + + for (i = 0; i < leaf->numportals; i++) + { + p = leaf->portals[i]; + if (p->removed) + continue; + pnum = p - portals; + if ((srcportal->portalfront[pnum>>3] & (1<<(pnum&7))) && + !(srcportal->portalflood[pnum>>3] & (1<<(pnum&7))) ) + { + srcportal->portalflood[pnum>>3] |= (1<<(pnum&7)); + SimpleFlood (srcportal, p->leaf); + } + } +} + +/* +============== +BasePortalVis +============== +*/ +void FRejectBuilder::BasePortalVis (int portalnum) +{ + int j, p1, p2; + VPortal *tp, *p; + + p = portals+portalnum; + + if (p->removed) + return; + + p->portalfront = new BYTE[portalbytes]; + memset (p->portalfront, 0, portalbytes); + + p->portalflood = new BYTE[portalbytes]; + memset (p->portalflood, 0, portalbytes); + + p->portalvis = new BYTE[portalbytes]; + memset (p->portalvis, 0, portalbytes); + + for (j = 0, tp = portals; j < numportals; j++, tp++) + { + if (j == portalnum) + continue; + if (tp->removed) + continue; + + //p->portalfront[j>>3] |= (1<<(j&7)); + //continue; + + // The target portal must be in front of this one + if ((p1 = PointOnSide (tp->winding.points[0], p->line)) > 0 || + (p2 = PointOnSide (tp->winding.points[1], p->line)) > 0) + { + continue; + } + + // Portals must not be colinear + if ((p1 | p2) == 0) + { + continue; + } + + // This portal must be behind the target portal + if (PointOnSide (p->winding.points[0], tp->line) < 0 || + PointOnSide (p->winding.points[1], tp->line) < 0) + { + continue; + } + + p->portalfront[j>>3] |= (1<<(j&7)); + } + + SimpleFlood (p, p->leaf); + + p->nummightsee = CountBits (p->portalflood, numportals); +// _printf ("portal %i: %i mightsee\n", portalnum, p->nummightsee); + c_flood += p->nummightsee; +} + + + + + +/* +=============================================================================== + +This is a second order aproximation + +Calculates portalvis bit vector + +WAAAAAAY too slow. + +=============================================================================== +*/ + +/* +================== +RecursiveLeafBitFlow + +================== +*/ +void FRejectBuilder::RecursiveLeafBitFlow (int leafnum, BYTE *mightsee, BYTE *cansee) +{ + VPortal *p; + FLeaf *leaf; + int i, j; + long more; + int pnum; + BYTE newmight[MAX_PORTALS/8]; + + leaf = &leafs[leafnum]; + + // check all portals for flowing into other leafs + for (i = 0; i < leaf->numportals; i++) + { + p = leaf->portals[i]; + if (p->removed) + continue; + pnum = p - portals; + + // if some previous portal can't see it, skip + if (! (mightsee[pnum>>3] & (1<<(pnum&7)) ) ) + continue; + + // if this portal can see some portals we mightsee, recurse + more = 0; + for (j = 0; j < portallongs; j++) + { + ((long *)newmight)[j] = ((long *)mightsee)[j] & ((long *)p->portalflood)[j]; + more |= ((long *)newmight)[j] & ~((long *)cansee)[j]; + } + + if (!more) + continue; // can't see anything new + + cansee[pnum>>3] |= (1<<(pnum&7)); + + RecursiveLeafBitFlow (p->leaf, newmight, cansee); + } +} + +/* +============== +BetterPortalVis +============== +*/ +void FRejectBuilder::BetterPortalVis (int portalnum) +{ + VPortal *p; + + p = portals+portalnum; + + if (p->removed) + return; + + RecursiveLeafBitFlow (p->leaf, p->portalflood, p->portalvis); + + // build leaf vis information + p->nummightsee = CountBits (p->portalvis, numportals); + c_vis += p->nummightsee; +} + + diff --git a/blockmapbuilder.cpp b/blockmapbuilder.cpp new file mode 100644 index 0000000..406f208 --- /dev/null +++ b/blockmapbuilder.cpp @@ -0,0 +1,455 @@ +/* + Routines for building a Doom map's BLOCKMAP lump. + Copyright (C) 2002 Randy Heit + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#include +#include +#include + +#include "zdbsp.h" +#include "templates.h" +#include "tarray.h" +#include "blockmapbuilder.h" + +#undef BLOCK_TEST + +FBlockmapBuilder::FBlockmapBuilder (FLevel &level) + : Level (level) +{ + BuildBlockmap (); +} + +#ifdef BLOCK_TEST +inline int PointOnSide (int x, int y, int x1, int y1, int dx, int dy) +{ + int foo = DMulScale32 ((y-y1) << 16, dx<<16, (x1-x)<<16, (dy)<<16); + //return abs(foo) < 4 ? 0 : foo; + return foo > 0; +} + +int BoxOnSide (int bx1, int by1, int bx2, int by2, + int lx1, int ly1, int lx2, int ly2) +{ + int p1; + int p2; + + if (ly1 == ly2) + { + p1 = by1 > ly1; + p2 = by2 > ly1; + } + else if (lx1 == lx2) + { + p1 = bx2 < lx1; + p2 = bx1 < lx1; + } + else if (((ly2-ly1) ^ (lx2-lx1)) >= 0) + { + p1 = PointOnSide (bx1, by1, lx1, ly1, lx2-lx1, ly2-ly1); + p2 = PointOnSide (bx2, by2, lx1, ly1, lx2-lx1, ly2-ly1); + } + else + { + p1 = PointOnSide (bx2, by1, lx1, ly1, lx2-lx1, ly2-ly1); + p2 = PointOnSide (bx1, by2, lx1, ly1, lx2-lx1, ly2-ly1); + } + + return (p1 == p2) ? 0 : 1; +} +#endif + +WORD *FBlockmapBuilder::GetBlockmap (int &size) +{ +#ifdef BLOCK_TEST + FILE *f = fopen ("blockmap.lmp", "rb"); + if (f) + { + size_t fsize; + fseek (f, 0, SEEK_END); + fsize = ftell (f); + fseek (f, 0, SEEK_SET); + short *stuff = (short *)alloca (fsize); + fread (stuff, 2, fsize/2, f); + fclose (f); + + if ((WORD)stuff[0] != BlockMap[0] || + (WORD)stuff[1] != BlockMap[1] || + (WORD)stuff[2] != BlockMap[2] || + (WORD)stuff[3] != BlockMap[3]) + { + printf ("different blockmap sizes\n"); + goto notest; + } + int i, x, y; + + for (i = 0; i < stuff[2] * stuff[3]; ++i) + { + WORD i1, i2; + i1 = stuff[4+i] + 1; + while (stuff[i1] != -1) + { + i2 = BlockMap[4+i] + 1; + while (BlockMap[i2] != 0xffff) + { + if (BlockMap[i2] == stuff[i1]) + break; + i2++; + } + if (BlockMap[i2] == 0xffff) + { + y = i / stuff[2]; + x = i - y * stuff[2]; + int l = stuff[i1]; + // If a diagonal line passed near a block (within 2 or 4 units, I think), + // it could be considered in the block even if it's really outside it, + // so if things differ, see if DoomBSP was at fault. + if (BoxOnSide ( + stuff[0] + 128*x, stuff[1] + 128*y, + stuff[0] + 128*x + 127, stuff[1] + 128*y + 127, + Vertices[Lines[l].v1].x, Vertices[Lines[l].v1].y, + Vertices[Lines[l].v2].x, Vertices[Lines[l].v2].y)) + { + printf ("not in cell %4d: line %4d [%2d,%2d] : (%5d,%5d)-(%5d,%5d)\n", i, stuff[i1], + x, y, + stuff[0] + 128*x, stuff[1] + 128*y, + stuff[0] + 128*x + 127, stuff[1] + 128*y + 127 + ); + } + } + i1 ++; + } + i1 = BlockMap[4+i] + 1; + while (BlockMap[i1] != 0xffff) + { + i2 = stuff[4+i] + 1; + while (stuff[i2] != -1) + { + if ((WORD)stuff[i2] == BlockMap[i1]) + break; + i2++; + } + if (stuff[i2] == -1) + { + y = i / BlockMap[2]; + x = i - y * BlockMap[2]; + int l = BlockMap[i1]; + if (!BoxOnSide ( + (short)BlockMap[0] + 128*x, (short)BlockMap[1] + 128*y, + (short)BlockMap[0] + 128*x + 127, (short)BlockMap[1] + 128*y + 127, + Vertices[Lines[l].v1].x, Vertices[Lines[l].v1].y, + Vertices[Lines[l].v2].x, Vertices[Lines[l].v2].y)) + { + printf ("EXT in cell %4d: line %4d [%2d,%2d] : (%5d,%5d)-(%5d,%5d)\n", i, (short)BlockMap[i1], + x, y, + (short)BlockMap[0] + 128*x, (short)BlockMap[1] + 128*y, + (short)BlockMap[0] + 128*x + 127, (short)BlockMap[1] + 128*y + 127 + ); + } + } + i1 ++; + } + } + } +notest: +#endif + size = BlockMap.Size(); + return &BlockMap[0]; +} + +void FBlockmapBuilder::BuildBlockmap () +{ + TArray *BlockLists, *block, *endblock; + WORD adder; + int bmapwidth, bmapheight; + int minx, maxx, miny, maxy; + int i; + WORD line; + + if (Level.NumVertices <= 0) + return; + + // Find map extents for the blockmap + minx = maxx = Level.Vertices[0].x; + miny = maxy = Level.Vertices[0].y; + + for (i = 1; i < Level.NumVertices; ++i) + { + if (Level.Vertices[i].x < minx) minx = Level.Vertices[i].x; + else if (Level.Vertices[i].x > maxx) maxx = Level.Vertices[i].x; + if (Level.Vertices[i].y < miny) miny = Level.Vertices[i].y; + else if (Level.Vertices[i].y > maxy) maxy = Level.Vertices[i].y; + } + + maxx >>= FRACBITS; + minx >>= FRACBITS; + maxy >>= FRACBITS; + miny >>= FRACBITS; + +/* + // DoomBSP did this to give the map a margin when drawing it + // in a window on NeXT machines. It's not necessary, but + // it lets me verify my output against DoomBSP's for correctness. + minx -= 8; + miny -= 8; + maxx += 8; + maxy += 8; + + // And DeepBSP seems to do this. + minx &= ~7; + miny &= ~7; +*/ + bmapwidth = ((maxx - minx) >> BLOCKBITS) + 1; + bmapheight = ((maxy - miny) >> BLOCKBITS) + 1; + + adder = WORD(minx); BlockMap.Push (adder); + adder = WORD(miny); BlockMap.Push (adder); + adder = WORD(bmapwidth); BlockMap.Push (adder); + adder = WORD(bmapheight); BlockMap.Push (adder); + + BlockLists = new TArray[bmapwidth * bmapheight]; + + for (line = 0; line < Level.NumLines; ++line) + { + int x1 = Level.Vertices[Level.Lines[line].v1].x >> FRACBITS; + int y1 = Level.Vertices[Level.Lines[line].v1].y >> FRACBITS; + int x2 = Level.Vertices[Level.Lines[line].v2].x >> FRACBITS; + int y2 = Level.Vertices[Level.Lines[line].v2].y >> FRACBITS; + int dx = x2 - x1; + int dy = y2 - y1; + int bx = (x1 - minx) >> BLOCKBITS; + int by = (y1 - miny) >> BLOCKBITS; + int bx2 = (x2 - minx) >> BLOCKBITS; + int by2 = (y2 - miny) >> BLOCKBITS; + + block = &BlockLists[bx + by * bmapwidth]; + endblock = &BlockLists[bx2 + by2 * bmapwidth]; + + if (block == endblock) // Single block + { + block->Push (line); + } + else if (by == by2) // Horizontal line + { + if (bx > bx2) + { + swap (block, endblock); + } + do + { + block->Push (line); + block += 1; + } while (block <= endblock); + } + else if (bx == bx2) // Vertical line + { + if (by > by2) + { + swap (block, endblock); + } + do + { + block->Push (line); + block += bmapwidth; + } while (block <= endblock); + } + else // Diagonal line + { + int xchange = (dx < 0) ? -1 : 1; + int ychange = (dy < 0) ? -1 : 1; + int ymove = ychange * bmapwidth; + int adx = abs (dx); + int ady = abs (dy); + + if (adx == ady) // 45 degrees + { + int xb = (x1 - minx) & (BLOCKSIZE-1); + int yb = (y1 - miny) & (BLOCKSIZE-1); + if (dx < 0) + { + xb = BLOCKSIZE-xb; + } + if (dy < 0) + { + yb = BLOCKSIZE-yb; + } + if (xb < yb) + adx--; + } + if (adx >= ady) // X-major + { + int yadd = dy < 0 ? -1 : BLOCKSIZE; + do + { + int stop = (Scale ((by << BLOCKBITS) + yadd - (y1 - miny), dx, dy) + (x1 - minx)) >> BLOCKBITS; + while (bx != stop) + { + block->Push (line); + block += xchange; + bx += xchange; + } + block->Push (line); + block += ymove; + by += ychange; + } while (by != by2); + while (block != endblock) + { + block->Push (line); + block += xchange; + } + block->Push (line); + } + else // Y-major + { + int xadd = dx < 0 ? -1 : BLOCKSIZE; + do + { + int stop = (Scale ((bx << BLOCKBITS) + xadd - (x1 - minx), dy, dx) + (y1 - miny)) >> BLOCKBITS; + while (by != stop) + { + block->Push (line); + block += ymove; + by += ychange; + } + block->Push (line); + block += xchange; + bx += xchange; + } while (bx != bx2); + while (block != endblock) + { + block->Push (line); + block += ymove; + } + block->Push (line); + } + } + } + + BlockMap.Reserve (bmapwidth * bmapheight); + CreatePackedBlockmap (BlockLists, bmapwidth, bmapheight); + delete[] BlockLists; +} + +void FBlockmapBuilder::CreateUnpackedBlockmap (TArray *blocks, int bmapwidth, int bmapheight) +{ + TArray *block; + WORD zero = 0; + WORD terminator = 0xffff; + + for (int i = 0; i < bmapwidth * bmapheight; ++i) + { + BlockMap[4+i] = WORD(BlockMap.Size()); + BlockMap.Push (zero); + block = &blocks[i]; + for (size_t j = 0; j < block->Size(); ++j) + { + BlockMap.Push ((*block)[j]); + } + BlockMap.Push (terminator); + } +} + +static unsigned int BlockHash (TArray *block) +{ + int hash = 0; + WORD *ar = &(*block)[0]; + for (size_t i = 0; i < block->Size(); ++i) + { + hash = hash * 12235 + ar[i]; + } + return hash & 0x7fffffff; +} + +static bool BlockCompare (TArray *block1, TArray *block2) +{ + size_t size = block1->Size(); + + if (size != block2->Size()) + { + return false; + } + if (size == 0) + { + return true; + } + WORD *ar1 = &(*block1)[0]; + WORD *ar2 = &(*block2)[0]; + for (size_t i = 0; i < size; ++i) + { + if (ar1[i] != ar2[i]) + { + return false; + } + } + return true; +} + +void FBlockmapBuilder::CreatePackedBlockmap (TArray *blocks, int bmapwidth, int bmapheight) +{ + WORD buckets[4096]; + WORD *hashes, hashblock; + TArray *block; + WORD zero = 0; + WORD terminator = 0xffff; + WORD *array; + int i, hash; + int hashed = 0, nothashed = 0; + + hashes = new WORD[bmapwidth * bmapheight]; + + memset (hashes, 0xff, sizeof(WORD)*bmapwidth*bmapheight); + memset (buckets, 0xff, sizeof(buckets)); + + for (i = 0; i < bmapwidth * bmapheight; ++i) + { + block = &blocks[i]; + hash = BlockHash (block) % 4096; + hashblock = buckets[hash]; + while (hashblock != 0xffff) + { + if (BlockCompare (block, &blocks[hashblock])) + { + break; + } + hashblock = hashes[hashblock]; + } + if (hashblock != 0xffff) + { + BlockMap[4+i] = BlockMap[4+hashblock]; + hashed++; + } + else + { + hashes[i] = buckets[hash]; + buckets[hash] = WORD(i); + BlockMap[4+i] = WORD(BlockMap.Size()); + BlockMap.Push (zero); + array = &(*block)[0]; + for (size_t j = 0; j < block->Size(); ++j) + { + BlockMap.Push (array[j]); + } + BlockMap.Push (terminator); + nothashed++; + } + } + + delete[] hashes; + +// printf ("%d blocks written, %d blocks saved\n", nothashed, hashed); +} + diff --git a/blockmapbuilder.h b/blockmapbuilder.h new file mode 100644 index 0000000..71c7478 --- /dev/null +++ b/blockmapbuilder.h @@ -0,0 +1,18 @@ +#include "doomdata.h" +#include "workdata.h" +#include "tarray.h" + +class FBlockmapBuilder +{ +public: + FBlockmapBuilder (FLevel &level); + WORD *GetBlockmap (int &size); + +private: + FLevel &Level; + TArray BlockMap; + + void BuildBlockmap (); + void CreateUnpackedBlockmap (TArray *blocks, int bmapwidth, int bmapheight); + void CreatePackedBlockmap (TArray *blocks, int bmapwidth, int bmapheight); +}; diff --git a/doomdata.h b/doomdata.h new file mode 100644 index 0000000..0d7a4f9 --- /dev/null +++ b/doomdata.h @@ -0,0 +1,177 @@ +#ifndef __DOOMDATA_H__ +#define __DOOMDATA_H__ + +#ifdef _MSC_VER +#pragma once +#endif + +enum +{ + BOXTOP, BOXBOTTOM, BOXLEFT, BOXRIGHT +}; + +struct MapVertex +{ + short x, y; +}; + +struct WideVertex +{ + fixed_t x, y; +}; + +struct MapSideDef +{ + short textureoffset; + short rowoffset; + char toptexture[8]; + char bottomtexture[8]; + char midtexture[8]; + short sector; +}; + +struct MapLineDef +{ + WORD v1; + WORD v2; + short flags; + short special; + short tag; + WORD sidenum[2]; +}; + +struct MapLineDef2 +{ + WORD v1; + WORD v2; + short flags; + unsigned char special; + unsigned char args[5]; + WORD sidenum[2]; +}; + +struct MapSector +{ + short floorheight; + short ceilingheight; + char floorpic[8]; + char ceilingpic[8]; + short lightlevel; + short special; + short tag; +}; + +struct MapSubsector +{ + WORD numlines; + WORD firstline; +}; + +struct MapSubsectorEx +{ + DWORD numlines; + DWORD firstline; +}; + +struct MapSeg +{ + WORD v1; + WORD v2; + WORD angle; + WORD linedef; + short side; + short offset; +}; + +struct MapSegGL +{ + WORD v1; + WORD v2; + WORD linedef; + WORD side; + WORD partner; +}; + +struct MapSegGLEx +{ + WORD v1; + WORD v2; + WORD linedef; + WORD side; + DWORD partner; +}; + +#define NF_SUBSECTOR 0x8000 +#define NFX_SUBSECTOR 0x80000000 + +struct MapNode +{ + short x,y,dx,dy; + short bbox[2][4]; + WORD children[2]; +}; + +struct MapNodeEx +{ + short x,y,dx,dy; + short bbox[2][4]; + DWORD children[2]; +}; + +struct MapThing +{ + short x; + short y; + short angle; + short type; + short flags; +}; + +struct MapThing2 +{ + unsigned short thingid; + short x; + short y; + short z; + short angle; + short type; + short flags; + char special; + char args[5]; +}; + +struct FLevel +{ + FLevel (); + ~FLevel (); + + WideVertex *Vertices; int NumVertices; + MapSideDef *Sides; int NumSides; + MapLineDef2 *Lines; int NumLines; + MapSector *Sectors; int NumSectors; + MapSubsectorEx *Subsectors; int NumSubsectors; + MapSeg *Segs; int NumSegs; + MapNodeEx *Nodes; int NumNodes; + MapThing2 *Things; int NumThings; + WORD *Blockmap; int BlockmapSize; + BYTE *Reject; int RejectSize; + + MapSubsectorEx *GLSubsectors; int NumGLSubsectors; + MapSegGLEx *GLSegs; int NumGLSegs; + MapNodeEx *GLNodes; int NumGLNodes; + WideVertex *GLVertices; int NumGLVertices; + BYTE *GLPVS; int GLPVSSize; + + int NumOrgVerts; + + void RemoveExtraLines (); + void RemoveExtraSides (); + void RemoveExtraSectors (); +}; + +const int BLOCKSIZE = 128; +const int BLOCKFRACSIZE = BLOCKSIZE<. + Ditto for AIX 3.2 and . */ +#ifndef _NO_PROTO +#define _NO_PROTO +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + +#if !defined (__STDC__) || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2 +#include +#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +#define ELIDE_CODE +#endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +#include +#include +#endif /* GNU C library. */ + +#ifdef VMS +#include +#if HAVE_STRING_H - 0 +#include +#endif +#endif + +#if defined (WIN32) && !defined (__CYGWIN32__) +/* It's not Unix, really. See? Capital letters. */ +#define WIN32_LEAN_AND_MEAN +#include +#define getpid() GetCurrentProcessId() +#endif + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. + When compiling libc, the _ macro is predefined. */ +#ifdef HAVE_LIBINTL_H +# include +# define _(msgid) gettext (msgid) +#else +# define _(msgid) (msgid) +#endif +#endif + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = NULL; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* Formerly, initialization of getopt depended on optind==0, which + causes problems with re-calling getopt as programs generally don't + know that. */ + +int __getopt_initialized = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return -1 with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +#include +#define my_index strchr +#else + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +char *getenv (); + +static char *my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. + If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. + That was relevant to code that was here before. */ +#if !defined (__STDC__) || !__STDC__ +/* gcc with -traditional declares the built-in strlen to return int, + and has done so at least since version 2.4.5. -- rms. */ +extern int strlen (const char *); +#endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +#ifdef _LIBC +/* Bash 2.0 gives us an environment variable containing flags + indicating ARGV elements that should not be considered arguments. */ + +/* Defined in getopt_init.c */ +extern char *__getopt_nonoption_flags; + +static int nonoption_flags_max_len; +static int nonoption_flags_len; + +static int original_argc; +static char *const *original_argv; + +extern pid_t __libc_pid; + +/* Make sure the environment variable bash 2.0 puts in the environment + is valid for the getopt call we must make sure that the ARGV passed + to getopt is that one passed to the process. */ +static void +__attribute__ ((unused)) +store_args_and_env (int argc, char *const *argv) +{ + /* XXX This is no good solution. We should rather copy the args so + that we can compare them later. But we must not use malloc(3). */ + original_argc = argc; + original_argv = argv; +} +text_set_element (__libc_subinit, store_args_and_env); + +# define SWAP_FLAGS(ch1, ch2) \ + if (nonoption_flags_len > 0) \ + { \ + char __tmp = __getopt_nonoption_flags[ch1]; \ + __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ + __getopt_nonoption_flags[ch2] = __tmp; \ + } +#else /* !_LIBC */ +# define SWAP_FLAGS(ch1, ch2) +#endif /* _LIBC */ + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +#if defined (__STDC__) && __STDC__ +static void exchange (char **); +#endif + +static void +exchange (argv) + char **argv; +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + +#ifdef _LIBC + /* First make sure the handling of the `__getopt_nonoption_flags' + string can work normally. Our top argument must be in the range + of the string. */ + if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) + { + /* We must extend the array. The user plays games with us and + presents new arguments. */ + char *new_str = malloc (top + 1); + if (new_str == NULL) + nonoption_flags_len = nonoption_flags_max_len = 0; + else + { + memcpy (new_str, __getopt_nonoption_flags, nonoption_flags_max_len); + memset (&new_str[nonoption_flags_max_len], '\0', + top + 1 - nonoption_flags_max_len); + nonoption_flags_max_len = top + 1; + __getopt_nonoption_flags = new_str; + } + } +#endif + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS (bottom + i, middle + i); + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +#if defined (__STDC__) && __STDC__ +static const char *_getopt_initialize (int, char *const *, const char *); +#endif +static const char * +_getopt_initialize (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + +#ifdef _LIBC + if (posixly_correct == NULL + && argc == original_argc && argv == original_argv) + { + if (nonoption_flags_max_len == 0) + { + if (__getopt_nonoption_flags == NULL + || __getopt_nonoption_flags[0] == '\0') + nonoption_flags_max_len = -1; + else + { + const char *orig_str = __getopt_nonoption_flags; + int len = nonoption_flags_max_len = strlen (orig_str); + if (nonoption_flags_max_len < argc) + nonoption_flags_max_len = argc; + __getopt_nonoption_flags = + (char *) malloc (nonoption_flags_max_len); + if (__getopt_nonoption_flags == NULL) + nonoption_flags_max_len = -1; + else + { + memcpy (__getopt_nonoption_flags, orig_str, len); + memset (&__getopt_nonoption_flags[len], '\0', + nonoption_flags_max_len - len); + } + } + } + nonoption_flags_len = nonoption_flags_max_len; + } + else + nonoption_flags_len = 0; +#endif + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns -1. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + optarg = NULL; + + if (optind == 0 || !__getopt_initialized) + { + if (optind == 0) + optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize (argc, argv, optstring); + __getopt_initialized = 1; + } + + /* Test whether ARGV[optind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + is only used when the used in the GNU libc. */ +#ifdef _LIBC +#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ + || (optind < nonoption_flags_len \ + && __getopt_nonoption_flags[optind] == '1')) +#else +#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') +#endif + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (last_nonopt > optind) + last_nonopt = optind; + if (first_nonopt > optind) + first_nonopt = optind; + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc && NONOPTION_P) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) + { + if (ordering == REQUIRE_ORDER) + return -1; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) + == (unsigned int) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + optopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + _("%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + _("%s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[optind - 1][0], pfound->name); + + nextchar += strlen (nextchar); + + optopt = pfound->val; + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, _("%s: unrecognized option `--%s'\n"), + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: illegal option -- %c\n"), + argv[0], c); + else + fprintf (stderr, _("%s: invalid option -- %c\n"), + argv[0], c); + } + optopt = c; + return '?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + + /* optarg is now the argument, see if it's in the + table of longopts. */ + + for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + fprintf (stderr, _("\ +%s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); + + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, + _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == -1) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/getopt.h b/getopt.h new file mode 100644 index 0000000..e2cdb12 --- /dev/null +++ b/getopt.h @@ -0,0 +1,133 @@ +/* Declarations for getopt. + Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc. + +NOTE: The canonical source of this file is maintained with the GNU C Library. +Bugs can be reported to bug-glibc@prep.ai.mit.edu. + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +USA. */ + +#ifndef _GETOPT_H +#define _GETOPT_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ +#if defined (__STDC__) && __STDC__ + const char *name; +#else + char *name; +#endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +#if (defined (__STDC__) && __STDC__) || (defined (__cplusplus)) +#ifdef __GNU_LIBRARY__ +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int getopt (int argc, char *const *argv, const char *shortopts); +#else /* not __GNU_LIBRARY__ */ +extern int getopt (); +#endif /* __GNU_LIBRARY__ */ +extern int getopt_long (int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind); +extern int getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind); + +/* Internal only. Users should not call this directly. */ +extern int _getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only); +#else /* not __STDC__ */ +extern int getopt (); +extern int getopt_long (); +extern int getopt_long_only (); + +extern int _getopt_internal (); +#endif /* __STDC__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* _GETOPT_H */ diff --git a/getopt1.c b/getopt1.c new file mode 100644 index 0000000..5133869 --- /dev/null +++ b/getopt1.c @@ -0,0 +1,189 @@ +/* getopt_long and getopt_long_only entry points for GNU getopt. + Copyright (C) 1987,88,89,90,91,92,93,94,96,97 Free Software Foundation, Inc. + +NOTE: The canonical source of this file is maintained with the GNU C Library. +Bugs can be reported to bug-glibc@prep.ai.mit.edu. + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "getopt.h" + +#if !defined (__STDC__) || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2 +#include +#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +#define ELIDE_CODE +#endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#include +#endif + +#ifndef NULL +#define NULL 0 +#endif + +int +getopt_long (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 1); +} + + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +#include + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = + { + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == -1) + break; + + switch (c) + { + case 0: + printf ("option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case 'd': + printf ("option d with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..5aab678 --- /dev/null +++ b/main.cpp @@ -0,0 +1,524 @@ +/* + The main glue for ZDBSP. + Copyright (C) 2002,2003 Randy Heit + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +// HEADER FILES ------------------------------------------------------------ + +#ifdef _WIN32 + +// Need windows.h for QueryPerformanceCounter +#define WIN32_LEAN_AND_MEAN +#include + +#define HAVE_TIMING 1 +#define START_COUNTER(s,e,f) \ + LARGE_INTEGER s, e, f; QueryPerformanceCounter (&s); +#define END_COUNTER(s,e,f,l) \ + QueryPerformanceCounter (&e); QueryPerformanceFrequency (&f); \ + if (!NoTiming) printf (l, double(e.QuadPart - s.QuadPart) / double(f.QuadPart)); + +#else + +#define HAVE_TIMING 0 +#define START_COUNTER(s,e,f) +#define END_COUNTER(s,e,f) + +// Need these to check if input/output are the same file +#include +#include + +#endif + +#include +#include +#include +#include +#include + +#include "zdbsp.h" +#include "wad.h" +#include "processor.h" +#include "getopt.h" + +// MACROS ------------------------------------------------------------------ + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static void ParseArgs (int argc, char **argv); +static void ShowUsage (); +static void ShowVersion (); +static bool CheckInOutNames (); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +extern "C" int optind; +extern "C" char *optarg; + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +const char *Map = NULL; +const char *InName; +const char *OutName = "tmp.wad"; +bool BuildNodes = true; +bool BuildGLNodes = false; +bool ConformNodes = false; +bool NoPrune = false; +EBlockmapMode BlockmapMode = EBM_Rebuild; +ERejectMode RejectMode = ERM_DontTouch; +int MaxSegs = 64; +int SplitCost = 8; +int AAPreference = 16; +bool CheckPolyobjs = true; +bool ShowMap = false; +bool ShowWarnings = false; +bool NoTiming = false; +bool CompressNodes = false; +bool CompressGLNodes = false; +bool GLOnly = false; + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static option long_opts[] = +{ + {"help", no_argument, 0, 1000}, + {"version", no_argument, 0, 'V'}, + {"view", no_argument, 0, 'v'}, + {"warn", no_argument, 0, 'w'}, + {"map", required_argument, 0, 'm'}, + {"output", required_argument, 0, 'o'}, + {"output-file", required_argument, 0, 'o'}, + {"file", required_argument, 0, 'f'}, + {"no-nodes", no_argument, 0, 'N'}, + {"gl", no_argument, 0, 'g'}, + {"gl-matching", no_argument, 0, 'G'}, + {"empty-blockmap", no_argument, 0, 'b'}, + {"empty-reject", no_argument, 0, 'r'}, + {"zero-reject", no_argument, 0, 'R'}, + {"full-reject", no_argument, 0, 'e'}, + {"no-reject", no_argument, 0, 'E'}, + {"partition", required_argument, 0, 'p'}, + {"split-cost", required_argument, 0, 's'}, + {"diagonal-cost", required_argument, 0, 'd'}, + {"no-polyobjs", no_argument, 0, 'P'}, + {"no-prune", no_argument, 0, 'q'}, + {"no-timing", no_argument, 0, 't'}, + {"compress", no_argument, 0, 'z'}, + {"compress-normal", no_argument, 0, 'Z'}, + {"gl-only", no_argument, 0, 'x'}, + {0,0,0,0} +}; + +static const char short_opts[] = "wVgGvbNrReEm:o:f:p:s:d:PqtzZx"; + +// CODE -------------------------------------------------------------------- + +int main (int argc, char **argv) +{ + bool fixSame = false; + + ParseArgs (argc, argv); + + if (InName == NULL) + { + if (optind >= argc || optind < argc-1) + { // Source file is unspecified or followed by junk + ShowUsage (); + return 0; + } + + InName = argv[optind]; + } + + try + { + START_COUNTER(t1a, t1b, t1c) + + if (CheckInOutNames ()) + { + // When the input and output files are the same, output will go to + // a temporary file. After everything is done, the input file is + // deleted and the output file is renamed to match the input file. + + char *out = new char[strlen(OutName)+3], *dot; + + if (out == NULL) + { + throw exception("Could not create temporary file name."); + } + + strcpy (out, OutName); + dot = strrchr (out, '.'); + if (dot && (dot[1] == 'w' || dot[1] == 'W') + && (dot[2] == 'a' || dot[2] == 'A') + && (dot[3] == 'd' || dot[3] == 'D') + && dot[4] == 0) + { + // *.wad becomes *.daw + dot[1] = 'd'; + dot[3] = 'w'; + } + else + { + // * becomes *.x + strcat (out, ".x"); + } + OutName = out; + fixSame = true; + } + + { + FWadReader inwad (InName); + FWadWriter outwad (OutName, inwad.IsIWAD()); + + int lump = 0; + int max = inwad.NumLumps (); + + while (lump < max) + { + if (inwad.IsMap (lump) && + (!Map || stricmp (inwad.LumpName (lump), Map) == 0)) + { + START_COUNTER(t2a, t2b, t2c) + FProcessor builder (inwad, lump); + builder.Write (outwad); + END_COUNTER(t2a, t2b, t2c, " %g seconds.\n") + + lump = inwad.LumpAfterMap (lump); + } + else if (inwad.IsGLNodes (lump)) + { + // Ignore GL nodes from the input for any maps we process. + if (BuildNodes && (Map == NULL || stricmp (inwad.LumpName (lump)+3, Map) == 0)) + { + lump = inwad.SkipGLNodes (lump); + } + else + { + outwad.CopyLump (inwad, lump); + ++lump; + } + } + else + { + //printf ("copy %s\n", inwad.LumpName (lump)); + outwad.CopyLump (inwad, lump); + ++lump; + } + } + + outwad.Close (); + } + + if (fixSame) + { + remove (InName); + if (0 != rename (OutName, InName)) + { + printf ("The output file could not be renamed to %s.\nYou can find it as %s.\n", + InName, OutName); + } + } + + END_COUNTER(t1a, t1b, t1c, "\nTotal time: %g seconds.\n") + } + catch (exception msg) + { + printf ("%s\n", msg.what()); + return 20; + } + + return 0; +} + +//========================================================================== +// +// ParseArgs +// +//========================================================================== + +static void ParseArgs (int argc, char **argv) +{ + int ch; + + while ((ch = getopt_long (argc, argv, short_opts, long_opts, NULL)) != EOF) + { + switch (ch) + { + case 0: + break; + + case 'v': + ShowMap = true; + break; + case 'w': + ShowWarnings = true; + break; + case 'm': + Map = optarg; + break; + case 'o': + OutName = optarg; + break; + case 'f': + InName = optarg; + break; + case 'N': + BuildNodes = false; + break; + case 'b': + BlockmapMode = EBM_Create0; + break; + case 'r': + RejectMode = ERM_Create0; + break; + case 'R': + RejectMode = ERM_CreateZeroes; + break; + case 'e': + RejectMode = ERM_Rebuild; + break; + case 'E': + RejectMode = ERM_DontTouch; + break; + case 'p': + MaxSegs = atoi (optarg); + if (MaxSegs < 3) + { // Don't be too unreasonable + MaxSegs = 3; + } + break; + case 's': + SplitCost = atoi (optarg); + if (SplitCost < 1) + { // 1 means to add no extra weight at all + SplitCost = 1; + } + break; + case 'd': + AAPreference = atoi (optarg); + if (AAPreference < 1) + { + AAPreference = 1; + } + break; + case 'P': + CheckPolyobjs = false; + break; + case 'g': + BuildGLNodes = true; + ConformNodes = false; + break; + case 'G': + BuildGLNodes = true; + ConformNodes = true; + break; + case 'z': + CompressNodes = true; + CompressGLNodes = true; + break; + case 'Z': + CompressNodes = true; + CompressGLNodes = false; + break; + case 'x': + GLOnly = true; + BuildGLNodes = true; + ConformNodes = false; + break; + case 'q': + NoPrune = true; + break; + case 't': + NoTiming = true; + break; + case 'V': + ShowVersion (); + exit (0); + break; + case 1000: + ShowUsage (); + exit (0); + default: + printf ("Try `zdbsp --help' for more information.\n"); + exit (0); + } + } +} + +//========================================================================== +// +// ShowUsage +// +//========================================================================== + +static void ShowUsage () +{ + printf ( +"Usage: zdbsp [options] sourcefile.wad\n" +" -m, --map=MAP Only affect the specified map\n" +" -o, --output=FILE Write output to FILE instead of tmp.wad\n" +" -q, --no-prune Keep unused sidedefs and sectors\n" +" -N, --no-nodes Do not rebuild nodes\n" +" -g, --gl Build GL-friendly nodes\n" +" -G, --gl-matching Build GL-friendly nodes that match normal nodes\n" +" -x, --gl-only Only build GL-friendly nodes\n" +" -b, --empty-blockmap Create an empty blockmap\n" +" -r, --empty-reject Create an empty reject table\n" +" -R, --zero-reject Create a reject table of all zeroes\n" +//" -e, --full-reject Rebuild reject table (unsupported)\n" +" -E, --no-reject Leave reject table untouched\n" +" -p, --partition=NNN Maximum number of segs to consider at each node\n"// (default 64)\n" +" -s, --split-cost=NNN Adjusts the cost for splitting segs\n"// (default 8)\n" +" -d, --diagonal-cost=NNN Adjusts the cost for avoiding diagonal splitters\n"// (default 16)\n" +" -P, --no-polyobjs Do not check for polyobject subsector splits\n" +" -z, --compress Compress the nodes (including GL nodes, if created)\n" +" -Z, --compress-normal Compress normal nodes but not GL nodes\n" +#ifdef _WIN32 +" -v, --view View the nodes\n" +#endif +" -w, --warn Show warning messages\n" +#if HAVE_TIMING +" -t, --no-timing Suppress timing information\n" +#endif +" -V, --version Display version information\n" +" --help Display this usage information\n" + ); +} + +//========================================================================== +// +// ShowVersion +// +//========================================================================== + +static void ShowVersion () +{ + printf ("ZDBSP " ZDBSP_VERSION "\n"); +} + +//========================================================================== +// +// CheckInOutNames +// +// Returns true if InName and OutName refer to the same file. This needs +// to be implemented different under Windows than Unix because the inode +// information returned by stat is always 0, so it cannot be used to +// determine duplicate files. +// +//========================================================================== + +static bool CheckInOutNames () +{ +#ifndef _WIN32 + struct stat info; + dev_t outdev; + ino_t outinode; + + if (0 != stat (OutName, &info)) + { // If out doesn't exist, it can't be duplicated + return false; + } + outdev = info.st_dev; + outinode = info.st_ino; + if (0 != stat (InName, &info)) + { + return false; + } + return outinode == info.st_ino && outdev == info.st_dev; +#else + HANDLE inFile, outFile; + + outFile = CreateFile (OutName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); + if (outFile == INVALID_HANDLE_VALUE) + { + return false; + } + inFile = CreateFile (InName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); + if (inFile == INVALID_HANDLE_VALUE) + { + CloseHandle (outFile); + return false; + } + + BY_HANDLE_FILE_INFORMATION inInfo, outInfo; + bool same = false; + + if (GetFileInformationByHandle (inFile, &inInfo) && + GetFileInformationByHandle (outFile, &outInfo)) + { + same = inInfo.dwVolumeSerialNumber == outInfo.dwVolumeSerialNumber && + inInfo.nFileIndexLow == outInfo.nFileIndexLow && + inInfo.nFileIndexHigh == outInfo.nFileIndexHigh; + } + + CloseHandle (inFile); + CloseHandle (outFile); + + return same; +#endif +} + +//========================================================================== +// +// PointToAngle +// +//========================================================================== + +angle_t PointToAngle (fixed_t x, fixed_t y) +{ + const double rad2bam = double(1<<30) / M_PI; + double ang = atan2 (double(y), double(x)); + if (ang < 0.0) + { + ang = 2*M_PI+ang; + } + return angle_t(ang * rad2bam) << 1; +} + +//========================================================================== +// +// Warn +// +//========================================================================== + +void Warn (const char *format, ...) +{ + va_list marker; + + if (!ShowWarnings) + { + return; + } + + va_start (marker, format); + vprintf (format, marker); + va_end (marker); +} diff --git a/nodebuild.cpp b/nodebuild.cpp new file mode 100644 index 0000000..e638941 --- /dev/null +++ b/nodebuild.cpp @@ -0,0 +1,1108 @@ +/* + Most of the logic for the node builder. + Copyright (C) 2002,2003 Randy Heit + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include +#include + +#include "zdbsp.h" +#include "nodebuild.h" +#include "templates.h" + +// Points within this distance of a line will be considered on the line. +// Units are in fixed_ts. +const double SIDE_EPSILON = 6.5536; + +// Vertices within this distance of each other vertically and horizontally +// will be considered as the same vertex. +const fixed_t VERTEX_EPSILON = 6; + +#define Printf printf +#define STACK_ARGS + +#if 0 +#define D(x) x +#else +#define D(x) do{}while(0) +#endif + +FNodeBuilder::FNodeBuilder (FLevel &level, + TArray &polyspots, TArray &anchors, + const char *name, bool makeGLnodes) + : Level (level), SegsStuffed (0), MapName (name) +{ + GLNodes = makeGLnodes; + FindUsedVertices (Level.Vertices, Level.NumVertices); + MakeSegsFromSides (); + FindPolyContainers (polyspots, anchors); + GroupSegPlanes (); + BuildTree (); +} + +void FNodeBuilder::BuildTree () +{ + fixed_t bbox[4]; + + fprintf (stderr, " BSP: 0.0%%\r"); + HackSeg = DWORD_MAX; + CreateNode (0, bbox); + CreateSubsectorsForReal (); + fprintf (stderr, " BSP: 100.0%%\n"); +} + +DWORD FNodeBuilder::CreateNode (DWORD set, fixed_t bbox[4]) +{ + node_t node; + int skip, count, selstat; + DWORD splitseg; + + count = CountSegs (set); + skip = count / MaxSegs; + + if ((selstat = SelectSplitter (set, node, splitseg, skip, true)) > 0 || + (skip > 0 && (selstat = SelectSplitter (set, node, splitseg, 1, true)) > 0) || + (selstat < 0 && (SelectSplitter (set, node, splitseg, skip, false) > 0) || + (skip > 0 && SelectSplitter (set, node, splitseg, 1, false))) || + CheckSubsector (set, node, splitseg, count)) + { + // Create a normal node + DWORD set1, set2; + + SplitSegs (set, node, splitseg, set1, set2); + D(PrintSet (1, set1)); + D(Printf ("(%d,%d) delta (%d,%d) from seg %d\n", node.x>>16, node.y>>16, node.dx>>16, node.dy>>16, splitseg)); + D(PrintSet (2, set2)); + node.intchildren[0] = CreateNode (set1, node.bbox[0]); + node.intchildren[1] = CreateNode (set2, node.bbox[1]); + bbox[BOXTOP] = MAX (node.bbox[0][BOXTOP], node.bbox[1][BOXTOP]); + bbox[BOXBOTTOM] = MIN (node.bbox[0][BOXBOTTOM], node.bbox[1][BOXBOTTOM]); + bbox[BOXLEFT] = MIN (node.bbox[0][BOXLEFT], node.bbox[1][BOXLEFT]); + bbox[BOXRIGHT] = MAX (node.bbox[0][BOXRIGHT], node.bbox[1][BOXRIGHT]); + return (int)Nodes.Push (node); + } + else + { + return NFX_SUBSECTOR | CreateSubsector (set, bbox); + } +} + +DWORD FNodeBuilder::CreateSubsector (DWORD set, fixed_t bbox[4]) +{ + int ssnum, count; + + bbox[BOXTOP] = bbox[BOXRIGHT] = INT_MIN; + bbox[BOXBOTTOM] = bbox[BOXLEFT] = INT_MAX; + + D(Printf ("Subsector from set %d\n", set)); + + assert (set != DWORD_MAX); + +#if defined(_DEBUG) || 1 + // Check for segs with duplicate start/end vertices + DWORD s1, s2; + + for (s1 = set; s1 != DWORD_MAX; s1 = Segs[s1].next) + { + for (s2 = Segs[s1].next; s2 != DWORD_MAX; s2 = Segs[s2].next) + { + if (Segs[s1].v1 == Segs[s2].v1) + printf ("Segs %d%c and %d%c have duplicate start vertex %d (%d, %d)\n", + s1, Segs[s1].linedef == -1 ? '*' : ' ', + s2, Segs[s2].linedef == -1 ? '*' : ' ', + Segs[s1].v1, + Vertices[Segs[s1].v1].x >> 16, Vertices[Segs[s1].v1].y >> 16); + if (Segs[s1].v2 == Segs[s2].v2) + printf ("Segs %d%c and %d%c have duplicate end vertex %d (%d, %d)\n", + s1, Segs[s1].linedef == -1 ? '*' : ' ', + s2, Segs[s2].linedef == -1 ? '*' : ' ', + Segs[s1].v2, + Vertices[Segs[s1].v2].x >> 16, Vertices[Segs[s1].v2].y >> 16); + } + } +#endif + + // We cannot actually create the subsector now because the node building + // process might split a seg in this subsector (because all partner segs + // must use the same pair of vertices), adding a new seg that hasn't been + // created yet. After all the nodes are built, then we can create the + // actual subsectors using the CreateSubsectorsForReal function below. + ssnum = (int)SubsectorSets.Push (set); + + count = 0; + while (set != DWORD_MAX) + { + AddSegToBBox (bbox, &Segs[set]); + set = Segs[set].next; + count++; + } + + SegsStuffed += count; + if ((SegsStuffed & ~63) != ((SegsStuffed - count) & ~63)) + { + int percent = (int)(SegsStuffed * 1000.0 / Segs.Size()); + fprintf (stderr, " BSP: %3d.%d%%\r", percent/10, percent%10); + } + + D(Printf ("bbox (%d,%d)-(%d,%d)\n", bbox[BOXLEFT]>>16, bbox[BOXBOTTOM]>>16, bbox[BOXRIGHT]>>16, bbox[BOXTOP]>>16)); + + return ssnum; +} + +void FNodeBuilder::CreateSubsectorsForReal () +{ + size_t i; + + for (i = 0; i < SubsectorSets.Size(); ++i) + { + subsector_t sub; + DWORD set = SubsectorSets[i]; + + sub.firstline = (DWORD)SegList.Size(); + while (set != DWORD_MAX) + { + USegPtr ptr; + + ptr.SegPtr = &Segs[set]; + SegList.Push (ptr); + set = ptr.SegPtr->next; + } + sub.numlines = (DWORD)(SegList.Size() - sub.firstline); + + // Sort segs by linedef for special effects + qsort (&SegList[sub.firstline], sub.numlines, sizeof(int), SortSegs); + + // Convert seg pointers into indices + for (size_t i = sub.firstline; i < SegList.Size(); ++i) + { + SegList[i].SegNum = SegList[i].SegPtr - &Segs[0]; + } + Subsectors.Push (sub); + } +} + +int STACK_ARGS FNodeBuilder::SortSegs (const void *a, const void *b) +{ + const FPrivSeg *x = ((const USegPtr *)a)->SegPtr; + const FPrivSeg *y = ((const USegPtr *)b)->SegPtr; + + // Segs are grouped into three categories in this order: + // + // 1. Segs with different front and back sectors (or no back at all). + // 2. Segs with the same front and back sectors. + // 3. Minisegs. + // + // Within the first two sets, segs are also sorted by linedef. + // + // Note that when GL subsectors are written, the segs will be reordered + // so that they are in clockwise order, and extra minisegs will be added + // as needed to close the subsector. But the first seg used will still be + // the first seg chosen here. + + int xtype, ytype; + + if (x->linedef == -1) + { + xtype = 2; + } + else if (x->frontsector == x->backsector) + { + xtype = 1; + } + else + { + xtype = 0; + } + + if (y->linedef == -1) + { + ytype = 2; + } + else if (y->frontsector == y->backsector) + { + ytype = 1; + } + else + { + ytype = 0; + } + + if (xtype != ytype) + { + return xtype - ytype; + } + else if (xtype < 2) + { + return x->linedef - y->linedef; + } + else + { + return 0; + } +} + +int FNodeBuilder::CountSegs (DWORD set) const +{ + int count = 0; + + while (set != DWORD_MAX) + { + count++; + set = Segs[set].next; + } + return count; +} + +// Given a set of segs, checks to make sure they all belong to a single +// sector. If so, false is returned, and they become a subsector. If not, +// a splitter is synthesized, and true is returned to continue processing +// down this branch of the tree. + +bool FNodeBuilder::CheckSubsector (DWORD set, node_t &node, DWORD &splitseg, int setsize) +{ + int sec; + DWORD seg; + + sec = -1; + seg = set; + + do + { + D(Printf (" - seg %d(%d,%d)-(%d,%d) line %d front %d back %d\n", seg, + Vertices[Segs[seg].v1].x>>16, Vertices[Segs[seg].v1].y>>16, + Vertices[Segs[seg].v2].x>>16, Vertices[Segs[seg].v2].y>>16, + Segs[seg].linedef, Segs[seg].frontsector, Segs[seg].backsector)); + if (Segs[seg].linedef != -1 && + Segs[seg].frontsector != sec + // Segs with the same front and back sectors are allowed to reside + // in a subsector with segs from a different sector, because the + // only effect they can have on the display is to place masked + // mid textures in the scene. Since minisegs only mark subsector + // boundaries, their sector information is unimportant. + // + // Update: Lines with the same front and back sectors *can* affect + // the display if their subsector does not match their front sector. + /*&& Segs[seg].frontsector != Segs[seg].backsector*/) + { + if (sec == -1) + { + sec = Segs[seg].frontsector; + } + else + { + break; + } + } + seg = Segs[seg].next; + } while (seg != DWORD_MAX); + + if (seg == DWORD_MAX) + { // It's a valid subsector + return false; + } + + D(Printf("Need to synthesize a splitter for set %d on seg %d\n", set, seg)); + splitseg = DWORD_MAX; + + // This is a very simple and cheap "fix" for subsectors with segs + // from multiple sectors, and it seems ZenNode does something + // similar. It is the only technique I could find that makes the + // "transparent water" in nb_bmtrk.wad work properly. + // + // The seg is marked to indicate that it should be forced to the + // back of the splitter. Because these segs already form a convex + // set, all the other segs will be in front of the splitter. Since + // the splitter is formed from this seg, the back of the splitter + // will have a one-dimensional subsector. SplitSegs() will add two + // new minisegs to close it: one seg replaces this one on the front + // of the splitter, and the other is its partner on the back. + // + // Old code that will actually create valid two-dimensional sectors + // is included below for reference but is not likely to be used again. + + SetNodeFromSeg (node, &Segs[seg]); + HackSeg = seg; + if (!Segs[seg].planefront) + { + node.x += node.dx; + node.y += node.dy; + node.dx = -node.dx; + node.dy = -node.dy; + } + return Heuristic (node, set, false) != 0; + +#if 0 + // If there are only two segs in the set, and they form two sides + // of a triangle, the splitter should pass through their shared + // point and the (imaginary) third side of the triangle + if (setsize == 2) + { + FPrivVert *v1, *v2, *v3; + + if (Vertices[Segs[set].v2] == Vertices[Segs[seg].v1]) + { + v1 = &Vertices[Segs[set].v1]; + v2 = &Vertices[Segs[seg].v2]; + v3 = &Vertices[Segs[set].v2]; + } + else if (Vertices[Segs[set].v1] == Vertices[Segs[seg].v2]) + { + v1 = &Vertices[Segs[seg].v1]; + v2 = &Vertices[Segs[set].v2]; + v3 = &Vertices[Segs[seg].v2]; + } + else + { + v1 = v2 = v3 = NULL; + } + if (v1 != NULL) + { + node.x = v3->x; + node.y = v3->y; + node.dx = v1->x + (v2->x-v1->x)/2 - node.x; + node.dy = v1->y + (v2->y-v1->y)/2 - node.y; + return Heuristic (node, set, false) != 0; + } + } + + bool nosplit = true; + int firsthit = seg; + + do + { + seg = firsthit; + do + { + if (Segs[seg].linedef != -1 && + Segs[seg].frontsector != sec && + Segs[seg].frontsector == Segs[seg].backsector) + { + node.x = Vertices[Segs[set].v1].x; + node.y = Vertices[Segs[set].v1].y; + node.dx = Vertices[Segs[seg].v2].x - node.x; + node.dy = Vertices[Segs[seg].v2].y - node.y; + + if (Heuristic (node, set, nosplit) != 0) + { + return true; + } + + node.dx = Vertices[Segs[seg].v1].x - node.x; + node.dy = Vertices[Segs[seg].v1].y - node.y; + + if (Heuristic (node, set, nosplit) != 0) + { + return true; + } + } + + seg = Segs[seg].next; + } while (seg != DWORD_MAX); + } while ((nosplit ^= 1) == 0); + + // Give up. + return false; +#endif +} + +// Splitters are chosen to coincide with segs in the given set. To reduce the +// number of segs that need to be considered as splitters, segs are grouped into +// according to the planes that they lie on. Because one seg on the plane is just +// as good as any other seg on the plane at defining a split, only one seg from +// each unique plane needs to be considered as a splitter. A result of 0 means +// this set is a convex region. A result of -1 means that there were possible +// splitters, but they all split segs we want to keep intact. +int FNodeBuilder::SelectSplitter (DWORD set, node_t &node, DWORD &splitseg, int step, bool nosplit) +{ + int stepleft; + int bestvalue; + DWORD bestseg; + DWORD seg; + bool nosplitters = false; + + bestvalue = 0; + bestseg = DWORD_MAX; + + seg = set; + stepleft = 0; + + memset (&PlaneChecked[0], 0, PlaneChecked.Size()); + + while (seg != DWORD_MAX) + { + FPrivSeg *pseg = &Segs[seg]; + + if (--stepleft <= 0) + { + int l = pseg->planenum >> 3; + int r = 1 << (pseg->planenum & 7); + + if (l < 0 || (PlaneChecked[l] & r) == 0) + { + if (l >= 0) + { + PlaneChecked[l] |= r; + } + + stepleft = step; + SetNodeFromSeg (node, pseg); + + int value = Heuristic (node, set, nosplit); + + D(Printf ("Seg %5d (%5d,%5d)-(%5d,%5d) scores %d\n", seg, node.x>>16, node.y>>16, + (node.x+node.dx)>>16, (node.y+node.dy)>>16, value)); + + if (value > bestvalue) + { + bestvalue = value; + bestseg = seg; + } + else if (value < 0) + { + nosplitters = true; + } + } + else + { + pseg = pseg; + } + } + + seg = pseg->next; + } + + if (bestseg == DWORD_MAX) + { // No lines split any others into two sets, so this is a convex region. + D(Printf ("set %d, step %d, nosplit %d has no good splitter (%d)\n", set, step, nosplit, nosplitters)); + return nosplitters ? -1 : 0; + } + + D(Printf ("split seg %lu in set %d, score %d, step %d, nosplit %d\n", bestseg, set, bestvalue, step, nosplit)); + + splitseg = bestseg; + SetNodeFromSeg (node, &Segs[bestseg]); + return 1; +} + +// Given a splitter (node), returns a score based on how "good" the resulting +// split in a set of segs is. Higher scores are better. -1 means this splitter +// splits something it shouldn't and will only be returned if honorNoSplit is +// true. A score of 0 means that the splitter does not split any of the segs +// in the set. + +int FNodeBuilder::Heuristic (node_t &node, DWORD set, bool honorNoSplit) +{ + int score = 0; + int segsInSet = 0; + int counts[2] = { 0, 0 }; + int realSegs[2] = { 0, 0 }; + int specialSegs[2] = { 0, 0 }; + DWORD i = set; + int sidev1, sidev2; + int side; + bool splitter = false; + size_t max, m2, p, q; + + Touched.Clear (); + Colinear.Clear (); + + while (i != DWORD_MAX) + { + const FPrivSeg *test = &Segs[i]; + + if (HackSeg == i) + { + side = 1; + } + else + { + side = ClassifyLine (node, test, sidev1, sidev2); + } + + switch (side) + { + case 0: // Seg is on only one side of the partition + case 1: + // If we don't split this line, but it abuts the splitter, also reject it. + // The "right" thing to do in this case is to only reject it if there is + // another nosplit seg from the same sector at this vertex. Note that a line + // that lies exactly on top of the splitter is okay. + if (test->loopnum && honorNoSplit && (sidev1 == 0 || sidev2 == 0)) + { + if ((sidev1 | sidev2) != 0) + { + max = Touched.Size(); + for (p = 0; p < max; ++p) + { + if (Touched[p] == test->loopnum) + { + break; + } + } + if (p == max) + { + Touched.Push (test->loopnum); + } + } + else + { + max = Colinear.Size(); + for (p = 0; p < max; ++p) + { + if (Colinear[p] == test->loopnum) + { + break; + } + } + if (p == max) + { + Colinear.Push (test->loopnum); + } + } + } + + counts[side]++; + if (test->linedef != -1) + { + realSegs[side]++; + if (test->frontsector == test->backsector) + { + specialSegs[side]++; + } + // Add some weight to the score for unsplit lines + score += SplitCost; + } + else + { + // Minisegs don't count quite as much for nosplitting + score += SplitCost / 4; + } + break; + + default: // Seg is cut by the partition + // If we are not allowed to split this seg, reject this splitter + if (test->loopnum) + { + if (honorNoSplit) + { + D(Printf ("Splits seg %d\n", i)); + return -1; + } + else + { + splitter = true; + } + } + + counts[0]++; + counts[1]++; + if (test->linedef != -1) + { + realSegs[0]++; + realSegs[1]++; + if (test->frontsector == test->backsector) + { + specialSegs[0]++; + specialSegs[1]++; + } + } + break; + } + + segsInSet++; + i = test->next; + } + + // If this line is outside all the others, return a special score + if (counts[0] == 0 || counts[1] == 0) + { + return 0; + } + + // A splitter must have at least one real seg on each side. + // Otherwise, a subsector could be left without any way to easily + // determine which sector it lies inside. + if (realSegs[0] == 0 || realSegs[1] == 0) + { + D(Printf ("Leaves a side with only mini segs\n")); + return -1; + } + + // Try to avoid splits that leave only "special" segs, so that the generated + // subsectors have a better chance of choosing the correct sector. This situation + // is not neccesarily bad, just undesirable. + if (honorNoSplit && (specialSegs[0] == realSegs[0] || specialSegs[1] == realSegs[1])) + { + D(Printf ("Leaves a side with only special segs\n")); + return -1; + } + + // If this splitter intersects any vertices of segs that should not be split, + // check if it is also colinear with another seg from the same sector. If it + // is, the splitter is okay. If not, it should be rejected. Why? Assuming that + // polyobject containers are convex (which they should be), a splitter that + // is colinear with one of the sector's segs and crosses the vertex of another + // seg of that sector must be crossing the container's corner and does not + // actually split the container. + + max = Touched.Size (); + m2 = Colinear.Size (); + + // If honorNoSplit is false, then both these lists will be empty. + + // If the splitter touches some vertices without being colinear to any, we + // can skip further checks and reject this right away. + if (m2 == 0 && max > 0) + { + return -1; + } + + for (p = 0; p < max; ++p) + { + int look = Touched[p]; + for (q = 0; q < m2; ++q) + { + if (look == Colinear[q]) + { + break; + } + } + if (q == m2) + { // Not a good one + return -1; + } + } + + // Doom maps are primarily axis-aligned lines, so it's usually a good + // idea to prefer axis-aligned splitters over diagonal ones. Doom originally + // had special-casing for orthogonal lines, so they performed better. ZDoom + // does not care about the line's direction, so this is merely a choice to + // try and improve the final tree. + + if ((node.dx == 0) || (node.dy == 0)) + { + // If we have to split a seg we would prefer to keep unsplit, give + // extra precedence to orthogonal lines so that the polyobjects + // outside the entrance to MAP06 in Hexen MAP02 display properly. + if (splitter) + { + score += segsInSet*8; + } + else + { + score += segsInSet/AAPreference; + } + } + + score += (counts[0] + counts[1]) - abs(counts[0] - counts[1]); + + return score; +} + +int FNodeBuilder::ClassifyLine (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2) +{ + const FPrivVert *v1 = &Vertices[seg->v1]; + const FPrivVert *v2 = &Vertices[seg->v2]; + sidev1 = PointOnSide (v1->x, v1->y, node.x, node.y, node.dx, node.dy); + sidev2 = PointOnSide (v2->x, v2->y, node.x, node.y, node.dx, node.dy); + + if ((sidev1 | sidev2) == 0) + { // seg is coplanar with the splitter, so use its orientation to determine + // which child it ends up in. If it faces the same direction as the splitter, + // it goes in front. Otherwise, it goes in back. + + if (node.dx != 0) + { + if ((node.dx > 0 && v2->x > v1->x) || (node.dx < 0 && v2->x < v1->x)) + { + return 0; + } + else + { + return 1; + } + } + else + { + if ((node.dy > 0 && v2->y > v1->y) || (node.dy < 0 && v2->y < v1->y)) + { + return 0; + } + else + { + return 1; + } + } + } + else if (sidev1 <= 0 && sidev2 <= 0) + { + return 0; + } + else if (sidev1 >= 0 && sidev2 >= 0) + { + return 1; + } + return -1; +} + +void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &outset0, DWORD &outset1) +{ + outset0 = DWORD_MAX; + outset1 = DWORD_MAX; + + Events.DeleteAll (); + SplitSharers.Clear (); + + while (set != DWORD_MAX) + { + bool hack; + FPrivSeg *seg = &Segs[set]; + int next = seg->next; + + int sidev1, sidev2, side; + + if (HackSeg == set) + { + HackSeg = DWORD_MAX; + side = 1; + sidev1 = sidev2 = 0; + hack = true; + } + else + { + side = ClassifyLine (node, seg, sidev1, sidev2); + hack = false; + } + + switch (side) + { + case 0: // seg is entirely in front + seg->next = outset0; + //Printf ("%lu in front\n", set); + outset0 = set; + break; + + case 1: // seg is entirely in back + seg->next = outset1; + //Printf ("%lu in back\n", set); + outset1 = set; + break; + + default: // seg needs to be split + double frac; + FPrivVert newvert; + size_t vertnum; + int seg2; + size_t i; + + //Printf ("%lu is cut\n", set); + if (seg->loopnum) + { + Printf (" Split seg %lu (%ld,%ld)-(%ld,%ld) of sector %d on line %d\n", + set, + Vertices[seg->v1].x>>16, Vertices[seg->v1].y>>16, + Vertices[seg->v2].x>>16, Vertices[seg->v2].y>>16, + seg->frontsector, seg->linedef); + } + + frac = InterceptVector (node, *seg); + newvert.x = Vertices[seg->v1].x; + newvert.y = Vertices[seg->v1].y; + newvert.x += fixed_t(frac * double(Vertices[seg->v2].x - newvert.x)); + newvert.y += fixed_t(frac * double(Vertices[seg->v2].y - newvert.y)); + for (i = 0; i < Vertices.Size(); ++i) + { + if (abs(Vertices[i].x - newvert.x) < VERTEX_EPSILON && + abs(Vertices[i].y - newvert.y) < VERTEX_EPSILON) + { + break; + } + } + if (i < Vertices.Size()) + { + vertnum = i; + } + else + { + newvert.segs = DWORD_MAX; + newvert.segs2 = DWORD_MAX; + vertnum = (int)Vertices.Push (newvert); + } + + seg2 = SplitSeg (set, vertnum, sidev1); + + Segs[seg2].next = outset0; + outset0 = seg2; + Segs[set].next = outset1; + outset1 = set; + + // Also split the seg on the back side + if (Segs[set].partner != DWORD_MAX) + { + int partner1 = Segs[set].partner; + int partner2 = SplitSeg (partner1, vertnum, sidev2); + // The newly created seg stays in the same set as the + // back seg because it has not been considered for splitting + // yet. If it had been, then the front seg would have already + // been split, and we would not be in this default case. + // Moreover, the back seg may not even be in the set being + // split, so we must not move its pieces into the out sets. + Segs[partner1].next = partner2; + Segs[partner2].partner = seg2; + Segs[seg2].partner = partner2; + + assert (Segs[partner2].v1 == Segs[seg2].v2); + assert (Segs[partner2].v2 == Segs[seg2].v1); + assert (Segs[partner1].v1 == Segs[set].v2); + assert (Segs[partner1].v2 == Segs[set].v1); + } + + if (GLNodes) + { + AddIntersection (node, vertnum); + } + + break; + } + if (side >= 0 && GLNodes) + { + if (sidev1 == 0) + { + double dist1 = AddIntersection (node, seg->v1); + if (sidev2 == 0) + { + double dist2 = AddIntersection (node, seg->v2); + FSplitSharer share = { dist1, set, dist2 > dist1 }; + SplitSharers.Push (share); + } + } + else if (sidev2 == 0) + { + AddIntersection (node, seg->v2); + } + } + if (hack && GLNodes) + { + DWORD newback, newfront; + + newback = AddMiniseg (seg->v2, seg->v1, DWORD_MAX, set, splitseg); + newfront = AddMiniseg (Segs[set].v1, Segs[set].v2, newback, set, splitseg); + Segs[newback].frontsector = Segs[newback].backsector = + Segs[newfront].frontsector = Segs[newfront].backsector = + Segs[set].frontsector; + + Segs[newback].next = outset1; + outset1 = newback; + Segs[newfront].next = outset0; + outset0 = newfront; + } + set = next; + } + FixSplitSharers (); + if (GLNodes) + { + AddMinisegs (node, splitseg, outset0, outset1); + } +} + +void FNodeBuilder::SetNodeFromSeg (node_t &node, const FPrivSeg *pseg) const +{ + if (pseg->planenum >= 0) + { + FSimpleLine *pline = &Planes[pseg->planenum]; + node.x = pline->x; + node.y = pline->y; + node.dx = pline->dx; + node.dy = pline->dy; + } + else + { + node.x = Vertices[pseg->v1].x; + node.y = Vertices[pseg->v1].y; + node.dx = Vertices[pseg->v2].x - node.x; + node.dy = Vertices[pseg->v2].y - node.y; + } +} + +DWORD FNodeBuilder::SplitSeg (DWORD segnum, int splitvert, int v1InFront) +{ + double dx, dy; + FPrivSeg newseg; + int newnum = (int)Segs.Size(); + + newseg = Segs[segnum]; + dx = double(Vertices[splitvert].x - Vertices[newseg.v1].x); + dy = double(Vertices[splitvert].y - Vertices[newseg.v1].y); + newseg.offset += fixed_t (sqrt (dx*dx + dy*dy)); + if (v1InFront > 0) + { + newseg.v1 = splitvert; + Segs[segnum].v2 = splitvert; + + RemoveSegFromVert2 (segnum, newseg.v2); + + newseg.nextforvert = Vertices[splitvert].segs; + Vertices[splitvert].segs = newnum; + + newseg.nextforvert2 = Vertices[newseg.v2].segs2; + Vertices[newseg.v2].segs2 = newnum; + + Segs[segnum].nextforvert2 = Vertices[splitvert].segs2; + Vertices[splitvert].segs2 = segnum; + } + else + { + Segs[segnum].v1 = splitvert; + newseg.v2 = splitvert; + + RemoveSegFromVert1 (segnum, newseg.v1); + + newseg.nextforvert = Vertices[newseg.v1].segs; + Vertices[newseg.v1].segs = newnum; + + newseg.nextforvert2 = Vertices[splitvert].segs2; + Vertices[splitvert].segs2 = newnum; + + Segs[segnum].nextforvert = Vertices[splitvert].segs; + Vertices[splitvert].segs = segnum; + } + + Segs.Push (newseg); + + D(Printf("Split seg %d to get seg %d\n", segnum, newnum)); + + return newnum; +} + +void FNodeBuilder::RemoveSegFromVert1 (DWORD segnum, int vertnum) +{ + FPrivVert *v = &Vertices[vertnum]; + + if (v->segs == segnum) + { + v->segs = Segs[segnum].nextforvert; + } + else + { + DWORD prev, curr; + prev = 0; + curr = v->segs; + while (curr != DWORD_MAX && curr != segnum) + { + prev = curr; + curr = Segs[curr].nextforvert; + } + if (curr == segnum) + { + Segs[prev].nextforvert = Segs[curr].nextforvert; + } + } +} + +void FNodeBuilder::RemoveSegFromVert2 (DWORD segnum, int vertnum) +{ + FPrivVert *v = &Vertices[vertnum]; + + if (v->segs2 == segnum) + { + v->segs2 = Segs[segnum].nextforvert2; + } + else + { + DWORD prev, curr; + prev = 0; + curr = v->segs2; + while (curr != DWORD_MAX && curr != segnum) + { + prev = curr; + curr = Segs[curr].nextforvert2; + } + if (curr == segnum) + { + Segs[prev].nextforvert2 = Segs[curr].nextforvert2; + } + } +} + +double FNodeBuilder::InterceptVector (const node_t &splitter, const FPrivSeg &seg) +{ + double v2x = (double)Vertices[seg.v1].x; + double v2y = (double)Vertices[seg.v1].y; + double v2dx = (double)Vertices[seg.v2].x - v2x; + double v2dy = (double)Vertices[seg.v2].y - v2y; + double v1dx = (double)splitter.dx; + double v1dy = (double)splitter.dy; + + double den = v1dy*v2dx - v1dx*v2dy; + + if (den == 0.0) + return 0; // parallel + + double v1x = (double)splitter.x; + double v1y = (double)splitter.y; + + double num = (v1x - v2x)*v1dy + (v2y - v1y)*v1dx; + double frac = num / den; + + return frac; +} + +int FNodeBuilder::PointOnSide (int x, int y, int x1, int y1, int dx, int dy) +{ + // For most cases, a simple dot product is enough. + double d_dx = double(dx); + double d_dy = double(dy); + double d_x = double(x); + double d_y = double(y); + double d_x1 = double(x1); + double d_y1 = double(y1); + + double s_num = (d_y1-d_y)*d_dx - (d_x1-d_x)*d_dy; + + if (fabs(s_num) < 17179869184.0) // 4<<32 + { + // Either the point is very near the line, or the segment defining + // the line is very short: Do a more expensive test to determine + // just how far from the line the point is. + double l = sqrt(d_dx*d_dx+d_dy*d_dy); + double dist = fabs(s_num)/l; + if (dist < SIDE_EPSILON) + { + return 0; + } + } + return s_num > 0.0 ? -1 : 1; +} + +void FNodeBuilder::PrintSet (int l, DWORD set) +{ + Printf ("set %d:\n", l); + for (; set != DWORD_MAX; set = Segs[set].next) + { + Printf ("\t%lu(%d):%d(%ld,%ld)-%d(%ld,%ld)\n", set, Segs[set].frontsector, + Segs[set].v1, + Vertices[Segs[set].v1].x>>16, Vertices[Segs[set].v1].y>>16, + Segs[set].v2, + Vertices[Segs[set].v2].x>>16, Vertices[Segs[set].v2].y>>16); + } + Printf ("*\n"); +} diff --git a/nodebuild.h b/nodebuild.h new file mode 100644 index 0000000..42426a9 --- /dev/null +++ b/nodebuild.h @@ -0,0 +1,189 @@ +#include "doomdata.h" +#include "workdata.h" +#include "tarray.h" + +struct FEventInfo +{ + int Vertex; + DWORD FrontSeg; +}; + +struct FEvent +{ + FEvent *Parent, *Left, *Right; + enum { RED, BLACK } Color; + double Distance; + FEventInfo Info; +}; + +class FEventTree +{ +public: + FEventTree (); + ~FEventTree (); + + FEvent *GetMinimum (); + FEvent *GetSuccessor (FEvent *event) const { FEvent *node = Successor(event); return node == &Nil ? NULL : node; } + FEvent *GetPredecessor (FEvent *event) const { FEvent *node = Predecessor(event); return node == &Nil ? NULL : node; } + + FEvent *GetNewNode (); + void Insert (FEvent *event); + void Delete (FEvent *event); + FEvent *FindEvent (double distance) const; + void DeleteAll (); + +private: + FEvent Nil; + FEvent *Root; + FEvent *Spare; + + void LeftRotate (FEvent *event); + void RightRotate (FEvent *event); + void DeleteFixUp (FEvent *event); + void DeletionTraverser (FEvent *event); + FEvent *Successor (FEvent *event) const; + FEvent *Predecessor (FEvent *event) const; +}; + +class FNodeBuilder +{ + struct FPrivSeg + { + int v1, v2; + int sidedef; + int linedef; + int frontsector; + int backsector; + DWORD next; + DWORD nextforvert; + DWORD nextforvert2; + int loopnum; // loop number for split avoidance (0 means splitting is okay) + DWORD partner; // seg on back side + DWORD storedseg; // seg # in the GL_SEGS lump + angle_t angle; + fixed_t offset; + + int planenum; + bool planefront; + FPrivSeg *hashnext; + }; + struct FPrivVert + { + fixed_t x, y; + DWORD segs; // segs that use this vertex as v1 + DWORD segs2; // segs that use this vertex as v2 + + bool operator== (const FPrivVert &other) + { + return x == other.x && y == other.y; + } + }; + struct FSimpleLine + { + fixed_t x, y, dx, dy; + }; + union USegPtr + { + DWORD SegNum; + FPrivSeg *SegPtr; + }; + struct FSplitSharer + { + double Distance; + DWORD Seg; + bool Forward; + }; +public: + struct FPolyStart + { + int polynum; + fixed_t x, y; + }; + + FNodeBuilder (FLevel &level, + TArray &polyspots, TArray &anchors, + const char *name, bool makeGLnodes); + + void GetVertices (WideVertex *&verts, int &count); + void GetNodes (MapNodeEx *&nodes, int &nodeCount, + MapSeg *&segs, int &segCount, + MapSubsectorEx *&ssecs, int &subCount); + + void GetGLNodes (MapNodeEx *&nodes, int &nodeCount, + MapSegGLEx *&segs, int &segCount, + MapSubsectorEx *&ssecs, int &subCount); + + // < 0 : in front of line + // == 0 : on line + // > 0 : behind line + + static int PointOnSide (int x, int y, int x1, int y1, int dx, int dy); + +private: + TArray Nodes; + TArray Subsectors; + TArray SubsectorSets; + TArray Segs; + TArray Vertices; + TArray SegList; + TArray PlaneChecked; + TArray Planes; + size_t InitialVertices; // Number of vertices in a map that are connected to linedefs + + TArray Touched; // Loops a splitter touches on a vertex + TArray Colinear; // Loops with edges colinear to a splitter + FEventTree Events; // Vertices intersected by the current splitter + TArray SplitSharers; // Segs collinear with the current splitter + + DWORD HackSeg; // Seg to force to back of splitter + FLevel &Level; + bool GLNodes; + + // Progress meter stuff + int SegsStuffed; + const char *MapName; + + void FindUsedVertices (WideVertex *vertices, int max); + int SelectVertexExact (FPrivVert &vertex); + void BuildTree (); + void MakeSegsFromSides (); + FPrivSeg *CheckSegForDuplicate (const FPrivSeg *check); + void GroupSegPlanes (); + void FindPolyContainers (TArray &spots, TArray &anchors); + bool GetPolyExtents (int polynum, fixed_t bbox[4]); + int MarkLoop (DWORD firstseg, int loopnum); + void AddSegToBBox (fixed_t bbox[4], const FPrivSeg *seg); + DWORD CreateNode (DWORD set, fixed_t bbox[4]); + DWORD CreateSubsector (DWORD set, fixed_t bbox[4]); + void CreateSubsectorsForReal (); + bool CheckSubsector (DWORD set, node_t &node, DWORD &splitseg, int setsize); + int SelectSplitter (DWORD set, node_t &node, DWORD &splitseg, int step, bool nosplit); + void SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &outset0, DWORD &outset1); + DWORD SplitSeg (DWORD segnum, int splitvert, int v1InFront); + int Heuristic (node_t &node, DWORD set, bool honorNoSplit); + int ClassifyLine (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2); + int CountSegs (DWORD set) const; + + void FixSplitSharers (); + double AddIntersection (const node_t &node, int vertex); + void AddMinisegs (const node_t &node, DWORD splitseg, DWORD &fset, DWORD &rset); + DWORD CheckLoopStart (fixed_t dx, fixed_t dy, int vertex1, int vertex2); + DWORD CheckLoopEnd (fixed_t dx, fixed_t dy, int vertex2); + void RemoveSegFromVert1 (DWORD segnum, int vertnum); + void RemoveSegFromVert2 (DWORD segnum, int vertnum); + DWORD AddMiniseg (int v1, int v2, DWORD partner, DWORD seg1, DWORD splitseg); + void SetNodeFromSeg (node_t &node, const FPrivSeg *pseg) const; + + int RemoveMinisegs (MapNodeEx *nodes, TArray &segs, MapSubsectorEx *subs, int node, short bbox[4]); + int StripMinisegs (TArray &segs, int subsector, short bbox[4]); + void AddSegToShortBBox (short bbox[4], const FPrivSeg *seg); + int CloseSubsector (TArray &segs, int subsector); + DWORD PushGLSeg (TArray &segs, const FPrivSeg *seg); + void PushConnectingGLSeg (int subsector, TArray &segs, int v1, int v2); + + static int SortSegs (const void *a, const void *b); + + double InterceptVector (const node_t &splitter, const FPrivSeg &seg); + + void PrintSet (int l, DWORD set); +}; diff --git a/nodebuild_events.cpp b/nodebuild_events.cpp new file mode 100644 index 0000000..adccb63 --- /dev/null +++ b/nodebuild_events.cpp @@ -0,0 +1,407 @@ +/* + A red-black tree implementation for building minisegs. + Copyright (C) 2002,2003 Randy Heit + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#include "zdbsp.h" +#include "nodebuild.h" + +FEventTree::FEventTree () +: Root (&Nil), Spare (NULL) +{ +} + +FEventTree::~FEventTree () +{ + FEvent *probe; + + DeleteAll (); + probe = Spare; + while (probe != NULL) + { + FEvent *next = probe->Left; + delete probe; + probe = next; + } +} + +void FEventTree::DeleteAll () +{ + DeletionTraverser (Root); + Root = &Nil; +} + +void FEventTree::DeletionTraverser (FEvent *node) +{ + if (node != &Nil && node != NULL) + { + DeletionTraverser (node->Left); + DeletionTraverser (node->Right); + node->Left = Spare; + Spare = node; + } +} + +void FEventTree::LeftRotate (FEvent *x) +{ + FEvent *y = x->Right; + x->Right = y->Left; + if (y->Left != &Nil) + { + y->Left->Parent = x; + } + y->Parent = x->Parent; + if (x->Parent != &Nil) + { + Root = y; + } + else if (x == x->Parent->Left) + { + x->Parent->Left = y; + } + else + { + x->Parent->Right = y; + } + y->Left = x; + x->Parent = y; +} + +void FEventTree::RightRotate (FEvent *x) +{ + FEvent *y = x->Left; + x->Left = y->Right; + if (y->Right != &Nil) + { + y->Right->Parent = x; + } + y->Parent = x->Parent; + if (x->Parent != &Nil) + { + Root = y; + } + else if (x == x->Parent->Left) + { + x->Parent->Left = y; + } + else + { + x->Parent->Right = y; + } + y->Right = x; + x->Parent = y; +} + +FEvent *FEventTree::GetNewNode () +{ + FEvent *node; + + if (Spare != NULL) + { + node = Spare; + Spare = node->Left; + } + else + { + node = new FEvent; + } + return node; +} + +void FEventTree::Insert (FEvent *z) +{ + FEvent *y = &Nil; + FEvent *x = Root; + + while (x != &Nil) + { + y = x; + if (z->Distance < x->Distance) + { + x = x->Left; + } + else + { + x = x->Right; + } + } + z->Parent = y; + if (y == &Nil) + { + Root = z; + } + else if (z->Distance < y->Distance) + { + y->Left = z; + } + else + { + y->Right = z; + } + z->Left = &Nil; + z->Right = &Nil; + + z->Color = FEvent::RED; + while (z != Root && z->Parent->Color != FEvent::RED) + { + if (z->Parent == z->Parent->Parent->Left) + { + y = z->Parent->Parent->Right; + if (y->Color == FEvent::RED) + { + z->Parent->Color = FEvent::BLACK; + y->Color = FEvent::BLACK; + z->Parent->Parent->Color = FEvent::RED; + z = z->Parent->Parent; + } + else + { + if (z == z->Parent->Right) + { + z = z->Parent; + LeftRotate (z); + } + z->Parent->Color = FEvent::BLACK; + z->Parent->Parent->Color = FEvent::RED; + RightRotate (z->Parent->Parent); + } + } + else + { + y = z->Parent->Parent->Left; + if (y->Color == FEvent::RED) + { + z->Parent->Color = FEvent::BLACK; + y->Color = FEvent::BLACK; + z->Parent->Parent->Color = FEvent::RED; + z = z->Parent->Parent; + } + else + { + if (z == z->Parent->Left) + { + z = z->Parent; + RightRotate (z); + } + z->Parent->Color = FEvent::BLACK; + z->Parent->Parent->Color = FEvent::RED; + RightRotate (z->Parent->Parent); + } + } + } +} + +void FEventTree::Delete (FEvent *z) +{ + FEvent *x, *y; + + if (z->Left == &Nil || z->Right == &Nil) + { + y = z; + } + else + { + y = Successor (z); + } + if (y->Left != &Nil) + { + x = y->Left; + } + else + { + x = y->Right; + } + x->Parent = y->Parent; + if (y->Parent == &Nil) + { + Root = x; + } + else if (y == y->Parent->Left) + { + y->Parent->Left = x; + } + else + { + y->Parent->Right = x; + } + if (y != z) + { + z->Distance = y->Distance; + z->Info = y->Info; + } + if (y->Color == FEvent::BLACK) + { + DeleteFixUp (x); + } + + y->Left = Spare; + Spare = y; +} + +void FEventTree::DeleteFixUp (FEvent *x) +{ + FEvent *w; + + while (x != Root && x->Color == FEvent::BLACK) + { + if (x == x->Parent->Left) + { + w = x->Parent->Right; + if (w->Color == FEvent::RED) + { + w->Color = FEvent::BLACK; + x->Parent->Color = FEvent::RED; + LeftRotate (x->Parent); + w = x->Parent->Right; + } + if (w->Left->Color == FEvent::BLACK && w->Right->Color == FEvent::BLACK) + { + w->Color = FEvent::RED; + x = x->Parent; + } + else + { + if (w->Right->Color == FEvent::BLACK) + { + w->Left->Color = FEvent::BLACK; + w->Color = FEvent::RED; + RightRotate (w); + w = x->Parent->Right; + } + w->Color = x->Parent->Color; + x->Parent->Color = FEvent::BLACK; + w->Right->Color = FEvent::BLACK; + LeftRotate (x->Parent); + x = Root; + } + } + else + { + w = x->Parent->Left; + if (w->Color == FEvent::RED) + { + w->Color = FEvent::BLACK; + x->Parent->Color = FEvent::RED; + RightRotate (x->Parent); + w = x->Parent->Left; + } + if (w->Right->Color == FEvent::BLACK && w->Left->Color == FEvent::BLACK) + { + w->Color = FEvent::RED; + x = x->Parent; + } + else + { + if (w->Left->Color == FEvent::BLACK) + { + w->Right->Color = FEvent::BLACK; + w->Color = FEvent::RED; + LeftRotate (w); + w = x->Parent->Left; + } + w->Color = x->Parent->Color; + x->Parent->Color = FEvent::BLACK; + w->Left->Color = FEvent::BLACK; + RightRotate (x->Parent); + x = Root; + } + } + } +} + +FEvent *FEventTree::Successor (FEvent *event) const +{ + if (event->Right != &Nil) + { + event = event->Right; + while (event->Left != &Nil) + { + event = event->Left; + } + return event; + } + else + { + FEvent *y = event->Parent; + while (y != &Nil && event == y->Right) + { + event = y; + y = y->Parent; + } + return y; + } +} + +FEvent *FEventTree::Predecessor (FEvent *event) const +{ + if (event->Left != &Nil) + { + event = event->Left; + while (event->Right != &Nil) + { + event = event->Right; + } + return event; + } + else + { + FEvent *y = event->Parent; + while (y != &Nil && event == y->Left) + { + event = y; + y = y->Parent; + } + return y; + } +} + +FEvent *FEventTree::FindEvent (double key) const +{ + FEvent *node = Root; + + while (node != &Nil) + { + if (node->Distance == key) + { + return node; + } + else if (node->Distance > key) + { + node = node->Left; + } + else + { + node = node->Right; + } + } + return NULL; +} + +FEvent *FEventTree::GetMinimum () +{ + FEvent *node = Root; + + if (node == &Nil) + { + return NULL; + } + while (node->Left != &Nil) + { + node = node->Left; + } + return node; +} diff --git a/nodebuild_extract.cpp b/nodebuild_extract.cpp new file mode 100644 index 0000000..b271724 --- /dev/null +++ b/nodebuild_extract.cpp @@ -0,0 +1,349 @@ +/* + Routines for extracting usable data from the new BSP tree. + Copyright (C) 2002,2003 Randy Heit + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#include +#include + +#include "zdbsp.h" +#include "nodebuild.h" +#include "templates.h" + +void FNodeBuilder::GetGLNodes (MapNodeEx *&outNodes, int &nodeCount, + MapSegGLEx *&outSegs, int &segCount, + MapSubsectorEx *&outSubs, int &subCount) +{ + TArray segs (Segs.Size()*5/4); + int i, j, k; + + nodeCount = Nodes.Size (); + outNodes = new MapNodeEx[nodeCount]; + for (i = 0; i < nodeCount; ++i) + { + const node_t *orgnode = &Nodes[i]; + MapNodeEx *newnode = &outNodes[i]; + + newnode->x = short(orgnode->x >> FRACBITS); + newnode->y = short(orgnode->y >> FRACBITS); + newnode->dx = short(orgnode->dx >> FRACBITS); + newnode->dy = short(orgnode->dy >> FRACBITS); + + for (j = 0; j < 2; ++j) + { + for (k = 0; k < 4; ++k) + { + newnode->bbox[j][k] = orgnode->bbox[j][k] >> FRACBITS; + } + newnode->children[j] = orgnode->intchildren[j]; + } + } + + subCount = Subsectors.Size(); + outSubs = new MapSubsectorEx[subCount]; + for (i = 0; i < subCount; ++i) + { + int numsegs = CloseSubsector (segs, i); + outSubs[i].numlines = numsegs; + outSubs[i].firstline = segs.Size() - numsegs; + } + + segCount = segs.Size (); + outSegs = new MapSegGLEx[segCount]; + memcpy (outSegs, &segs[0], segCount*sizeof(MapSegGLEx)); + + for (i = 0; i < segCount; ++i) + { + if (outSegs[i].partner != DWORD_MAX) + { + outSegs[i].partner = Segs[outSegs[i].partner].storedseg; + } + } +} + +int FNodeBuilder::CloseSubsector (TArray &segs, int subsector) +{ + FPrivSeg *seg, *prev; + angle_t prevAngle; + double accumx, accumy; + fixed_t midx, midy; + int i, j, first, max, count, firstVert; + + first = Subsectors[subsector].firstline; + max = first + Subsectors[subsector].numlines; + count = 0; + + accumx = accumy = 0.0; + + for (i = first; i < max; ++i) + { + seg = &Segs[SegList[i].SegNum]; + accumx += double(Vertices[seg->v1].x) + double(Vertices[seg->v2].x); + accumy += double(Vertices[seg->v1].y) + double(Vertices[seg->v2].y); + } + + midx = fixed_t(accumx / (max - first) / 2); + midy = fixed_t(accumy / (max - first) / 2); + + seg = &Segs[SegList[first].SegNum]; + prevAngle = PointToAngle (Vertices[seg->v1].x - midx, Vertices[seg->v1].y - midy); + seg->storedseg = PushGLSeg (segs, seg); + count = 1; + prev = seg; + firstVert = seg->v1; + +#if 0 + printf("--%d--\n", subsector); + for (j = first; j < max; ++j) + { + seg = &Segs[SegList[j].SegNum]; + angle_t ang = PointToAngle (Vertices[seg->v1].x - midx, Vertices[seg->v1].y - midy); + printf ("%d: %5d(%5d,%5d)->%5d(%5d,%5d) - %3.3f\n", j, + seg->v1, Vertices[seg->v1].x>>16, Vertices[seg->v1].y>>16, + seg->v2, Vertices[seg->v2].x>>16, Vertices[seg->v2].y>>16, + double(ang/2)*180/(1<<30)); + } +#endif + + for (i = first + 1; i < max; ++i) + { + angle_t bestdiff = ANGLE_MAX; + FPrivSeg *bestseg = NULL; + int bestj = -1; + for (j = first; j < max; ++j) + { + seg = &Segs[SegList[j].SegNum]; + angle_t ang = PointToAngle (Vertices[seg->v1].x - midx, Vertices[seg->v1].y - midy); + angle_t diff = prevAngle - ang; + if (seg->v1 == prev->v2) + { + bestdiff = diff; + bestseg = seg; + bestj = j; + break; + } + if (diff < bestdiff && diff > 0) + { + bestdiff = diff; + bestseg = seg; + bestj = j; + } + } + if (bestseg != NULL) + { + seg = bestseg; + } + if (prev->v2 != seg->v1) + { + // Add a new miniseg to connect the two segs + PushConnectingGLSeg (subsector, segs, prev->v2, seg->v1); + count++; + } +#if 0 + printf ("+%d\n", bestj); +#endif + prevAngle -= bestdiff; + seg->storedseg = PushGLSeg (segs, seg); + count++; + prev = seg; + if (seg->v2 == firstVert) + { + prev = seg; + break; + } + } +#if 0 + printf ("\n"); +#endif + + if (prev->v2 != firstVert) + { + PushConnectingGLSeg (subsector, segs, prev->v2, firstVert); + count++; + } + + return count; +} + +DWORD FNodeBuilder::PushGLSeg (TArray &segs, const FPrivSeg *seg) +{ + MapSegGLEx newseg; + + newseg.v1 = seg->v1; + newseg.v2 = seg->v2; + newseg.linedef = seg->linedef; + newseg.side = newseg.linedef != NO_INDEX + ? Level.Lines[newseg.linedef].sidenum[1] == seg->sidedef ? 1 : 0 + : 0; + newseg.partner = seg->partner; + return segs.Push (newseg); +} + +void FNodeBuilder::PushConnectingGLSeg (int subsector, TArray &segs, int v1, int v2) +{ + MapSegGLEx newseg; + + Warn ("Unclosed subsector %d, from (%d,%d) to (%d,%d)\n", subsector, + Vertices[v1].x >> FRACBITS, Vertices[v1].y >> FRACBITS, + Vertices[v2].x >> FRACBITS, Vertices[v2].y >> FRACBITS); + + newseg.v1 = v1; + newseg.v2 = v2; + newseg.linedef = NO_INDEX; + newseg.side = 0; + newseg.partner = DWORD_MAX; + segs.Push (newseg); +} + +void FNodeBuilder::GetVertices (WideVertex *&verts, int &count) +{ + count = Vertices.Size (); + verts = new WideVertex[count]; + + for (int i = 0; i < count; ++i) + { + verts[i].x = Vertices[i].x; + verts[i].y = Vertices[i].y; + } +} + +void FNodeBuilder::GetNodes (MapNodeEx *&outNodes, int &nodeCount, + MapSeg *&outSegs, int &segCount, + MapSubsectorEx *&outSubs, int &subCount) +{ + short bbox[4]; + TArray segs (Segs.Size()); + + // Walk the BSP and create a new BSP with only the information + // suitable for a standard tree. At a minimum, this means removing + // all minisegs. As an optional step, I also recompute all the + // nodes' bounding boxes so that they only bound the real segs and + // not the minisegs. + + nodeCount = Nodes.Size (); + outNodes = new MapNodeEx[nodeCount]; + + subCount = Subsectors.Size (); + outSubs = new MapSubsectorEx[subCount]; + + RemoveMinisegs (outNodes, segs, outSubs, Nodes.Size() - 1, bbox); + + segCount = segs.Size (); + outSegs = new MapSeg[segCount]; + memcpy (outSegs, &segs[0], segCount*sizeof(MapSeg)); +} + +int FNodeBuilder::RemoveMinisegs (MapNodeEx *nodes, + TArray &segs, MapSubsectorEx *subs, int node, short bbox[4]) +{ + if (node & NFX_SUBSECTOR) + { + int subnum = node == -1 ? 0 : node & ~NFX_SUBSECTOR; + int numsegs = StripMinisegs (segs, subnum, bbox); + subs[subnum].numlines = numsegs; + subs[subnum].firstline = segs.Size() - numsegs; + return NFX_SUBSECTOR | subnum; + } + else + { + const node_t *orgnode = &Nodes[node]; + MapNodeEx *newnode = &nodes[node]; + + int child0 = RemoveMinisegs (nodes, segs, subs, orgnode->intchildren[0], newnode->bbox[0]); + int child1 = RemoveMinisegs (nodes, segs, subs, orgnode->intchildren[1], newnode->bbox[1]); + + + newnode->x = orgnode->x >> FRACBITS; + newnode->y = orgnode->y >> FRACBITS; + newnode->dx = orgnode->dx >> FRACBITS; + newnode->dy = orgnode->dy >> FRACBITS; + newnode->children[0] = child0; + newnode->children[1] = child1; + + bbox[BOXTOP] = MAX(newnode->bbox[0][BOXTOP], newnode->bbox[1][BOXTOP]); + bbox[BOXBOTTOM] = MIN(newnode->bbox[0][BOXBOTTOM], newnode->bbox[1][BOXBOTTOM]); + bbox[BOXLEFT] = MIN(newnode->bbox[0][BOXLEFT], newnode->bbox[1][BOXLEFT]); + bbox[BOXRIGHT] = MAX(newnode->bbox[0][BOXRIGHT], newnode->bbox[1][BOXRIGHT]); + + return node; + } +} + +int FNodeBuilder::StripMinisegs (TArray &segs, int subsector, short bbox[4]) +{ + int count, i, max; + + // The bounding box is recomputed to only cover the real segs and not the + // minisegs in the subsector. + bbox[BOXTOP] = -32768; + bbox[BOXBOTTOM] = 32767; + bbox[BOXLEFT] = 32767; + bbox[BOXRIGHT] = -32768; + + i = Subsectors[subsector].firstline; + max = Subsectors[subsector].numlines + i; + + for (count = 0; i < max; ++i) + { + const FPrivSeg *org = &Segs[SegList[i].SegNum]; + + // Because of the ordering guaranteed by SortSegs(), all mini segs will + // be at the end of the subsector, so once one is encountered, we can + // stop right away. + if (org->linedef == -1) + { + break; + } + else + { + MapSeg newseg; + + AddSegToShortBBox (bbox, org); + + newseg.v1 = org->v1; + newseg.v2 = org->v2; + newseg.angle = org->angle >> 16; + newseg.offset = org->offset >> FRACBITS; + newseg.linedef = org->linedef; + newseg.side = Level.Lines[org->linedef].sidenum[1] == org->sidedef ? 1 : 0; + segs.Push (newseg); + ++count; + } + } + return count; +} + +void FNodeBuilder::AddSegToShortBBox (short bbox[4], const FPrivSeg *seg) +{ + const FPrivVert *v1 = &Vertices[seg->v1]; + const FPrivVert *v2 = &Vertices[seg->v2]; + + short v1x = v1->x >> FRACBITS; + short v1y = v1->y >> FRACBITS; + short v2x = v2->x >> FRACBITS; + short v2y = v2->y >> FRACBITS; + + if (v1x < bbox[BOXLEFT]) bbox[BOXLEFT] = v1x; + if (v1x > bbox[BOXRIGHT]) bbox[BOXRIGHT] = v1x; + if (v1y < bbox[BOXBOTTOM]) bbox[BOXBOTTOM] = v1y; + if (v1y > bbox[BOXTOP]) bbox[BOXTOP] = v1y; + + if (v2x < bbox[BOXLEFT]) bbox[BOXLEFT] = v2x; + if (v2x > bbox[BOXRIGHT]) bbox[BOXRIGHT] = v2x; + if (v2y < bbox[BOXBOTTOM]) bbox[BOXBOTTOM] = v2y; + if (v2y > bbox[BOXTOP]) bbox[BOXTOP] = v2y; +} diff --git a/nodebuild_gl.cpp b/nodebuild_gl.cpp new file mode 100644 index 0000000..5461627 --- /dev/null +++ b/nodebuild_gl.cpp @@ -0,0 +1,388 @@ +/* + Routines only necessary for building GL-friendly nodes. + Copyright (C) 2002,2003 Randy Heit + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#include +#include "zdbsp.h" +#include "nodebuild.h" + +#define Printf printf + +#if 0 +#include +#define D(x) x +#else +#define D(x) do{}while(0) +#endif + +double FNodeBuilder::AddIntersection (const node_t &node, int vertex) +{ + static const FEventInfo defaultInfo = + { + -1, DWORD_MAX + }; + + // Calculate signed distance of intersection vertex from start of splitter. + // Only ordering is important, so we don't need a sqrt. + FPrivVert *v = &Vertices[vertex]; + double dx = double(v->x - node.x); + double dy = double(v->y - node.y); + double dist = dx*dx + dy*dy; + + if (node.dx != 0) + { + if ((node.dx > 0 && dx < 0.0) || (node.dx < 0 && dx > 0.0)) + { + dist = -dist; + } + } + else + { + if ((node.dy > 0 && dy < 0.0) || (node.dy < 0 && dy > 0.0)) + { + dist = -dist; + } + } + + FEvent *event = Events.FindEvent (dist); + + if (event == NULL) + { + event = Events.GetNewNode (); + event->Distance = dist; + event->Info = defaultInfo; + event->Info.Vertex = vertex; + Events.Insert (event); + } + + return dist; +} + +// If there are any segs on the splitter that span more than two events, they +// must be split. Alien Vendetta is one example wad that is quite bad about +// having overlapping lines. If we skip this step, these segs will still be +// split later, but minisegs will erroneously be added for them, and partner +// seg information will be messed up in the generated tree. +void FNodeBuilder::FixSplitSharers () +{ + for (size_t i = 0; i < SplitSharers.Size(); ++i) + { + DWORD seg = SplitSharers[i].Seg; + int v2 = Segs[seg].v2; + FEvent *event = Events.FindEvent (SplitSharers[i].Distance); + FEvent *next; + + if (event == NULL) + { // Should not happen + continue; + } + + if (SplitSharers[i].Forward) + { + event = Events.GetSuccessor (event); + if (event == NULL) + { + continue; + } + next = Events.GetSuccessor (event); + } + else + { + event = Events.GetPredecessor (event); + if (event == NULL) + { + continue; + } + next = Events.GetPredecessor (event); + } + + while (event != NULL && next != NULL && event->Info.Vertex != v2) + { + D(Printf("Forced split of seg %d(%d->%d) at %d(%d,%d)\n", seg, + Segs[seg].v1, Segs[seg].v2, + event->Info.Vertex, + Vertices[event->Info.Vertex].x>>16, + Vertices[event->Info.Vertex].y>>16)); + + DWORD newseg = SplitSeg (seg, event->Info.Vertex, 1); + + Segs[newseg].next = Segs[seg].next; + Segs[seg].next = newseg; + + DWORD partner = Segs[seg].partner; + if (partner != DWORD_MAX) + { + int endpartner = SplitSeg (partner, event->Info.Vertex, 1); + + Segs[endpartner].next = Segs[partner].next; + Segs[partner].next = endpartner; + + Segs[seg].partner = endpartner; + //Segs[endpartner].partner = seg; + Segs[partner].partner = newseg; + + assert (Segs[Segs[seg].partner].partner == seg); + assert (Segs[Segs[newseg].partner].partner == newseg); + assert (Segs[seg].v1 == Segs[endpartner].v2); + assert (Segs[seg].v2 == Segs[endpartner].v1); + assert (Segs[partner].v1 == Segs[newseg].v2); + assert (Segs[partner].v2 == Segs[newseg].v1); + } + + seg = newseg; + if (SplitSharers[i].Forward) + { + event = next; + next = Events.GetSuccessor (next); + } + else + { + event = next; + next = Events.GetPredecessor (next); + } + } + } +} + +void FNodeBuilder::AddMinisegs (const node_t &node, DWORD splitseg, DWORD &fset, DWORD &bset) +{ + FEvent *event = Events.GetMinimum (), *prev = NULL; + + while (event != NULL) + { + if (prev != NULL) + { + DWORD fseg1, bseg1, fseg2, bseg2; + DWORD fnseg, bnseg; + + // Minisegs should only be added when they can create valid loops on both the front and + // back of the splitter. This means some subsectors could be unclosed if their sectors + // are unclosed, but at least we won't be needlessly creating subsectors in void space. + // Unclosed subsectors can be closed trivially once the BSP tree is complete. + + if ((fseg1 = CheckLoopStart (node.dx, node.dy, prev->Info.Vertex, event->Info.Vertex)) != DWORD_MAX && + (bseg1 = CheckLoopStart (-node.dx, -node.dy, event->Info.Vertex, prev->Info.Vertex)) != DWORD_MAX && + (fseg2 = CheckLoopEnd (node.dx, node.dy, event->Info.Vertex)) != DWORD_MAX && + (bseg2 = CheckLoopEnd (-node.dx, -node.dy, prev->Info.Vertex)) != DWORD_MAX) + { + // Add miniseg on the front side + fnseg = AddMiniseg (prev->Info.Vertex, event->Info.Vertex, DWORD_MAX, fseg1, splitseg); + Segs[fnseg].next = fset; + fset = fnseg; + + // Add miniseg on the back side + bnseg = AddMiniseg (event->Info.Vertex, prev->Info.Vertex, fnseg, bseg1, splitseg); + Segs[bnseg].next = bset; + bset = bnseg; + + int fsector, bsector; + + fsector = Segs[fseg1].frontsector; + bsector = Segs[bseg1].frontsector; + + Segs[fnseg].frontsector = fsector; + Segs[fnseg].backsector = bsector; + Segs[bnseg].frontsector = bsector; + Segs[bnseg].backsector = fsector; + + // Only print the warning if this might be bad. + if (fsector != bsector && + fsector != Segs[fseg1].backsector && + bsector != Segs[bseg1].backsector) + { + Warn ("Sectors %d at (%d,%d) and %d at (%d,%d) don't match.\n", + Segs[fseg1].frontsector, + Vertices[prev->Info.Vertex].x>>FRACBITS, Vertices[prev->Info.Vertex].y>>FRACBITS, + Segs[bseg1].frontsector, + Vertices[event->Info.Vertex].x>>FRACBITS, Vertices[event->Info.Vertex].y>>FRACBITS + ); + } + + D(Printf ("**Minisegs** %d/%d added %d(%d,%d)->%d(%d,%d)\n", fnseg, bnseg, + prev->Info.Vertex, + Vertices[prev->Info.Vertex].x>>16, Vertices[prev->Info.Vertex].y>>16, + event->Info.Vertex, + Vertices[event->Info.Vertex].x>>16, Vertices[event->Info.Vertex].y>>16)); + } + } + prev = event; + event = Events.GetSuccessor (event); + } +} + +DWORD FNodeBuilder::AddMiniseg (int v1, int v2, DWORD partner, DWORD seg1, DWORD splitseg) +{ + DWORD nseg; + FPrivSeg *seg = &Segs[seg1]; + FPrivSeg newseg; + + newseg.sidedef = NO_INDEX; + newseg.linedef = -1; + newseg.loopnum = 0; + newseg.next = DWORD_MAX; + newseg.planefront = true; + + if (splitseg != DWORD_MAX) + { + newseg.planenum = Segs[splitseg].planenum; + } + else + { + newseg.planenum = -1; + } + + newseg.v1 = v1; + newseg.v2 = v2; + newseg.nextforvert = Vertices[v1].segs; + newseg.nextforvert2 = Vertices[v2].segs2; + newseg.next = seg->next; + if (partner != DWORD_MAX) + { + newseg.partner = partner; + + assert (Segs[partner].v1 == newseg.v2); + assert (Segs[partner].v2 == newseg.v1); + } + else + { + newseg.partner = DWORD_MAX; + } + nseg = Segs.Push (newseg); + if (newseg.partner != DWORD_MAX) + { + Segs[partner].partner = nseg; + } + Vertices[v1].segs = nseg; + Vertices[v2].segs2 = nseg; + //Printf ("Between %d and %d::::\n", seg1, seg2); + return nseg; +} + +DWORD FNodeBuilder::CheckLoopStart (fixed_t dx, fixed_t dy, int vertex, int vertex2) +{ + FPrivVert *v = &Vertices[vertex]; + angle_t splitAngle = PointToAngle (dx, dy); + DWORD segnum; + angle_t bestang; + DWORD bestseg; + + // Find the seg ending at this vertex that forms the smallest angle + // to the splitter. + segnum = v->segs2; + bestang = ANGLE_MAX; + bestseg = DWORD_MAX; + while (segnum != DWORD_MAX) + { + FPrivSeg *seg = &Segs[segnum]; + angle_t segAngle = PointToAngle (Vertices[seg->v1].x - v->x, Vertices[seg->v1].y - v->y); + angle_t diff = splitAngle - segAngle; + + if (diff < ANGLE_EPSILON && + PointOnSide (Vertices[seg->v1].x, Vertices[seg->v1].y, v->x, v->y, dx, dy) == 0) + { + // If a seg lies right on the splitter, don't count it + } + else + { + if (diff <= bestang) + { + bestang = diff; + bestseg = segnum; + } + } + segnum = seg->nextforvert2; + } + if (bestseg == DWORD_MAX) + { + return DWORD_MAX; + } + // Now make sure there are no segs starting at this vertex that form + // an even smaller angle to the splitter. + segnum = v->segs; + while (segnum != DWORD_MAX) + { + FPrivSeg *seg = &Segs[segnum]; + if (seg->v2 == vertex2) + { + return DWORD_MAX; + } + angle_t segAngle = PointToAngle (Vertices[seg->v2].x - v->x, Vertices[seg->v2].y - v->y); + angle_t diff = splitAngle - segAngle; + if (diff < bestang && seg->partner != bestseg) + { + return DWORD_MAX; + } + segnum = seg->nextforvert; + } + return bestseg; +} + +DWORD FNodeBuilder::CheckLoopEnd (fixed_t dx, fixed_t dy, int vertex) +{ + FPrivVert *v = &Vertices[vertex]; + angle_t splitAngle = PointToAngle (dx, dy) + ANGLE_180; + DWORD segnum; + angle_t bestang; + DWORD bestseg; + + // Find the seg starting at this vertex that forms the smallest angle + // to the splitter. + segnum = v->segs; + bestang = ANGLE_MAX; + bestseg = DWORD_MAX; + while (segnum != DWORD_MAX) + { + FPrivSeg *seg = &Segs[segnum]; + angle_t segAngle = PointToAngle (Vertices[seg->v2].x - v->x, Vertices[seg->v2].y - v->y); + angle_t diff = segAngle - splitAngle; + + if (diff < ANGLE_EPSILON && + PointOnSide (Vertices[seg->v1].x, Vertices[seg->v1].y, v->x, v->y, dx, dy) == 0) + { + // If a seg lies right on the splitter, don't count it + } + else + { + if (diff <= bestang) + { + bestang = diff; + bestseg = segnum; + } + } + segnum = seg->nextforvert; + } + if (bestseg == DWORD_MAX) + { + return DWORD_MAX; + } + // Now make sure there are no segs ending at this vertex that form + // an even smaller angle to the splitter. + segnum = v->segs2; + while (segnum != DWORD_MAX) + { + FPrivSeg *seg = &Segs[segnum]; + angle_t segAngle = PointToAngle (Vertices[seg->v1].x - v->x, Vertices[seg->v1].y - v->y); + angle_t diff = segAngle - splitAngle; + if (diff < bestang && seg->partner != bestseg) + { + return DWORD_MAX; + } + segnum = seg->nextforvert2; + } + return bestseg; +} diff --git a/nodebuild_utility.cpp b/nodebuild_utility.cpp new file mode 100644 index 0000000..aa72ed3 --- /dev/null +++ b/nodebuild_utility.cpp @@ -0,0 +1,500 @@ +/* + Various utility functions. + Copyright (C) 2002,2003 Randy Heit + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#include +#include +#include + +#include "zdbsp.h" +#include "nodebuild.h" + +static const int PO_LINE_START = 1; +static const int PO_LINE_EXPLICIT = 5; + +#if 0 +#define D(x) x +#else +#define D(x) do{}while(0) +#endif + +#if 0 +#define P(x) x +#define Printf printf +#else +#define P(x) do{}while(0) +#endif + +void FNodeBuilder::FindUsedVertices (WideVertex *oldverts, int max) +{ + size_t *map = (size_t *)alloca (max*sizeof(size_t)); + int i; + FPrivVert newvert; + + memset (&map[0], -1, sizeof(size_t)*max); + + newvert.segs = DWORD_MAX; + newvert.segs2 = DWORD_MAX; + + for (i = 0; i < Level.NumLines; ++i) + { + int v1 = Level.Lines[i].v1; + int v2 = Level.Lines[i].v2; + + if (map[v1] == (size_t)-1) + { + newvert.x = oldverts[v1].x; + newvert.y = oldverts[v1].y; + map[v1] = SelectVertexExact (newvert); + } + if (map[v2] == (size_t)-1) + { + newvert.x = oldverts[v2].x; + newvert.y = oldverts[v2].y; + map[v2] = SelectVertexExact (newvert); + } + + Level.Lines[i].v1 = map[v1]; + Level.Lines[i].v2 = map[v2]; + } + InitialVertices = Vertices.Size (); + Level.NumOrgVerts = InitialVertices; +} + +int FNodeBuilder::SelectVertexExact (FPrivVert &vertex) +{ + for (size_t i = 0; i < Vertices.Size(); ++i) + { + if (Vertices[i].x == vertex.x && Vertices[i].y == vertex.y) + { + return (int)i; + } + } + return (int)Vertices.Push (vertex); +} + +// For every sidedef in the map, create a corresponding seg. + +void FNodeBuilder::MakeSegsFromSides () +{ + FPrivSeg *share1, *share2; + FPrivSeg seg; + int i, j; + + seg.next = DWORD_MAX; + seg.loopnum = 0; + seg.offset = 0; + seg.partner = DWORD_MAX; + + for (i = 0; i < Level.NumLines; ++i) + { + if (Level.Lines[i].sidenum[0] != NO_INDEX) + { + WORD backside; + + seg.linedef = i; + seg.sidedef = Level.Lines[i].sidenum[0]; + backside = Level.Lines[i].sidenum[1]; + seg.frontsector = Level.Sides[seg.sidedef].sector; + seg.backsector = backside != NO_INDEX ? Level.Sides[backside].sector : -1; + seg.v1 = Level.Lines[i].v1; + seg.v2 = Level.Lines[i].v2; + seg.nextforvert = Vertices[seg.v1].segs; + seg.nextforvert2 = Vertices[seg.v2].segs2; + share1 = CheckSegForDuplicate (&seg); + if (share1 == NULL) + { + seg.angle = PointToAngle (Vertices[seg.v2].x-Vertices[seg.v1].x, + Vertices[seg.v2].y-Vertices[seg.v1].y); + j = (int)Segs.Push (seg); + Vertices[seg.v1].segs = j; + Vertices[seg.v2].segs2 = j; + } + else + { + printf ("Linedefs %d and %d share endpoints.\n", i, share1->linedef); + } + } + else + { + printf ("Linedef %d does not have a front side.\n", i); + } + + if (Level.Lines[i].sidenum[1] != NO_INDEX) + { + WORD backside; + + seg.linedef = i; + seg.sidedef = Level.Lines[i].sidenum[1]; + backside = Level.Lines[i].sidenum[0]; + seg.frontsector = Level.Sides[seg.sidedef].sector; + seg.backsector = backside != NO_INDEX ? Level.Sides[backside].sector : -1; + seg.v1 = Level.Lines[i].v2; + seg.v2 = Level.Lines[i].v1; + seg.nextforvert = Vertices[seg.v1].segs; + seg.nextforvert2 = Vertices[seg.v2].segs2; + seg.angle = PointToAngle (Vertices[seg.v2].x-Vertices[seg.v1].x, + Vertices[seg.v2].y-Vertices[seg.v1].y); + share2 = CheckSegForDuplicate (&seg); + if (share2 == NULL) + { + j = (int)Segs.Push (seg); + Vertices[seg.v1].segs = j; + Vertices[seg.v2].segs2 = j; + + if (Level.Lines[i].sidenum[0] != NO_INDEX && share1 == NULL) + { + Segs[j-1].partner = j; + Segs[j].partner = j-1; + } + } + else if (share2->linedef != share1->linedef) + { + printf ("Linedefs %d and %d share endpoints.\n", i, share2->linedef); + } + } + } +} + +// Check for another seg with the same start and end vertices as this one. +// Combined with its use above, this will find two-sided lines that are shadowed +// by another one- or two-sided line, and it will also find one-sided lines that +// shadow each other. It will not find one-sided lines that share endpoints but +// face opposite directions. Although they should probably be a single two-sided +// line, leaving them in will not generate bad nodes. + +FNodeBuilder::FPrivSeg *FNodeBuilder::CheckSegForDuplicate (const FNodeBuilder::FPrivSeg *check) +{ + DWORD segnum; + + // Check for segs facing the same direction + for (segnum = check->nextforvert; segnum != DWORD_MAX; segnum = Segs[segnum].nextforvert) + { + if (Segs[segnum].v2 == check->v2) + { + return &Segs[segnum]; + } + } + return NULL; +} + +// Group colinear segs together so that only one seg per line needs to be checked +// by SelectSplitter(). + +void FNodeBuilder::GroupSegPlanes () +{ + const int bucketbits = 12; + FPrivSeg *buckets[1<next = i+1; + seg->hashnext = NULL; + } + + Segs[Segs.Size()-1].next = DWORD_MAX; + + for (i = planenum = 0; i < (int)Segs.Size(); ++i) + { + FPrivSeg *seg = &Segs[i]; + fixed_t x1 = Vertices[seg->v1].x; + fixed_t y1 = Vertices[seg->v1].y; + fixed_t x2 = Vertices[seg->v2].x; + fixed_t y2 = Vertices[seg->v2].y; + angle_t ang = PointToAngle (x2 - x1, y2 - y1); + + if (ang >= 1u<<31) + ang += 1u<<31; + + FPrivSeg *check = buckets[ang >>= 31-bucketbits]; + + while (check != NULL) + { + fixed_t cx1 = Vertices[check->v1].x; + fixed_t cy1 = Vertices[check->v1].y; + fixed_t cdx = Vertices[check->v2].x - cx1; + fixed_t cdy = Vertices[check->v2].y - cy1; + if (PointOnSide (x1, y1, cx1, cy1, cdx, cdy) == 0 && + PointOnSide (x2, y2, cx1, cy1, cdx, cdy) == 0) + { + break; + } + check = check->hashnext; + } + if (check != NULL) + { + seg->planenum = check->planenum; + const FSimpleLine *line = &Planes[seg->planenum]; + if (line->dx != 0) + { + if ((line->dx > 0 && x2 > x1) || (line->dx < 0 && x2 < x1)) + { + seg->planefront = true; + } + else + { + seg->planefront = false; + } + } + else + { + if ((line->dy > 0 && y2 > y1) || (line->dy < 0 && y2 < y1)) + { + seg->planefront = true; + } + else + { + seg->planefront = false; + } + } + } + else + { + seg->hashnext = buckets[ang]; + buckets[ang] = seg; + seg->planenum = planenum++; + seg->planefront = true; + + FSimpleLine pline = { Vertices[seg->v1].x, + Vertices[seg->v1].y, + Vertices[seg->v2].x - Vertices[seg->v1].x, + Vertices[seg->v2].y - Vertices[seg->v1].y }; + Planes.Push (pline); + } + } + + D(Printf ("%d planes from %d segs\n", planenum, Segs.Size())); + + planenum = (planenum+7)/8; + PlaneChecked.Reserve (planenum); +} + +// Find "loops" of segs surrounding polyobject's origin. Note that a polyobject's origin +// is not solely defined by the polyobject's anchor, but also by the polyobject itself. +// For the split avoidance to work properly, you must have a convex, complete loop of +// segs surrounding the polyobject origin. All the maps in hexen.wad have complete loops of +// segs around their polyobjects, but they are not all convex: The doors at the start of MAP01 +// and some of the pillars in MAP02 that surround the entrance to MAP06 are not convex. +// Heuristic() uses some special weighting to make these cases work properly. + +void FNodeBuilder::FindPolyContainers (TArray &spots, TArray &anchors) +{ + int loop = 1; + + for (size_t i = 0; i < spots.Size(); ++i) + { + FPolyStart *spot = &spots[i]; + fixed_t bbox[4]; + + if (GetPolyExtents (spot->polynum, bbox)) + { + FPolyStart *anchor; + + size_t j; + + for (j = 0; j < anchors.Size(); ++j) + { + anchor = &anchors[j]; + if (anchor->polynum == spot->polynum) + { + break; + } + } + + if (j < anchors.Size()) + { + vertex_t mid; + vertex_t center; + + mid.x = bbox[BOXLEFT] + (bbox[BOXRIGHT]-bbox[BOXLEFT])/2; + mid.y = bbox[BOXBOTTOM] + (bbox[BOXTOP]-bbox[BOXBOTTOM])/2; + + center.x = mid.x - anchor->x + spot->x; + center.y = mid.y - anchor->y + spot->y; + + // Scan right for the seg closest to the polyobject's center after it + // gets moved to its start spot. + fixed_t closestdist = FIXED_MAX; + DWORD closestseg = 0; + + P(Printf ("start %d,%d -- center %d, %d\n", spot->x>>16, spot->y>>16, center.x>>16, center.y>>16)); + + for (size_t j = 0; j < Segs.Size(); ++j) + { + FPrivSeg *seg = &Segs[j]; + FPrivVert *v1 = &Vertices[seg->v1]; + FPrivVert *v2 = &Vertices[seg->v2]; + fixed_t dy = v2->y - v1->y; + + if (dy == 0) + { // Horizontal, so skip it + continue; + } + if ((v1->y < center.y && v2->y < center.y) || (v1->y > center.y && v2->y > center.y)) + { // Not crossed + continue; + } + + fixed_t dx = v2->x - v1->x; + + if (PointOnSide (center.x, center.y, v1->x, v1->y, dx, dy) <= 0) + { + fixed_t t = DivScale30 (center.y - v1->y, dy); + fixed_t sx = v1->x + MulScale30 (dx, t); + fixed_t dist = sx - spot->x; + + if (dist < closestdist && dist >= 0) + { + closestdist = dist; + closestseg = (long)j; + } + } + } + if (closestseg >= 0) + { + loop = MarkLoop (closestseg, loop); + P(Printf ("Found polyobj in sector %d (loop %d)\n", Segs[closestseg].frontsector, + Segs[closestseg].loopnum)); + } + } + } + } +} + +int FNodeBuilder::MarkLoop (DWORD firstseg, int loopnum) +{ + int seg; + int sec = Segs[firstseg].frontsector; + + if (Segs[firstseg].loopnum != 0) + { // already marked + return loopnum; + } + + seg = firstseg; + + do + { + FPrivSeg *s1 = &Segs[seg]; + + s1->loopnum = loopnum; + + P(Printf ("Mark seg %d (%d,%d)-(%d,%d)\n", seg, + Vertices[s1->v1].x>>16, Vertices[s1->v1].y>>16, + Vertices[s1->v2].x>>16, Vertices[s1->v2].y>>16)); + + DWORD bestseg = DWORD_MAX; + DWORD tryseg = Vertices[s1->v2].segs; + angle_t bestang = ANGLE_MAX; + angle_t ang1 = s1->angle; + + while (tryseg != DWORD_MAX) + { + FPrivSeg *s2 = &Segs[tryseg]; + + if (s2->frontsector == sec) + { + angle_t ang2 = s2->angle + ANGLE_180; + angle_t angdiff = ang2 - ang1; + + if (angdiff < bestang && angdiff > 0) + { + bestang = angdiff; + bestseg = tryseg; + } + } + tryseg = s2->nextforvert; + } + + seg = bestseg; + } while (seg != DWORD_MAX && Segs[seg].loopnum == 0); + + return loopnum + 1; +} + +// Find the bounding box for a specific polyobject. + +bool FNodeBuilder::GetPolyExtents (int polynum, fixed_t bbox[4]) +{ + size_t i; + + bbox[BOXLEFT] = bbox[BOXBOTTOM] = FIXED_MAX; + bbox[BOXRIGHT] = bbox[BOXTOP] = FIXED_MIN; + + // Try to find a polyobj marked with a start line + for (i = 0; i < Segs.Size(); ++i) + { + if (Level.Lines[Segs[i].linedef].special == PO_LINE_START && + Level.Lines[Segs[i].linedef].args[0] == polynum) + { + break; + } + } + + if (i < Segs.Size()) + { + vertex_t start; + size_t vert; + + vert = Segs[i].v1; + + start.x = Vertices[vert].x; + start.y = Vertices[vert].y; + + do + { + AddSegToBBox (bbox, &Segs[i]); + vert = Segs[i].v2; + i = Vertices[vert].segs; + } while (i != DWORD_MAX && (Vertices[vert].x != start.x || Vertices[vert].y != start.y)); + + return true; + } + + // Try to find a polyobj marked with explicit lines + bool found = false; + + for (i = 0; i < Segs.Size(); ++i) + { + if (Level.Lines[Segs[i].linedef].special == PO_LINE_EXPLICIT && + Level.Lines[Segs[i].linedef].args[0] == polynum) + { + AddSegToBBox (bbox, &Segs[i]); + found = true; + } + } + return found; +} + +void FNodeBuilder::AddSegToBBox (fixed_t bbox[4], const FPrivSeg *seg) +{ + FPrivVert *v1 = &Vertices[seg->v1]; + FPrivVert *v2 = &Vertices[seg->v2]; + + if (v1->x < bbox[BOXLEFT]) bbox[BOXLEFT] = v1->x; + if (v1->x > bbox[BOXRIGHT]) bbox[BOXRIGHT] = v1->x; + if (v1->y < bbox[BOXBOTTOM]) bbox[BOXBOTTOM] = v1->y; + if (v1->y > bbox[BOXTOP]) bbox[BOXTOP] = v1->y; + + if (v2->x < bbox[BOXLEFT]) bbox[BOXLEFT] = v2->x; + if (v2->x > bbox[BOXRIGHT]) bbox[BOXRIGHT] = v2->x; + if (v2->y < bbox[BOXBOTTOM]) bbox[BOXBOTTOM] = v2->y; + if (v2->y > bbox[BOXTOP]) bbox[BOXTOP] = v2->y; +} diff --git a/poly_bad.png b/poly_bad.png new file mode 100644 index 0000000000000000000000000000000000000000..3797ad8a4e83003ba969d3a43f9f30da496b08af GIT binary patch literal 471 zcmV;|0Vw{7P)ZksHmu@kdTn`XxC8y00DPNL_t(|UhUYyN&_(zhS7og0zbXb zy##UDAsd&y;i69OL>cOS(46^Ykg~tKU7xsJ;dT{ZcK}H_Mdwj_rT&!o%hxudrk?`@8rL62udvA-(`dE#`>pGc2KM)c3KI6m`S z%==-^y}0-7&ppJQ7uk6f<8gi-oZd6Y&?CLa#oOn!oR literal 0 HcmV?d00001 diff --git a/poly_mov.png b/poly_mov.png new file mode 100644 index 0000000000000000000000000000000000000000..2264d8d7961aba41199f680f431a60e3ab9ab505 GIT binary patch literal 458 zcmV;*0X6=KP)vn;u*m&mh*zUiPNRULkvw#$#1p z_OkaF;=TXy;8J)7Yj~$~&GAy(wx7q@+7D^vx!ZL8R-dJn?yKL6e}7ZYc<)2KZtK+R zCS2V4eAnPniuXm!28U7j<8QzSTT9?Ox1# z=KJ$Vk!Ns!9$aG2hI{reJPP+Xt!FUj!D;b$-Xqh5c(s!}7j`~_c@IiFmON77)sQ?i zi9LG|J$c1WJU`9`H5LlOs8Ss4F|HB^y1eXt0nhF)5Fp(iLjV8(07*qoM6N<$f*!Hj Ar2qf` literal 0 HcmV?d00001 diff --git a/poly_new.png b/poly_new.png new file mode 100644 index 0000000000000000000000000000000000000000..cbf52e3d9079899b32c1d1d81c49c42bffaf8a2f GIT binary patch literal 457 zcmV;)0XF`LP)_|2Tu&Qq03silWv<&Y~?nL9ypIDsKgUk1)jxs^}sni zK{cMt;nDTRe6M@42bOz+iaaUz_|6_!>Itgyq}1c+oreBjhW_0De%;MK^WE?J%Xj|` z&g|>M7|lCO_YUR>?s#$xtnWiTPZ_&j)jOVu#_yk!Hy&+oXg%sB%eF4JUfJ*abPp`? z1l4&`;;}vtjC+C#J&Ah+ng>QbL6x3FJx=jf$tw2B*9XQtL8YF=JT`Ca81X()trxm~ z^u4s5?yb`6JPBRz)4b;CbWfLjz0Esi^=Uu-{lPi)8PWa^9ItuJYo6+1ny328=STCJ z*SygFe*56_T=P<>T<8Q@Yj?6i-p-vY%WK{rUo~}+k>(ZQ00000NkvXXu0mjflz8P1 literal 0 HcmV?d00001 diff --git a/processor.cpp b/processor.cpp new file mode 100644 index 0000000..6455509 --- /dev/null +++ b/processor.cpp @@ -0,0 +1,1268 @@ +/* + Reads wad files, builds nodes, and saves new wad files. + Copyright (C) 2002,2003 Randy Heit + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "processor.h" +//#include "rejectbuilder.h" + +extern void ShowView (FLevel *level); + +enum +{ + // Thing numbers used in Hexen maps + PO_HEX_ANCHOR_TYPE = 3000, + PO_HEX_SPAWN_TYPE, + PO_HEX_SPAWNCRUSH_TYPE, + + // Thing numbers used in Doom and Heretic maps + PO_ANCHOR_TYPE = 9300, + PO_SPAWN_TYPE, + PO_SPAWNCRUSH_TYPE +}; + +FLevel::FLevel () +{ + memset (this, 0, sizeof(*this)); +} + +FLevel::~FLevel () +{ + if (Things) delete[] Things; + if (Lines) delete[] Lines; + if (Vertices) delete[] Vertices; + if (Sides) delete[] Sides; + if (Sectors) delete[] Sectors; + if (Subsectors) delete[] Subsectors; + if (Segs) delete[] Segs; + if (Nodes) delete[] Nodes; + if (Blockmap) delete[] Blockmap; + if (Reject) delete[] Reject; + if (GLSubsectors) delete[] GLSubsectors; + if (GLSegs) delete[] GLSegs; + if (GLNodes) delete[] GLNodes; + if (GLPVS) delete[] GLPVS; +} + +FProcessor::FProcessor (FWadReader &inwad, int lump) +: + Wad (inwad), Lump (lump) +{ + printf ("----%s----\n", Wad.LumpName (Lump)); + + Extended = Wad.MapHasBehavior (lump); + LoadThings (); + LoadVertices (); + LoadLines (); + LoadSides (); + LoadSectors (); + + if (Level.NumLines == 0 || Level.NumVertices == 0 || Level.NumSides == 0 || Level.NumSectors == 0) + { + printf (" Map is incomplete\n"); + } + else + { + // Removing extra vertices is done by the node builder. + Level.RemoveExtraLines (); + if (!NoPrune) + { + Level.RemoveExtraSides (); + Level.RemoveExtraSectors (); + } + + if (BuildNodes) + { + GetPolySpots (); + } + } +} + +void FProcessor::LoadThings () +{ + if (Extended) + { + ReadMapLump (Wad, "THINGS", Lump, Level.Things, Level.NumThings); + + for (int i = 0; i < Level.NumThings; ++i) + { + Level.Things[i].x = SHORT(Level.Things[i].x); + Level.Things[i].y = SHORT(Level.Things[i].y); + Level.Things[i].angle = SHORT(Level.Things[i].angle); + Level.Things[i].type = SHORT(Level.Things[i].type); + Level.Things[i].flags = SHORT(Level.Things[i].flags); + } + } + else + { + MapThing *mt; + ReadMapLump (Wad, "THINGS", Lump, mt, Level.NumThings); + + Level.Things = new MapThing2[Level.NumThings]; + memset (Level.Things, 0, sizeof(*Level.Things)*Level.NumThings); + for (int i = 0; i < Level.NumThings; ++i) + { + Level.Things[i].x = SHORT(mt[i].x); + Level.Things[i].y = SHORT(mt[i].y); + Level.Things[i].angle = SHORT(mt[i].angle); + Level.Things[i].type = SHORT(mt[i].type); + Level.Things[i].flags = SHORT(mt[i].flags); + } + delete[] mt; + } +} + +void FProcessor::LoadLines () +{ + if (Extended) + { + ReadMapLump (Wad, "LINEDEFS", Lump, Level.Lines, Level.NumLines); + + for (int i = 0; i < Level.NumLines; ++i) + { + Level.Lines[i].v1 = SHORT(Level.Lines[i].v1); + Level.Lines[i].v2 = SHORT(Level.Lines[i].v2); + Level.Lines[i].flags = SHORT(Level.Lines[i].flags); + Level.Lines[i].sidenum[0] = SHORT(Level.Lines[i].sidenum[0]); + Level.Lines[i].sidenum[1] = SHORT(Level.Lines[i].sidenum[1]); + } + } + else + { + MapLineDef *ml; + ReadMapLump (Wad, "LINEDEFS", Lump, ml, Level.NumLines); + + Level.Lines = new MapLineDef2[Level.NumLines]; + memset (Level.Lines, 0, sizeof(*Level.Lines)*Level.NumLines); + for (int i = 0; i < Level.NumLines; ++i) + { + Level.Lines[i].v1 = SHORT(ml[i].v1); + Level.Lines[i].v2 = SHORT(ml[i].v2); + Level.Lines[i].flags = SHORT(ml[i].flags); + Level.Lines[i].sidenum[0] = SHORT(ml[i].sidenum[0]); + Level.Lines[i].sidenum[1] = SHORT(ml[i].sidenum[1]); + + // Store the special and tag in the args array so we don't lose them + short t = SHORT(ml[i].special); + Level.Lines[i].args[2] = t & 255; + Level.Lines[i].args[3] = t >> 8; + t = SHORT(ml[i].tag); + Level.Lines[i].args[0] = t & 255; + Level.Lines[i].args[1] = t >> 8; + } + delete[] ml; + } +} + +void FProcessor::LoadVertices () +{ + MapVertex *verts; + ReadMapLump (Wad, "VERTEXES", Lump, verts, Level.NumVertices); + + Level.Vertices = new WideVertex[Level.NumVertices]; + + for (int i = 0; i < Level.NumVertices; ++i) + { + Level.Vertices[i].x = SHORT(verts[i].x) << FRACBITS; + Level.Vertices[i].y = SHORT(verts[i].y) << FRACBITS; + } +} + +void FProcessor::LoadSides () +{ + ReadMapLump (Wad, "SIDEDEFS", Lump, Level.Sides, Level.NumSides); + + for (int i = 0; i < Level.NumSides; ++i) + { + Level.Sides[i].sector = SHORT(Level.Sides[i].sector); + } +} + +void FProcessor::LoadSectors () +{ + ReadMapLump (Wad, "SECTORS", Lump, Level.Sectors, Level.NumSectors); +} + +void FLevel::RemoveExtraLines () +{ + int i, newNumLines; + + // Extra lines are those with 0 length. Collision detection against + // one of those could cause a divide by 0, so it's best to remove them. + + for (i = newNumLines = 0; i < NumLines; ++i) + { + if (Vertices[Lines[i].v1].x != Vertices[Lines[i].v2].x || + Vertices[Lines[i].v1].y != Vertices[Lines[i].v2].y) + { + if (i != newNumLines) + { + Lines[newNumLines] = Lines[i]; + } + ++newNumLines; + } + } + if (newNumLines < NumLines) + { + int diff = NumLines - newNumLines; + + printf (" Removed %d line%s with 0 length.\n", diff, diff > 1 ? "s" : ""); + } + NumLines = newNumLines; +} + +void FLevel::RemoveExtraSides () +{ + BYTE *used; + WORD *remap; + int i, newNumSides; + + // Extra sides are those that aren't referenced by any lines. + // They just waste space, so get rid of them. + + used = new BYTE[NumSides]; + memset (used, 0, NumSides*sizeof(*used)); + remap = new WORD[NumSides]; + + // Mark all used sides + for (i = 0; i < NumLines; ++i) + { + if (Lines[i].sidenum[0] != NO_INDEX) + { + used[Lines[i].sidenum[0]] = 1; + } + else + { + printf (" Line %d needs a front sidedef before it will run with ZDoom.\n", i); + } + if (Lines[i].sidenum[1] != NO_INDEX) + { + used[Lines[i].sidenum[1]] = 1; + } + } + + // Shift out any unused sides + for (i = newNumSides = 0; i < NumSides; ++i) + { + if (used[i]) + { + if (i != newNumSides) + { + Sides[newNumSides] = Sides[i]; + } + remap[i] = newNumSides++; + } + else + { + remap[i] = NO_INDEX; + } + } + + if (newNumSides < NumSides) + { + int diff = NumSides - newNumSides; + + printf (" Removed %d unused sidedef%s.\n", diff, diff > 1 ? "s" : ""); + NumSides = newNumSides; + + // Renumber side references in lines + for (i = 0; i < NumLines; ++i) + { + if (Lines[i].sidenum[0] != NO_INDEX) + { + Lines[i].sidenum[0] = remap[Lines[i].sidenum[0]]; + } + if (Lines[i].sidenum[1] != NO_INDEX) + { + Lines[i].sidenum[1] = remap[Lines[i].sidenum[1]]; + } + } + } + + delete[] used; + delete[] remap; +} + +void FLevel::RemoveExtraSectors () +{ + BYTE *used; + WORD *remap; + int i, newNumSectors; + + // Extra sectors are those that aren't referenced by any sides. + // They just waste space, so get rid of them. + + used = new BYTE[NumSectors]; + memset (used, 0, NumSectors*sizeof(*used)); + remap = new WORD[NumSectors]; + + // Mark all used sectors + for (i = 0; i < NumSides; ++i) + { + if (Sides[i].sector != NO_INDEX) + { + used[Sides[i].sector] = 1; + } + else + { + printf (" Sidedef %d needs a front sector before it will run with ZDoom.\n", i); + } + } + + // Shift out any unused sides + for (i = newNumSectors = 0; i < NumSectors; ++i) + { + if (used[i]) + { + if (i != newNumSectors) + { + Sectors[newNumSectors] = Sectors[i]; + } + remap[i] = newNumSectors++; + } + else + { + remap[i] = NO_INDEX; + } + } + + if (newNumSectors < NumSectors) + { + int diff = NumSectors - newNumSectors; + printf (" Removed %d unused sector%s.\n", diff, diff > 1 ? "s" : ""); + NumSectors = newNumSectors; + + // Renumber sector references in sides + for (i = 0; i < NumSides; ++i) + { + if (Sides[i].sector != NO_INDEX) + { + Sides[i].sector = remap[Sides[i].sector]; + } + } + } + + delete[] used; + delete[] remap; +} + +void FProcessor::GetPolySpots () +{ + if (Extended && CheckPolyobjs) + { + int spot1, spot2, anchor, i; + + // Determine if this is a Hexen map by looking for things of type 3000 + // Only Hexen maps use them, and they are the polyobject anchors + for (i = 0; i < Level.NumThings; ++i) + { + if (Level.Things[i].type == PO_HEX_ANCHOR_TYPE) + { + break; + } + } + + if (i < Level.NumThings) + { + spot1 = PO_HEX_SPAWN_TYPE; + spot2 = PO_HEX_SPAWNCRUSH_TYPE; + anchor = PO_HEX_ANCHOR_TYPE; + } + else + { + spot1 = PO_SPAWN_TYPE; + spot2 = PO_SPAWNCRUSH_TYPE; + anchor = PO_ANCHOR_TYPE; + } + + for (i = 0; i < Level.NumThings; ++i) + { + if (Level.Things[i].type == spot1 || + Level.Things[i].type == spot2 || + Level.Things[i].type == anchor) + { + FNodeBuilder::FPolyStart newvert; + newvert.x = Level.Things[i].x << FRACBITS; + newvert.y = Level.Things[i].y << FRACBITS; + newvert.polynum = Level.Things[i].angle; + if (Level.Things[i].type == anchor) + { + PolyAnchors.Push (newvert); + } + else + { + PolyStarts.Push (newvert); + } + } + } + } +} + +void FProcessor::Write (FWadWriter &out) +{ + if (Level.NumLines == 0 || Level.NumSides == 0 || Level.NumSectors == 0 || Level.NumVertices == 0) + { + // Map is empty, so just copy it as-is + out.CopyLump (Wad, Lump); + out.CopyLump (Wad, Wad.FindMapLump ("THINGS", Lump)); + out.CopyLump (Wad, Wad.FindMapLump ("LINEDEFS", Lump)); + out.CopyLump (Wad, Wad.FindMapLump ("SIDEDEFS", Lump)); + out.CopyLump (Wad, Wad.FindMapLump ("VERTEXES", Lump)); + out.CreateLabel ("SEGS"); + out.CreateLabel ("SSECTORS"); + out.CreateLabel ("NODES"); + out.CopyLump (Wad, Wad.FindMapLump ("SECTORS", Lump)); + out.CreateLabel ("REJECT"); + out.CreateLabel ("BLOCKMAP"); + if (Extended) + { + out.CopyLump (Wad, Wad.FindMapLump ("BEHAVIOR", Lump)); + out.CopyLump (Wad, Wad.FindMapLump ("SCRIPTS", Lump)); + } + return; + } + + bool compress, compressGL; + +#ifdef BLOCK_TEST + int size; + BYTE *blockmap; + ReadLump (Wad, Wad.FindMapLump ("BLOCKMAP", Lump), blockmap, size); + if (blockmap) + { + FILE *f = fopen ("blockmap.lmp", "wb"); + if (f) + { + fwrite (blockmap, 1, size, f); + fclose (f); + } + delete[] blockmap; + } +#endif + + if (BuildNodes) + { + FNodeBuilder *builder = NULL; + + try + { + builder = new FNodeBuilder (Level, PolyStarts, PolyAnchors, Wad.LumpName (Lump), BuildGLNodes); + if (builder == NULL) + { + throw exception(" Not enough memory to build nodes!"); + } + + delete[] Level.Vertices; + builder->GetVertices (Level.Vertices, Level.NumVertices); + + if (ConformNodes) + { + // When the nodes are "conformed", the normal and GL nodes use the same + // basic information. This creates normal nodes that are less "good" than + // possible, but it makes it easier to compare the two sets of nodes to + // determine the correctness of the GL nodes. + builder->GetNodes (Level.Nodes, Level.NumNodes, + Level.Segs, Level.NumSegs, + Level.Subsectors, Level.NumSubsectors); + builder->GetVertices (Level.GLVertices, Level.NumGLVertices); + builder->GetGLNodes (Level.GLNodes, Level.NumGLNodes, + Level.GLSegs, Level.NumGLSegs, + Level.GLSubsectors, Level.NumGLSubsectors); + } + else + { + if (BuildGLNodes) + { + builder->GetVertices (Level.GLVertices, Level.NumGLVertices); + builder->GetGLNodes (Level.GLNodes, Level.NumGLNodes, + Level.GLSegs, Level.NumGLSegs, + Level.GLSubsectors, Level.NumGLSubsectors); + + if (!GLOnly) + { + // Now repeat the process to obtain regular nodes + delete builder; + builder = new FNodeBuilder (Level, PolyStarts, PolyAnchors, Wad.LumpName (Lump), false); + if (builder == NULL) + { + throw exception(" Not enough memory to build regular nodes!"); + } + delete[] Level.Vertices; + builder->GetVertices (Level.Vertices, Level.NumVertices); + } + } + if (!GLOnly) + { + builder->GetNodes (Level.Nodes, Level.NumNodes, + Level.Segs, Level.NumSegs, + Level.Subsectors, Level.NumSubsectors); + } + } + delete builder; + builder = NULL; + } + catch (...) + { + if (builder != NULL) + { + delete builder; + } + throw; + } + } + + FBlockmapBuilder bbuilder (Level); + WORD *blocks = bbuilder.GetBlockmap (Level.BlockmapSize); + Level.Blockmap = new WORD[Level.BlockmapSize]; + memcpy (Level.Blockmap, blocks, Level.BlockmapSize*sizeof(WORD)); + + Level.RejectSize = (Level.NumSectors*Level.NumSectors + 7) / 8; + Level.Reject = NULL; + + switch (RejectMode) + { + case ERM_Rebuild: + //FRejectBuilder reject (Level); + //Level.Reject = reject.GetReject (); + printf (" Rebuilding the reject is unsupported.\n"); + // Intentional fall-through + + case ERM_DontTouch: + { + int lump = Wad.FindMapLump ("REJECT", Lump); + + if (lump >= 0) + { + ReadLump (Wad, lump, Level.Reject, Level.RejectSize); + if (Level.RejectSize != (Level.NumSectors*Level.NumSectors + 7) / 8) + { + // If the reject is the wrong size, don't use it. + delete[] Level.Reject; + Level.Reject = NULL; + if (Level.RejectSize != 0) + { // Do not warn about 0-length rejects + printf (" REJECT is the wrong size, so it will be removed.\n"); + } + Level.RejectSize = 0; + } + } + } + break; + + case ERM_Create0: + break; + + case ERM_CreateZeroes: + Level.Reject = new BYTE[Level.RejectSize]; + memset (Level.Reject, 0, Level.RejectSize); + break; + } + + if (ShowMap) + { + ShowView (&Level); + } + + if (Level.GLNodes != NULL ) + { + compressGL = CompressGLNodes || + (Level.NumVertices > 32767) || + (Level.NumGLVertices > 32767) || + (Level.NumGLSegs > 65535) || + (Level.NumGLNodes > 32767) || + (Level.NumGLSubsectors > 32767); + } + else + { + compressGL = false; + } + + // If the GL nodes are compressed, then the regular nodes must also be compressed. + compress = CompressNodes || compressGL || + (Level.NumVertices > 65535) || + (Level.NumSegs > 65535) || + (Level.NumSubsectors > 32767) || + (Level.NumNodes > 32767); + + out.CopyLump (Wad, Lump); + out.CopyLump (Wad, Wad.FindMapLump ("THINGS", Lump)); + WriteLines (out); + WriteSides (out); + WriteVertices (out, compress || GLOnly ? Level.NumOrgVerts : Level.NumVertices); + if (BuildNodes) + { + if (!compress) + { + if (!GLOnly) + { + WriteSegs (out); + WriteSSectors (out); + WriteNodes (out); + } + else + { + out.CreateLabel ("SEGS"); + out.CreateLabel ("SSECTORS"); + out.CreateLabel ("NODES"); + } + } + else + { + out.CreateLabel ("SEGS"); + if (compressGL) + { + WriteGLBSPZ (out, "SSECTORS"); + } + else + { + out.CreateLabel ("SSECTORS"); + } + if (!GLOnly) + { + WriteBSPZ (out, "NODES"); + } + else + { + out.CreateLabel ("NODES"); + } + } + } + else + { + out.CopyLump (Wad, Wad.FindMapLump ("SEGS", Lump)); + out.CopyLump (Wad, Wad.FindMapLump ("SSECTORS", Lump)); + out.CopyLump (Wad, Wad.FindMapLump ("NODES", Lump)); + } + WriteSectors (out); + WriteReject (out); + WriteBlockmap (out); + if (Extended) + { + out.CopyLump (Wad, Wad.FindMapLump ("BEHAVIOR", Lump)); + out.CopyLump (Wad, Wad.FindMapLump ("SCRIPTS", Lump)); + } + if (Level.GLNodes != NULL && !compressGL) + { + char glname[9]; + glname[0] = 'G'; + glname[1] = 'L'; + glname[2] = '_'; + glname[8] = 0; + strncpy (glname+3, Wad.LumpName (Lump), 5); + out.CreateLabel (glname); + WriteGLVertices (out); + WriteGLSegs (out); + WriteGLSSect (out); + WriteGLNodes (out); + } +} + +MapNodeEx *FProcessor::NodesToEx (const MapNode *nodes, int count) +{ + if (count == 0) + { + return NULL; + } + + MapNodeEx *Nodes = new MapNodeEx[Level.NumNodes]; + int x; + + for (x = 0; x < count; ++x) + { + WORD child; + int i; + + for (i = 0; i < 4+2*4; ++i) + { + *((WORD *)&Nodes[x] + i) = SHORT(*((WORD *)&nodes[x] + i)); + } + for (i = 0; i < 2; ++i) + { + child = SHORT(nodes[x].children[i]); + if (child & NF_SUBSECTOR) + { + Nodes[x].children[i] = child + (NFX_SUBSECTOR - NF_SUBSECTOR); + } + else + { + Nodes[x].children[i] = child; + } + } + } + return Nodes; +} + +MapSubsectorEx *FProcessor::SubsectorsToEx (const MapSubsector *ssec, int count) +{ + if (count == 0) + { + return NULL; + } + + MapSubsectorEx *out = new MapSubsectorEx[Level.NumSubsectors]; + int x; + + for (x = 0; x < count; ++x) + { + out[x].numlines = SHORT(ssec[x].numlines); + out[x].firstline = SHORT(ssec[x].firstline); + } + + return out; +} + +MapSegGLEx *FProcessor::SegGLsToEx (const MapSegGL *segs, int count) +{ + if (count == 0) + { + return NULL; + } + + MapSegGLEx *out = new MapSegGLEx[count]; + int x; + + for (x = 0; x < count; ++x) + { + out[x].v1 = SHORT(segs[x].v1); + out[x].v2 = SHORT(segs[x].v2); + out[x].linedef = SHORT(segs[x].linedef); + out[x].side = SHORT(segs[x].side); + out[x].partner = SHORT(segs[x].partner); + } + + return out; +} + +void FProcessor::WriteVertices (FWadWriter &out, int count) +{ + int i; + fixed_t *vertdata = (fixed_t *)Level.Vertices; + + count *= 2; + short *verts = new short[count]; + + for (i = 0; i < count; ++i) + { + verts[i] = SHORT(vertdata[i] >> FRACBITS); + } + out.WriteLump ("VERTEXES", verts, sizeof(*verts)*count); + delete[] verts; + + if (count >= 65536) + { + printf (" VERTEXES is past the normal limit. (%d vertices)\n", count/2); + } +} + +void FProcessor::WriteLines (FWadWriter &out) +{ + int i; + + if (Extended) + { + for (i = 0; i < Level.NumLines; ++i) + { + Level.Lines[i].v1 = SHORT(Level.Lines[i].v1); + Level.Lines[i].v2 = SHORT(Level.Lines[i].v2); + Level.Lines[i].flags = SHORT(Level.Lines[i].flags); + Level.Lines[i].sidenum[0] = SHORT(Level.Lines[i].sidenum[0]); + Level.Lines[i].sidenum[1] = SHORT(Level.Lines[i].sidenum[1]); + } + out.WriteLump ("LINEDEFS", Level.Lines, Level.NumLines*sizeof(*Level.Lines)); + } + else + { + MapLineDef *ld = new MapLineDef[Level.NumLines]; + + for (i = 0; i < Level.NumLines; ++i) + { + short t; + + ld[i].v1 = SHORT(Level.Lines[i].v1); + ld[i].v2 = SHORT(Level.Lines[i].v2); + ld[i].flags = SHORT(Level.Lines[i].flags); + ld[i].sidenum[0] = SHORT(Level.Lines[i].sidenum[0]); + ld[i].sidenum[1] = SHORT(Level.Lines[i].sidenum[1]); + + t = Level.Lines[i].args[2] + (Level.Lines[i].args[3]<<8); + ld[i].special = SHORT(t); + + t = Level.Lines[i].args[0] + (Level.Lines[i].args[1]<<8); + ld[i].tag = SHORT(t); + } + out.WriteLump ("LINEDEFS", ld, Level.NumLines*sizeof(*ld)); + delete[] ld; + } +} + +void FProcessor::WriteSides (FWadWriter &out) +{ + int i; + + for (i = 0; i < Level.NumSides; ++i) + { + Level.Sides[i].sector = SHORT(Level.Sides[i].sector); + } + out.WriteLump ("SIDEDEFS", Level.Sides, Level.NumSides*sizeof(*Level.Sides)); +} + +void FProcessor::WriteSectors (FWadWriter &out) +{ + out.WriteLump ("SECTORS", Level.Sectors, Level.NumSectors*sizeof(*Level.Sectors)); +} + +void FProcessor::WriteSegs (FWadWriter &out) +{ + int i, count; + short *segdata; + + segdata = (short *)Level.Segs; + count = Level.NumSegs*sizeof(MapSeg)/sizeof(*segdata); + + for (i = 0; i < count; ++i) + { + segdata[i] = SHORT(segdata[i]); + } + out.WriteLump ("SEGS", segdata, sizeof(*segdata)*count); + + count /= sizeof(MapSeg)/sizeof(*segdata); + if (count >= 65536) + { + printf (" SEGS is too big for any port. (%d segs)\n", count); + } + else if (count >= 32768) + { + printf (" SEGS is too big for vanilla Doom and most ports. (%d segs)\n", count); + } +} + +void FProcessor::WriteSSectors (FWadWriter &out) const +{ + WriteSSectors2 (out, "SSECTORS", Level.Subsectors, Level.NumSubsectors); +} + +void FProcessor::WriteSSectors2 (FWadWriter &out, const char *name, const MapSubsectorEx *subs, int count) const +{ + int i; + MapSubsector *ssec; + + ssec = new MapSubsector[count]; + + for (i = 0; i < count; ++i) + { + ssec[i].firstline = SHORT((WORD)subs[i].firstline); + ssec[i].numlines = SHORT((WORD)subs[i].numlines); + } + out.WriteLump (name, ssec, sizeof(*ssec)*count); + FILE *f = fopen (name, "wb"); + if (f) + { + fwrite (ssec, count, sizeof(*ssec), f); + fclose (f); + } + delete[] ssec; + + if (count >= 65536) + { + printf (" %s is too big. (%d subsectors)\n", name, count); + } +} + +void FProcessor::WriteNodes (FWadWriter &out) const +{ + WriteNodes2 (out, "NODES", Level.Nodes, Level.NumNodes); +} + +void FProcessor::WriteNodes2 (FWadWriter &out, const char *name, const MapNodeEx *zaNodes, int count) const +{ + int i, j; + short *onodes, *nodes; + + nodes = onodes = new short[count * sizeof(MapNode)/2]; + + for (i = 0; i < count; ++i) + { + short *inodes = (short *)&zaNodes[i]; + for (j = 0; j < 4+2*4; ++j) + { + nodes[j] = SHORT(inodes[j]); + } + nodes += j; + for (j = 0; j < 2; ++j) + { + DWORD child = zaNodes[i].children[j]; + if (child & NFX_SUBSECTOR) + { + *nodes++ = SHORT(WORD(child - (NFX_SUBSECTOR + NF_SUBSECTOR))); + } + else + { + *nodes++ = SHORT((WORD)child); + } + } + } + out.WriteLump (name, onodes, count * sizeof(MapNode)); + delete[] onodes; + + if (count >= 32768) + { + printf (" %s is too big. (%d nodes)\n", name, count); + } +} + +void FProcessor::WriteBlockmap (FWadWriter &out) +{ + if (BlockmapMode == EBM_Create0) + { + out.CreateLabel ("BLOCKMAP"); + return; + } + + size_t i, count; + WORD *blocks; + + count = Level.BlockmapSize; + blocks = Level.Blockmap; + + for (i = 0; i < count; ++i) + { + blocks[i] = SHORT(blocks[i]); + } + out.WriteLump ("BLOCKMAP", blocks, sizeof(*blocks)*count); + +#ifdef BLOCK_TEST + FILE *f = fopen ("blockmap.lm2", "wb"); + if (f) + { + fwrite (blocks, count, sizeof(*blocks), f); + fclose (f); + } +#endif + + for (i = 0; i < count; ++i) + { + blocks[i] = SHORT(blocks[i]); + } + + if (count >= 65536) + { + printf (" BLOCKMAP is so big that ports will have to recreate it.\n" + " Vanilla Doom cannot handle it at all. If this map is for ZDoom 2+,\n" + " you should use the -b switch to save space in the wad.\n"); + } + else if (count >= 32768) + { + printf (" BLOCKMAP is too big for vanilla Doom.\n"); + } +} + +void FProcessor::WriteReject (FWadWriter &out) +{ + if (RejectMode == ERM_Create0 || Level.Reject == NULL) + { + out.CreateLabel ("REJECT"); + } + else + { + out.WriteLump ("REJECT", Level.Reject, Level.RejectSize); + } +} + +void FProcessor::WriteGLVertices (FWadWriter &out) +{ + int i, count = (Level.NumGLVertices - Level.NumOrgVerts) * 2; + fixed_t *vertdata = (fixed_t *)Level.GLVertices + Level.NumOrgVerts * 2; + + fixed_t *verts = new fixed_t[count+1]; + char *magic = (char *)verts; + magic[0] = 'g'; + magic[1] = 'N'; + magic[2] = 'd'; + magic[3] = '2'; + + for (i = 0; i < count; ++i) + { + verts[i+1] = LONG(vertdata[i]); + } + out.WriteLump ("GL_VERT", verts, sizeof(*verts)*(count+1)); + delete[] verts; + + if (count > 65536) + { + printf (" GL_VERT is too big. (%d GL vertices)\n", count/2); + } +} + +void FProcessor::WriteGLSegs (FWadWriter &out) +{ + int i, count; + MapSegGL *segdata; + + count = Level.NumGLSegs; + segdata = new MapSegGL[count]; + + for (i = 0; i < count; ++i) + { + if (Level.GLSegs[i].v1 < Level.NumOrgVerts) + { + segdata[i].v1 = SHORT(Level.GLSegs[i].v1); + } + else + { + segdata[i].v1 = SHORT(0x8000 | (Level.GLSegs[i].v1 - Level.NumOrgVerts)); + } + if (Level.GLSegs[i].v2 < Level.NumOrgVerts) + { + segdata[i].v2 = SHORT(Level.GLSegs[i].v2); + } + else + { + segdata[i].v2 = SHORT(0x8000 | (Level.GLSegs[i].v2 - Level.NumOrgVerts)); + } + segdata[i].linedef = SHORT(Level.GLSegs[i].linedef); + segdata[i].side = SHORT(Level.GLSegs[i].side); + segdata[i].partner = SHORT((WORD)Level.GLSegs[i].partner); + } + out.WriteLump ("GL_SEGS", segdata, sizeof(MapSegGL)*count); + + if (count >= 65536) + { + printf (" GL_SEGS is too big for any port. (%d GL segs)\n", count); + } + else if (count >= 32768) + { + printf (" GL_SEGS is too big for some ports. (%d GL segs)\n", count); + } +} + +void FProcessor::WriteGLSSect (FWadWriter &out) +{ + WriteSSectors2 (out, "GL_SSECT", Level.GLSubsectors, Level.NumGLSubsectors); +} + +void FProcessor::WriteGLNodes (FWadWriter &out) +{ + WriteNodes2 (out, "GL_NODES", Level.GLNodes, Level.NumGLNodes); +} + +void FProcessor::WriteBSPZ (FWadWriter &out, const char *label) +{ + ZLibOut zout (out); + + if (!CompressNodes) + { + printf (" Nodes are so big that compression has been forced.\n"); + } + + out.StartWritingLump (label); + out.AddToLump ("ZNOD", 4); + WriteVerticesZ (zout, &Level.Vertices[Level.NumOrgVerts], Level.NumOrgVerts, Level.NumVertices - Level.NumOrgVerts); + WriteSubsectorsZ (zout, Level.Subsectors, Level.NumSubsectors); + WriteSegsZ (zout, Level.Segs, Level.NumSegs); + WriteNodesZ (zout, Level.Nodes, Level.NumNodes); +} + +void FProcessor::WriteGLBSPZ (FWadWriter &out, const char *label) +{ + ZLibOut zout (out); + + if (!CompressGLNodes) + { + printf (" GL Nodes are so big that compression has been forced.\n"); + } + + out.StartWritingLump (label); + out.AddToLump ("ZGLN", 4); + WriteVerticesZ (zout, &Level.GLVertices[Level.NumOrgVerts], Level.NumOrgVerts, Level.NumGLVertices - Level.NumOrgVerts); + WriteSubsectorsZ (zout, Level.GLSubsectors, Level.NumGLSubsectors); + WriteGLSegsZ (zout, Level.GLSegs, Level.NumGLSegs); + WriteNodesZ (zout, Level.GLNodes, Level.NumGLNodes); +} + +void FProcessor::WriteVerticesZ (ZLibOut &out, const WideVertex *verts, int orgverts, int newverts) +{ + out << (DWORD)orgverts << (DWORD)newverts; + + for (int i = 0; i < newverts; ++i) + { + out << verts[i].x << verts[i].y; + } +} + +void FProcessor::WriteSubsectorsZ (ZLibOut &out, const MapSubsectorEx *subs, int numsubs) +{ + out << (DWORD)numsubs; + + for (int i = 0; i < numsubs; ++i) + { + out << (DWORD)subs[i].numlines; + } +} + +void FProcessor::WriteSegsZ (ZLibOut &out, const MapSeg *segs, int numsegs) +{ + out << (DWORD)numsegs; + + for (int i = 0; i < numsegs; ++i) + { + out << (DWORD)segs[i].v1 + << (DWORD)segs[i].v2 + << (WORD)segs[i].linedef + << (BYTE)segs[i].side; + } +} + +void FProcessor::WriteGLSegsZ (ZLibOut &out, const MapSegGLEx *segs, int numsegs) +{ + out << (DWORD)numsegs; + + for (int i = 0; i < numsegs; ++i) + { + out << (DWORD)segs[i].v1 + << (DWORD)segs[i].partner + << (WORD)segs[i].linedef + << (BYTE)segs[i].side; + } +} + +void FProcessor::WriteNodesZ (ZLibOut &out, const MapNodeEx *nodes, int numnodes) +{ + out << (DWORD)numnodes; + + for (int i = 0; i < numnodes; ++i) + { + out << (SWORD)nodes[i].x + << (SWORD)nodes[i].y + << (SWORD)nodes[i].dx + << (SWORD)nodes[i].dy; + for (int j = 0; j < 2; ++j) + { + for (int k = 0; k < 4; ++k) + { + out << (SWORD)nodes[i].bbox[j][k]; + } + } + out << (DWORD)nodes[i].children[0] + << (DWORD)nodes[i].children[1]; + } +} + +// zlib lump writer --------------------------------------------------------- + +ZLibOut::ZLibOut (FWadWriter &out) + : Out (out) +{ + int err; + + Stream.next_in = Z_NULL; + Stream.avail_in = 0; + Stream.zalloc = Z_NULL; + Stream.zfree = Z_NULL; + err = deflateInit (&Stream, 9); + + if (err != Z_OK) + { + throw exception("Could not initialize deflate buffer."); + } + + Stream.next_out = Buffer; + Stream.avail_out = BUFFER_SIZE; +} + +ZLibOut::~ZLibOut () +{ + int err; + + for (;;) + { + err = deflate (&Stream, Z_FINISH); + if (err != Z_OK) + { + break; + } + if (Stream.avail_out == 0) + { + Out.AddToLump (Buffer, BUFFER_SIZE); + Stream.next_out = Buffer; + Stream.avail_out = BUFFER_SIZE; + } + } + deflateEnd (&Stream); + if (err != Z_STREAM_END) + { + throw exception("Error deflating data."); + } + Out.AddToLump (Buffer, BUFFER_SIZE - Stream.avail_out); +} + +void ZLibOut::Write (BYTE *data, int len) +{ + int err; + + Stream.next_in = data; + Stream.avail_in = len; + err = deflate (&Stream, 0); + while (Stream.avail_out == 0 && err == Z_OK) + { + Out.AddToLump (Buffer, BUFFER_SIZE); + Stream.next_out = Buffer; + Stream.avail_out = BUFFER_SIZE; + if (Stream.avail_in != 0) + { + err = deflate (&Stream, 0); + } + } + if (err != Z_OK) + { + throw exception("Error deflating data."); + } +} + +ZLibOut &ZLibOut::operator << (BYTE val) +{ + Write (&val, 1); + return *this; +} + +ZLibOut &ZLibOut::operator << (WORD val) +{ + val = SHORT(val); + Write ((BYTE *)&val, 2); + return *this; +} + +ZLibOut &ZLibOut::operator << (SWORD val) +{ + val = SHORT(val); + Write ((BYTE *)&val, 2); + return *this; +} + +ZLibOut &ZLibOut::operator << (DWORD val) +{ + val = LONG(val); + Write ((BYTE *)&val, 4); + return *this; +} + +ZLibOut &ZLibOut::operator << (fixed_t val) +{ + val = LONG(val); + Write ((BYTE *)&val, 4); + return *this; +} \ No newline at end of file diff --git a/processor.h b/processor.h new file mode 100644 index 0000000..34fcfb2 --- /dev/null +++ b/processor.h @@ -0,0 +1,95 @@ +#ifndef __PROCESSOR_H__ +#define __PROCESSOR_H__ + +#ifdef _MSC_VER +#pragma once +#endif + +#include "wad.h" +#include "doomdata.h" +#include "workdata.h" +#include "tarray.h" +#include "nodebuild.h" +#include "blockmapbuilder.h" +#include + +class ZLibOut +{ +public: + ZLibOut (FWadWriter &out); + ~ZLibOut (); + + ZLibOut &operator << (BYTE); + ZLibOut &operator << (WORD); + ZLibOut &operator << (SWORD); + ZLibOut &operator << (DWORD); + ZLibOut &operator << (fixed_t); + void Write (BYTE *data, int len); + +private: + enum { BUFFER_SIZE = 8192 }; + + z_stream Stream; + BYTE Buffer[BUFFER_SIZE]; + + FWadWriter &Out; +}; + +class FProcessor +{ +public: + FProcessor (FWadReader &inwad, int lump); + + void Write (FWadWriter &out); + +private: + void LoadThings (); + void LoadLines (); + void LoadVertices (); + void LoadSides (); + void LoadSectors (); + void GetPolySpots (); + + MapNodeEx *NodesToEx (const MapNode *nodes, int count); + MapSubsectorEx *SubsectorsToEx (const MapSubsector *ssec, int count); + MapSegGLEx *SegGLsToEx (const MapSegGL *segs, int count); + + void WriteLines (FWadWriter &out); + void WriteVertices (FWadWriter &out, int count); + void WriteSectors (FWadWriter &out); + void WriteSides (FWadWriter &out); + void WriteSegs (FWadWriter &out); + void WriteSSectors (FWadWriter &out) const; + void WriteNodes (FWadWriter &out) const; + void WriteBlockmap (FWadWriter &out); + void WriteReject (FWadWriter &out); + + void WriteGLVertices (FWadWriter &out); + void WriteGLSegs (FWadWriter &out); + void WriteGLSSect (FWadWriter &out); + void WriteGLNodes (FWadWriter &out); + + void WriteBSPZ (FWadWriter &out, const char *label); + void WriteGLBSPZ (FWadWriter &out, const char *label); + + void WriteVerticesZ (ZLibOut &out, const WideVertex *verts, int orgverts, int newverts); + void WriteSubsectorsZ (ZLibOut &out, const MapSubsectorEx *subs, int numsubs); + void WriteSegsZ (ZLibOut &out, const MapSeg *segs, int numsegs); + void WriteGLSegsZ (ZLibOut &out, const MapSegGLEx *segs, int numsegs); + void WriteNodesZ (ZLibOut &out, const MapNodeEx *nodes, int numnodes); + + void WriteNodes2 (FWadWriter &out, const char *name, const MapNodeEx *zaNodes, int count) const; + void WriteSSectors2 (FWadWriter &out, const char *name, const MapSubsectorEx *zaSubs, int count) const; + + FLevel Level; + + TArray PolyStarts; + TArray PolyAnchors; + + bool Extended; + + FWadReader &Wad; + int Lump; +}; + +#endif //__PROCESSOR_H__ diff --git a/resource.h b/resource.h new file mode 100644 index 0000000..209eef7 --- /dev/null +++ b/resource.h @@ -0,0 +1,20 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by resource.rc +// +#define IDD_MAPVIEW 101 +#define IDC_MAPVIEW 1004 +#define IDC_STATICNUMBER 1007 +#define IDC_COMBO3 1010 +#define IDC_COMBO1 1010 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1011 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/resource.rc b/resource.rc new file mode 100644 index 0000000..7e4b8f3 --- /dev/null +++ b/resource.rc @@ -0,0 +1,103 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_MAPVIEW DIALOGEX 0, 0, 510, 446 +STYLE DS_SETFONT | DS_CENTER | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | + WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +EXSTYLE WS_EX_APPWINDOW +CAPTION "ZDBSP Map Viewer" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + CONTROL "Custom2",IDC_MAPVIEW,"MapViewer",WS_VSCROLL | + WS_HSCROLL,7,7,496,411,WS_EX_CLIENTEDGE + PUSHBUTTON "OK",IDOK,7,423,53,16 + LTEXT "Static",IDC_STATICNUMBER,68,425,60,14 + COMBOBOX IDC_COMBO1,142,427,123,250,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_MAPVIEW, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 503 + TOPMARGIN, 7 + BOTTOMMARGIN, 439 + END +END +#endif // APSTUDIO_INVOKED + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/tarray.h b/tarray.h new file mode 100644 index 0000000..58271f0 --- /dev/null +++ b/tarray.h @@ -0,0 +1,207 @@ +/* +** tarray.h +** Templated, automatically resizing array +** +**--------------------------------------------------------------------------- +** Copyright 1998-2001 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#ifndef __TARRAY_H__ +#define __TARRAY_H__ + +#include + +template +class TArray +{ +public: + TArray () + { + Most = 0; + Count = 0; + Array = NULL; + } + TArray (int max) + { + Most = max; + Count = 0; + Array = new T[max]; + } + TArray (const TArray &other) + { + DoCopy (other); + } + TArray &operator= (const TArray &other) + { + if (&other != this) + { + if (Array != NULL) + { + delete[] Array; + } + DoCopy (other); + } + return *this; + } + ~TArray () + { + if (Array) + free (Array); + } + T &operator[] (size_t index) const + { + return Array[index]; + } + size_t Push (const T &item) + { + if (Count >= Most) + { + Most = (Most >= 16) ? Most + Most / 2 : 16; + Realloc (Count, Most); + } + Array[Count] = item; + return Count++; + } + bool Pop (T &item) + { + if (Count > 0) + { + item = Array[--Count]; + return true; + } + return false; + } + void Delete (int index) + { + if (index < Count-1) + memmove (Array + index, Array + index + 1, (Count - index) * sizeof(T)); + else if (index < Count) + Count--; + } + void Realloc (size_t count, size_t most) + { + T *copy = new T[most]; + for (size_t i = 0; i < count; ++i) + { + copy[i] = Array[i]; + } + delete[] Array; + Array = copy; + } + void ShrinkToFit () + { + if (Most > Count) + { + Most = Count; + if (Most == 0) + { + if (Array != NULL) + { + free (Array); + Array = NULL; + } + } + else + { + Realloc (Count, Most); + } + } + } + // Grow Array to be large enough to hold amount more entries without + // further growing. + void Grow (size_t amount) + { + if (Count + amount > Most) + { + const size_t choicea = Count + amount; + const size_t choiceb = Most + Most/2; + Most = (choicea > choiceb ? choicea : choiceb); + Realloc (Count, Most); + } + } + // Resize Array so that it has exactly amount entries in use. + void Resize (size_t amount) + { + if (Count < amount) + { + Grow (amount - Count); + } + else if (Count > amount) + { + Count = amount; + } + } + // Reserves amount entries at the end of the array, but does nothing + // with them. + size_t Reserve (size_t amount) + { + if (Count + amount > Most) + { + Grow (amount); + } + size_t place = Count; + Count += amount; + return place; + } + size_t Size () const + { + return Count; + } + size_t Max () const + { + return Most; + } + void Clear () + { + Count = 0; + } +private: + T *Array; + size_t Most; + size_t Count; + + void DoCopy (const TArray &other) + { + Most = Count = other.Count; + if (Count != 0) + { + Array = new T[Most]; + for (size_t i = 0; i < Count; ++i) + { + Array[i] = other.Array[i]; + } + } + else + { + Array = NULL; + } + } +}; + +#endif //__TARRAY_H__ diff --git a/templates.h b/templates.h new file mode 100644 index 0000000..b8a1197 --- /dev/null +++ b/templates.h @@ -0,0 +1,202 @@ +/* +** templates.h +** Some useful template functions +** +**--------------------------------------------------------------------------- +** Copyright 1998-2001 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#ifndef __TEMPLATES_H__ +#define __TEMPLATES_H__ + +#ifdef _MSC_VER +#pragma once +#endif + +#include + +//========================================================================== +// +// BinarySearch +// +// Searches an array sorted in ascending order for an element matching +// the desired key. +// +// Template parameters: +// ClassType - The class to be searched +// KeyType - The type of the key contained in the class +// +// Function parameters: +// first - Pointer to the first element in the array +// max - The number of elements in the array +// keyptr - Pointer to the key member of ClassType +// key - The key value to look for +// +// Returns: +// A pointer to the element with a matching key or NULL if none found. +//========================================================================== + +template +inline +const ClassType *BinarySearch (const ClassType *first, int max, + const KeyType ClassType::*keyptr, const KeyType key) +{ + int min = 0; + --max; + + while (min <= max) + { + int mid = (min + max) / 2; + const ClassType *probe = &first[mid]; + const KeyType &seekey = probe->*keyptr; + if (seekey == key) + { + return probe; + } + else if (seekey < key) + { + min = mid + 1; + } + else + { + max = mid - 1; + } + } + return NULL; +} + +//========================================================================== +// +// BinarySearchFlexible +// +// THIS DOES NOT WORK RIGHT WITH VISUAL C++ +// ONLY ONE COMPTYPE SEEMS TO BE USED FOR ANY INSTANCE OF THIS FUNCTION +// IN A DEBUG BUILD. RELEASE MIGHT BE DIFFERENT--I DIDN'T BOTHER TRYING. +// +// Similar to BinarySearch, except another function is used to copmare +// items in the array. +// +// Template parameters: +// IndexType - The type used to index the array (int, size_t, etc.) +// KeyType - The type of the key +// CompType - A class with a static DoCompare(IndexType, KeyType) method. +// +// Function parameters: +// max - The number of elements in the array +// key - The key value to look for +// noIndex - The value to return if no matching element is found. +// +// Returns: +// The index of the matching element or noIndex. +//========================================================================== + +template +inline +IndexType BinarySearchFlexible (IndexType max, const KeyType key, IndexType noIndex) +{ + IndexType min = 0; + --max; + + while (min <= max) + { + IndexType mid = (min + max) / 2; + int lexx = CompType::DoCompare (mid, key); + if (lexx == 0) + { + return mid; + } + else if (lexx < 0) + { + min = mid + 1; + } + else + { + max = mid - 1; + } + } + return noIndex; +} + +//========================================================================== +// +// MIN +// +// Returns the minimum of a and b. +//========================================================================== + +template +inline +const T MIN (const T a, const T b) +{ + return a < b ? a : b; +} + +//========================================================================== +// +// MAX +// +// Returns the maximum of a and b. +//========================================================================== + +template +inline +const T MAX (const T a, const T b) +{ + return a > b ? a : b; +} + +//========================================================================== +// +// clamp +// +// Clamps in to the range [min,max]. +//========================================================================== + +template +inline +T clamp (const T in, const T min, const T max) +{ + return in <= min ? min : in >= max ? max : in; +} + +//========================================================================== +// +// swap +// +// Swaps the values of a and b. +//========================================================================== + +template +inline +void swap (T &a, T &b) +{ + T temp = a; a = b; b = temp; +} + +#endif //__TEMPLATES_H__ diff --git a/view.cpp b/view.cpp new file mode 100644 index 0000000..8bb8049 --- /dev/null +++ b/view.cpp @@ -0,0 +1,1049 @@ +/* + A really crappy viewer module. + Copyright (C) 2002,2003 Randy Heit + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#define WIN32_LEAN_AND_MEAN +#define _WIN32_WINNT 0x0400 +#include +#include "resource.h" +#include "zdbsp.h" +#include "doomdata.h" +#include "templates.h" +#include "tarray.h" + +static int MapWidthDiff, MapHeightDiff; +static int ButtonsLeftOffset, ButtonsTopOffset; +static int StaticNumberLeft, StaticComboLeft; +static FLevel *Level; +static RECT MapBounds; +static POINT MapSize; +static int Divisor; + +enum ViewMode +{ + ViewNodes, + ViewGLNodes, + ViewSubsectors, + ViewGLSubsectors, + ViewReject, + ViewPVS +}; + +ViewMode Viewing; + +void ResetViews (); +void SetStaticText (); + +void ResizeDialog (HWND hDlg) +{ + RECT client; + HWND control; + + GetClientRect (hDlg, &client); + control = GetDlgItem (hDlg, IDC_MAPVIEW); + SetWindowPos (control, 0, 0, 0, client.right - MapWidthDiff, client.bottom - MapHeightDiff, + SWP_NOMOVE|SWP_NOREPOSITION|SWP_NOZORDER); + + control = GetDlgItem (hDlg, IDOK); + SetWindowPos (control, 0, ButtonsLeftOffset, client.bottom - ButtonsTopOffset, 0, 0, + SWP_NOSIZE|SWP_NOREPOSITION|SWP_NOZORDER); + + control = GetDlgItem (hDlg, IDC_STATICNUMBER); + SetWindowPos (control, 0, StaticNumberLeft, client.bottom - ButtonsTopOffset, 0, 0, + SWP_NOSIZE|SWP_NOREPOSITION|SWP_NOZORDER); + + control = GetDlgItem (hDlg, IDC_COMBO1); + SetWindowPos (control, 0, StaticComboLeft, client.bottom - ButtonsTopOffset, 0, 0, + SWP_NOSIZE|SWP_NOREPOSITION|SWP_NOZORDER); +} + +void AddToCombo (HWND control, const char *name, ViewMode mode) +{ + LRESULT lResult = SendMessage (control, CB_ADDSTRING, 0, (LPARAM)name); + if (lResult != CB_ERR && lResult != CB_ERRSPACE) + { + SendMessage (control, CB_SETITEMDATA, lResult, (LPARAM)mode); + if (Viewing == mode) + { + SendMessage (control, CB_SETCURSEL, (WPARAM)lResult, 0); + } + } +} + +INT_PTR CALLBACK ViewDialogFunc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + HWND control; + RECT rect1, rect2; + + switch (uMsg) + { + case WM_INITDIALOG: + if (GetClientRect (hDlg, &rect1)) + { + control = GetDlgItem (hDlg, IDC_MAPVIEW); + if (GetWindowRect (control, &rect2)) + { + MapWidthDiff = rect1.right - rect2.right + rect2.left; + MapHeightDiff = rect1.bottom - rect2.bottom + rect2.top; + } + control = GetDlgItem (hDlg, IDOK); + if (GetWindowRect (control, &rect2)) + { + POINT pt = { 0, rect1.bottom }; + ClientToScreen (hDlg, &pt); + ButtonsTopOffset = pt.y - rect2.top; + ButtonsLeftOffset = rect2.left - pt.x; + control = GetDlgItem (hDlg, IDC_STATICNUMBER); + if (GetWindowRect (control, &rect2)) + { + StaticNumberLeft = rect2.left - pt.x; + } + control = GetDlgItem (hDlg, IDC_COMBO1); + if (GetWindowRect (control, &rect2)) + { + StaticComboLeft = rect2.left - pt.x; + } + } + } + + control = GetDlgItem (hDlg, IDC_COMBO1); + if (Level->Nodes) + { + AddToCombo (control, "Nodes", ViewNodes); + } + if (Level->Subsectors) + { + AddToCombo (control, "Subsectors", ViewSubsectors); + } + if (Level->Reject) + { + AddToCombo (control, "Reject", ViewReject); + } + if (Level->GLNodes) + { + AddToCombo (control, "GL Nodes", ViewGLNodes); + } + if (Level->GLSubsectors) + { + AddToCombo (control, "GL Subsectors", ViewGLSubsectors); + } + if (Level->GLPVS) + { + AddToCombo (control, "PVS", ViewPVS); + } + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDOK: + case IDCANCEL: + EndDialog (hDlg, 1); + break; + + case IDC_COMBO1: + if (HIWORD(wParam) == CBN_SELENDOK) + { + ViewMode newmode; + + newmode = (ViewMode)SendMessage (HWND(lParam), CB_GETITEMDATA, + SendMessage (HWND(lParam), CB_GETCURSEL, 0, 0), 0); + + if (newmode != Viewing) + { + Viewing = newmode; + ResetViews (); + InvalidateRect (GetDlgItem (hDlg, IDC_MAPVIEW), NULL, TRUE); + } + } + break; + } + return TRUE; + + case WM_GETMINMAXINFO: + ((MINMAXINFO *)lParam)->ptMinTrackSize.x = 480; + ((MINMAXINFO *)lParam)->ptMinTrackSize.y = 200; + return TRUE; + + case WM_SIZE: + if (wParam != SIZE_MAXHIDE && wParam != SIZE_MAXSHOW && wParam != SIZE_MINIMIZED) + { + ResizeDialog (hDlg); + } + return TRUE; + + case WM_MOUSEWHEEL: + control = GetDlgItem (hDlg, IDC_MAPVIEW); + PostMessage (control, WM_MOUSEWHEEL, wParam, lParam); + return 0; + + case WM_APP: + control = GetDlgItem (hDlg, IDC_STATICNUMBER); + char foo[16]; + wsprintf (foo, "%d", wParam); + SendMessage (control, WM_SETTEXT, 0, (LPARAM)foo); + return 0; + } + return FALSE; +} + +static inline int VERTX (int i) +{ + return Level->Vertices[i].x >> FRACBITS; +} + +static inline int VERTY (int i) +{ + return Level->Vertices[i].y >> FRACBITS; +} + +static inline int GLVERTX (int i) +{ + return Level->GLVertices[i].x >> FRACBITS; +} + +static inline int GLVERTY (int i) +{ + return Level->GLVertices[i].y >> FRACBITS; +} + +static HPEN NotInNode; +static HPEN LeftOfSplitter, LeftPen1, LeftPen2; +static HPEN RightOfSplitter, RightPen1, RightPen2; +static HPEN OutPen1; +static HPEN Splitter; +static short DesiredNode; +static TArray DesiredHistory; + +static void DrawSubsector (HDC dc, int ssec) +{ + for (DWORD i = 0; i < Level->Subsectors[ssec].numlines; ++i) + { + int seg = Level->Subsectors[ssec].firstline + i; + if (Level->Segs[seg].side == 0) + { + MoveToEx (dc, VERTX(Level->Segs[seg].v1), VERTY(Level->Segs[seg].v1), NULL); + LineTo (dc, VERTX(Level->Segs[seg].v2), VERTY(Level->Segs[seg].v2)); + } + else + { + MoveToEx (dc, VERTX(Level->Segs[seg].v2), VERTY(Level->Segs[seg].v2), NULL); + LineTo (dc, VERTX(Level->Segs[seg].v1), VERTY(Level->Segs[seg].v1)); + } + } +} + +static void DrawSubsectorGL (HDC dc, int ssec, HPEN miniPen, HPEN badPen) +{ + int seg; + + seg = Level->GLSubsectors[ssec].firstline; + MoveToEx (dc, GLVERTX(Level->GLSegs[seg].v1), GLVERTY(Level->GLSegs[seg].v1), NULL); + for (DWORD i = 0; i < Level->GLSubsectors[ssec].numlines; ++i) + { + bool mini = false; + HPEN oldPen; + seg = Level->GLSubsectors[ssec].firstline + i; + if (Level->GLSegs[seg].linedef == NO_INDEX) + { + mini = true; + if (Level->GLSegs[seg].partner == NO_INDEX) + { + oldPen = (HPEN)SelectObject (dc, badPen); + } + else + { + oldPen = (HPEN)SelectObject (dc, miniPen); + } + } + if (Level->GLSegs[seg].side == 0 || 1) + { + //MoveToEx (dc, GLVERTX(Level->GLSegs[seg].v1), GLVERTY(Level->GLSegs[seg].v1), NULL); + LineTo (dc, GLVERTX(Level->GLSegs[seg].v2), GLVERTY(Level->GLSegs[seg].v2)); + } + else + { + //MoveToEx (dc, GLVERTX(Level->GLSegs[seg].v2), GLVERTY(Level->GLSegs[seg].v2), NULL); + LineTo (dc, GLVERTX(Level->GLSegs[seg].v1), GLVERTY(Level->GLSegs[seg].v1)); + } + if (mini) + { + SelectObject (dc, oldPen); + } + } +} + + +static void RealDrawNode (HDC dc, int node, HPEN miniPen, HPEN badPen) +{ + if (Viewing == ViewNodes) + { + if (node & NFX_SUBSECTOR) + { + DrawSubsector (dc, node & ~NFX_SUBSECTOR); + } + else + { + RealDrawNode (dc, Level->Nodes[node].children[0], miniPen, badPen); + RealDrawNode (dc, Level->Nodes[node].children[1], miniPen, badPen); + } + } + else + { + if (node & NFX_SUBSECTOR) + { + DrawSubsectorGL (dc, node & ~NFX_SUBSECTOR, miniPen, badPen); + } + else + { + RealDrawNode (dc, Level->GLNodes[node].children[0], miniPen, badPen); + RealDrawNode (dc, Level->GLNodes[node].children[1], miniPen, badPen); + } + } +} + +static void DrawNode (HDC dc, int node) +{ + if (node & NFX_SUBSECTOR) + { + return; + } + else + { + if (Viewing == ViewNodes) + { + if (node == DesiredNode) + { + SelectObject (dc, LeftOfSplitter); + RealDrawNode (dc, Level->Nodes[node].children[1], LeftPen1, LeftPen2); + SelectObject (dc, RightOfSplitter); + RealDrawNode (dc, Level->Nodes[node].children[0], RightPen1, RightPen2); + return; + } + else + { + DrawNode (dc, Level->Nodes[node].children[0]); + DrawNode (dc, Level->Nodes[node].children[1]); + } + } + else + { + if (node == DesiredNode) + { + SelectObject (dc, LeftOfSplitter); + RealDrawNode (dc, Level->GLNodes[node].children[1], LeftPen1, LeftPen2); + SelectObject (dc, RightOfSplitter); + RealDrawNode (dc, Level->GLNodes[node].children[0], RightPen1, RightPen2); + return; + } + else + { + DrawNode (dc, Level->GLNodes[node].children[0]); + DrawNode (dc, Level->GLNodes[node].children[1]); + } + } + } +} + +static void DrawOutsideNode (HDC dc, int node) +{ + if (node == DesiredNode) + { + return; + } + else if (node & NFX_SUBSECTOR) + { + if (Viewing == ViewNodes) + { + DrawSubsector (dc, node & ~NFX_SUBSECTOR); + } + else + { + DrawSubsectorGL (dc, node & ~NFX_SUBSECTOR, OutPen1, OutPen1); + } + } + else + { + if (Viewing == ViewNodes) + { + DrawOutsideNode (dc, Level->Nodes[node].children[0]); + DrawOutsideNode (dc, Level->Nodes[node].children[1]); + } + else + { + DrawOutsideNode (dc, Level->GLNodes[node].children[0]); + DrawOutsideNode (dc, Level->GLNodes[node].children[1]); + } + } +} + +static void DrawLevelNodes (HDC dc) +{ + HPEN oldPen; + + NotInNode = CreatePen (PS_SOLID, 1, RGB(200,200,200)); + OutPen1 = CreatePen (PS_SOLID, 1, RGB(238,238,238)); + LeftOfSplitter = CreatePen (PS_SOLID, 1, RGB(255,0,0)); + LeftPen1 = CreatePen (PS_SOLID, 1, RGB(255,200,200)); + LeftPen2 = CreatePen (PS_SOLID, 1, RGB(128,0,0)); + RightOfSplitter = CreatePen (PS_SOLID, 1, RGB(0,255,0)); + RightPen1 = CreatePen (PS_SOLID, 1, RGB(200,255,200)); + RightPen2 = CreatePen (PS_SOLID, 1, RGB(0,128,0)); + Splitter = CreatePen (PS_DOT, 1, RGB(70,0,255)); + + oldPen = (HPEN)SelectObject (dc, NotInNode); + + if (Viewing == ViewNodes) + { + DrawOutsideNode (dc, Level->NumNodes - 1); + DrawNode (dc, Level->NumNodes - 1); + SelectObject (dc, Splitter); + MoveToEx (dc, Level->Nodes[DesiredNode].x - Level->Nodes[DesiredNode].dx, Level->Nodes[DesiredNode].y - Level->Nodes[DesiredNode].dy, NULL); + LineTo (dc, Level->Nodes[DesiredNode].x + 2*Level->Nodes[DesiredNode].dx, Level->Nodes[DesiredNode].y + 2*Level->Nodes[DesiredNode].dy); + } + else + { + DrawOutsideNode (dc, Level->NumGLNodes - 1); + DrawNode (dc, Level->NumGLNodes - 1); + SelectObject (dc, Splitter); + MoveToEx (dc, Level->GLNodes[DesiredNode].x - Level->GLNodes[DesiredNode].dx, Level->GLNodes[DesiredNode].y - Level->GLNodes[DesiredNode].dy, NULL); + LineTo (dc, Level->GLNodes[DesiredNode].x + 2*Level->GLNodes[DesiredNode].dx, Level->GLNodes[DesiredNode].y + 2*Level->GLNodes[DesiredNode].dy); + } + + + SelectObject (dc, oldPen); + + DeleteObject (NotInNode); + DeleteObject (LeftOfSplitter); + DeleteObject (LeftPen1); + DeleteObject (LeftPen2); + DeleteObject (RightOfSplitter); + DeleteObject (RightPen1); + DeleteObject (RightPen2); + DeleteObject (Splitter); +} + +static int DesiredSubsector; + +static void DrawLevelSubsectors (HDC dc) +{ + HPEN oldPen, pen, mini, bad; + int i; + + pen = CreatePen (PS_SOLID, 1, RGB(200,200,200)); + mini = CreatePen (PS_SOLID, 1, RGB(100,200,255)); + bad = CreatePen (PS_SOLID, 1, RGB(255,128,128)); + + oldPen = (HPEN)SelectObject (dc, pen); + + if (Viewing == ViewGLSubsectors) + { + for (i = 0; i < Level->NumGLSubsectors; ++i) + { + if (i != DesiredSubsector) + { + DrawSubsectorGL (dc, i, mini, bad); + } + } + } + else + { + for (i = 0; i < Level->NumSubsectors; ++i) + { + if (i != DesiredSubsector) + { + DrawSubsector (dc, i); + } + } + } + SelectObject (dc, oldPen); + DeleteObject (pen); + DeleteObject (mini); + DeleteObject (bad); + + pen = CreatePen (PS_SOLID, 1, RGB(0,0,0)); + mini = CreatePen (PS_SOLID, 1, RGB(0,0,255)); + bad = CreatePen (PS_SOLID, 1, RGB(255,0,0)); + SelectObject (dc, pen); + if (Viewing == ViewGLSubsectors) + { + DrawSubsectorGL (dc, DesiredSubsector, mini, bad); + } + else + { + DrawSubsector (dc, DesiredSubsector); + } + SelectObject (dc, oldPen); + DeleteObject (pen); + DeleteObject (mini); + DeleteObject (bad); +} + +static inline int PointOnSide (int x, int y, const MapNodeEx &nd) +{ + int foo = DMulScale32 (y-nd.y, nd.dx, nd.x-x, nd.dy); + return foo >= 0; +} + +static void SetDesiredSubsector (int x, int y) +{ + if (Viewing == ViewSubsectors) + { + int node = Level->NumNodes - 1; + + while (!(node & NFX_SUBSECTOR)) + { + node = Level->Nodes[node].children[PointOnSide (x, y, Level->Nodes[node])]; + } + DesiredSubsector = node & ~NFX_SUBSECTOR; + } + else + { + int node = Level->NumGLNodes - 1; + + while (!(node & NFX_SUBSECTOR)) + { + node = Level->GLNodes[node].children[PointOnSide (x, y, Level->GLNodes[node])]; + } + DesiredSubsector = node & ~NFX_SUBSECTOR; + } +} + +static void SetDesiredNode (int x, int y) +{ + int node, parent; + size_t depth = 0; + + // Traverse the tree until we find a node that is not on the + // path to the previous desired node. + if (Viewing == ViewNodes) + { + node = Level->NumNodes - 1, parent = node; + while (depth < DesiredHistory.Size() && node == DesiredHistory[depth]) + { + parent = node; + node = Level->Nodes[node].children[PointOnSide (x, y, Level->Nodes[node])]; + depth++; + } + } + else + { + node = Level->NumGLNodes - 1, parent = node; + while (depth < DesiredHistory.Size() && node == DesiredHistory[depth]) + { + parent = node; + node = Level->GLNodes[node].children[PointOnSide (x, y, Level->GLNodes[node])]; + depth++; + } + } + + // If we traversed all the way through the history, the new desired + // node is a child of the old one. + if (depth == DesiredHistory.Size()) + { + if (!(node & NFX_SUBSECTOR)) + { + DesiredNode = node; + DesiredHistory.Push (DesiredNode); + } + } + // If we didn't make it all the way through the history, set the + // desired node to this node's parent, so that the new desired node + // will include both the old node and the one just clicked. + else + { + DesiredHistory.Resize (depth); + DesiredNode = DesiredHistory[depth-1];; + } +} + +static int DesiredSector; + +static void DrawLevelReject (HDC dc) +{ + int seeFromRow = DesiredSector * Level->NumSectors; + HPEN oldPen; + HPEN cantSee; + HPEN canSee; + HPEN seeFrom; + + cantSee = CreatePen (PS_SOLID, 1, RGB(200,200,200)); + canSee = CreatePen (PS_SOLID, 1, RGB(200,0,200)); + seeFrom = CreatePen (PS_SOLID, 1, RGB(255,128,0)); + + oldPen = (HPEN)SelectObject (dc, canSee); + enum { UNDECIDED, CANT_SEE, CAN_SEE, SEE_FROM } choice, prevchoice = CAN_SEE; + + for (int i = 0; i < Level->NumLines; ++i) + { + choice = UNDECIDED; + + if (Level->Lines[i].sidenum[0] != NO_INDEX) + { + if (Level->Sides[Level->Lines[i].sidenum[0]].sector == DesiredSector) + { + choice = SEE_FROM; + } + else if (Level->Reject != NULL) + { + int pnum = seeFromRow + Level->Sides[Level->Lines[i].sidenum[0]].sector; + if (Level->Reject[pnum>>3] & (1<<(pnum&7))) + { + choice = CANT_SEE; + } + else + { + choice = CAN_SEE; + } + } + else + { + choice = CAN_SEE; + } + } + if (Level->Lines[i].sidenum[1] != NO_INDEX && choice < SEE_FROM) + { + if (Level->Sides[Level->Lines[i].sidenum[1]].sector == DesiredSector) + { + choice = SEE_FROM; + } + else if (Level->Reject != NULL && choice < CAN_SEE) + { + int pnum = seeFromRow + Level->Sides[Level->Lines[i].sidenum[1]].sector; + if (Level->Reject[pnum>>3] & (1<<(pnum&7))) + { + choice = CANT_SEE; + } + else + { + choice = CAN_SEE; + } + } + else + { + choice = CAN_SEE; + } + } + if (choice != UNDECIDED) + { + if (choice != prevchoice) + { + prevchoice = choice; + switch (choice) + { + case CANT_SEE: SelectObject (dc, cantSee); break; + case CAN_SEE: SelectObject (dc, canSee); break; + case SEE_FROM: SelectObject (dc, seeFrom); break; + } + } + MoveToEx (dc, VERTX(Level->Lines[i].v1), VERTY(Level->Lines[i].v1), NULL); + LineTo (dc, VERTX(Level->Lines[i].v2), VERTY(Level->Lines[i].v2)); + } + } + SelectObject (dc, oldPen); + DeleteObject (cantSee); + DeleteObject (canSee); + DeleteObject (seeFrom); +} + +static void DrawLevelPVS (HDC dc) +{ + HPEN oldPen; + HPEN pen; + HPEN mini; + + pen = CreatePen (PS_SOLID, 1, RGB(200,200,200)); + mini = CreatePen (PS_SOLID, 1, RGB(100,200,255)); + + oldPen = (HPEN)SelectObject (dc, pen); + + int i, row = DesiredSubsector * Level->NumGLSubsectors; + + for (i = 0; i < Level->NumGLSubsectors; ++i) + { + int l = (row + i) >> 3; + int r = (row + i) & 7; + + if (!(Level->GLPVS[l] & (1 << r))) + { + DrawSubsectorGL (dc, i, mini, mini); + } + } + + SelectObject (dc, oldPen); + DeleteObject (pen); + DeleteObject (mini); + + pen = CreatePen (PS_SOLID, 1, RGB(200,0,200)); + mini = CreatePen (PS_SOLID, 1, RGB(200,100,200)); + SelectObject (dc, pen); + + for (i = 0; i < Level->NumGLSubsectors; ++i) + { + int l = (row + i) >> 3; + int r = (row + i) & 7; + + if (Level->GLPVS[l] & (1 << r)) + { + DrawSubsectorGL (dc, i, mini, mini); + } + } + + SelectObject (dc, oldPen); + DeleteObject (pen); + DeleteObject (mini); + + pen = CreatePen (PS_SOLID, 1, RGB(255,128,0)); + mini = CreatePen (PS_SOLID, 1, RGB(255,150,100)); + SelectObject (dc, pen); + + DrawSubsectorGL (dc, DesiredSubsector, mini, mini); + + SelectObject (dc, oldPen); + DeleteObject (pen); + DeleteObject (mini); +} + +static void SetDesiredSector (int x, int y) +{ + int node = Level->NumNodes - 1; + const MapSeg *seg; + + while (!(node & NFX_SUBSECTOR)) + { + node = Level->Nodes[node].children[PointOnSide (x, y, Level->Nodes[node])]; + } + node &= ~NFX_SUBSECTOR; + seg = &Level->Segs[Level->Subsectors[node].firstline]; + DesiredSector = Level->Sides[Level->Lines[seg->linedef].sidenum[seg->side]].sector; +} + +static void DrawLevel (HDC dc) +{ + switch (Viewing) + { + case ViewNodes: + case ViewGLNodes: + DrawLevelNodes (dc); + break; + case ViewSubsectors: + case ViewGLSubsectors: + DrawLevelSubsectors (dc); + break; + case ViewReject: + DrawLevelReject (dc); + break; + case ViewPVS: + DrawLevelPVS (dc); + break; + } + /* + int i; + + for (i = 0; i < Level->NumSegs; ++i) + { + MoveToEx (dc, VERTX(Level->Segs[i].v1), VERTY(Level->Segs[i].v1), NULL); + LineTo (dc, VERTX(Level->Segs[i].v2), VERTY(Level->Segs[i].v2)); + } + */ +} + +void SizeView (HWND wnd, bool firstTime) +{ + RECT rect; + SCROLLINFO sinfo = { sizeof(SCROLLINFO) }; + + GetClientRect (wnd, &rect); + sinfo.fMask = SIF_PAGE|SIF_RANGE|SIF_POS; + + GetScrollInfo (wnd, SB_VERT, &sinfo); + sinfo.nMin = 0; + sinfo.nMax = MapBounds.bottom - MapBounds.top; + sinfo.nPage = rect.bottom; + if (firstTime) + { + sinfo.nPos = 0; + } + else if (sinfo.nPos > sinfo.nMax - (int)sinfo.nPage) + { + int delta = sinfo.nPos - sinfo.nMax + (int)sinfo.nPage; + ScrollWindowEx (wnd, 0, (delta + Divisor-1) / Divisor, NULL, NULL, NULL, NULL, SW_INVALIDATE|SW_ERASE); + sinfo.nPos = sinfo.nMax - sinfo.nPage; + } + SetScrollInfo (wnd, SB_VERT, &sinfo, TRUE); + + GetScrollInfo (wnd, SB_HORZ, &sinfo); + sinfo.nMin = 0; + sinfo.nMax = MapBounds.right - MapBounds.left; + sinfo.nPage = rect.right; + if (firstTime) + { + sinfo.nPos = 0; + } + else if (sinfo.nPos > sinfo.nMax - (int)sinfo.nPage) + { + int delta = sinfo.nPos - sinfo.nMax + (int)sinfo.nPage; + ScrollWindowEx (wnd, (delta + Divisor-1) / Divisor, 0, NULL, NULL, NULL, NULL, SW_INVALIDATE|SW_ERASE); + sinfo.nPos = sinfo.nMax - sinfo.nPage; + } + SetScrollInfo (wnd, SB_HORZ, &sinfo, TRUE); +} + +LRESULT CALLBACK MapViewFunc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + static bool dragging = false; + static POINTS dragpos; + static int hitx, hity; + SCROLLINFO sinfo = { sizeof(SCROLLINFO), }; + int pos; + + switch (uMsg) + { + case WM_CREATE: + SizeView (hWnd, true); + break; + + case WM_SIZE: + SizeView (hWnd, false); + //InvalidateRect (hWnd, NULL, TRUE); + break; + + case WM_PAINT: + if (GetUpdateRect (hWnd, NULL, FALSE)) + { + PAINTSTRUCT paint; + HDC dc = BeginPaint (hWnd, &paint); + + if (dc != NULL) + { + sinfo.fMask = SIF_POS; + GetScrollInfo (hWnd, SB_HORZ, &sinfo); + pos = sinfo.nPos; + GetScrollInfo (hWnd, SB_VERT, &sinfo); + SetMapMode (dc, MM_ANISOTROPIC); + ScaleWindowExtEx (dc, Divisor, 1, Divisor, -1, NULL); + SetWindowOrgEx (dc, pos + MapBounds.left, MapBounds.bottom - sinfo.nPos, NULL); + DrawLevel (dc); + EndPaint (hWnd, &paint); + } + } + return 0; + + case WM_LBUTTONDOWN: + sinfo.fMask = SIF_POS; + GetScrollInfo (hWnd, SB_HORZ, &sinfo); + pos = sinfo.nPos; + GetScrollInfo (hWnd, SB_VERT, &sinfo); + hitx = LOWORD(lParam)*Divisor + pos + MapBounds.left; + hity = MapBounds.bottom - sinfo.nPos - HIWORD(lParam)*Divisor; + switch (Viewing) + { + case ViewNodes: + case ViewGLNodes: + SetDesiredNode (hitx, hity); + PostMessage (GetParent (hWnd), WM_APP, DesiredNode, 0); + break; + case ViewSubsectors: + case ViewGLSubsectors: + case ViewPVS: + SetDesiredSubsector (hitx, hity); + PostMessage (GetParent (hWnd), WM_APP, DesiredSubsector, 0); + break; + case ViewReject: + SetDesiredSector (hitx, hity); + PostMessage (GetParent (hWnd), WM_APP, DesiredSector, 0); + break; + } + InvalidateRect (hWnd, NULL, TRUE); + return 0; + + case WM_HSCROLL: + sinfo.fMask = SIF_POS|SIF_TRACKPOS|SIF_RANGE|SIF_PAGE; + GetScrollInfo (hWnd, SB_HORZ, &sinfo); + switch (LOWORD(wParam)) + { + case SB_PAGEUP: pos = sinfo.nPos - sinfo.nPage; break; + case SB_PAGEDOWN: pos = sinfo.nPos + sinfo.nPage; break; + case SB_LINEUP: pos = sinfo.nPos - 10; break; + case SB_LINEDOWN: pos = sinfo.nPos + 10; break; + case SB_THUMBTRACK: pos = sinfo.nTrackPos; break; + default: pos = sinfo.nPos; + }; + + pos = clamp (pos, 0, sinfo.nMax - sinfo.nPage); + if (pos != sinfo.nPos) + { + SetScrollPos (hWnd, SB_HORZ, pos, TRUE); + int oldx = sinfo.nPos / Divisor; + int newx = pos / Divisor; + ScrollWindowEx (hWnd, oldx - newx, 0, NULL, NULL, NULL, NULL, SW_INVALIDATE|SW_ERASE); + UpdateWindow (hWnd); + } + return 0; + + case WM_VSCROLL: + sinfo.fMask = SIF_POS|SIF_TRACKPOS|SIF_RANGE|SIF_PAGE; + GetScrollInfo (hWnd, SB_VERT, &sinfo); + switch (LOWORD(wParam)) + { + case SB_PAGEUP: pos = sinfo.nPos - sinfo.nPage; break; + case SB_PAGEDOWN: pos = sinfo.nPos + sinfo.nPage; break; + case SB_LINEUP: pos = sinfo.nPos - 10; break; + case SB_LINEDOWN: pos = sinfo.nPos + 10; break; + case SB_THUMBTRACK: pos = sinfo.nTrackPos; break; + default: pos = sinfo.nPos; + }; + + pos = clamp (pos, 0, sinfo.nMax - sinfo.nPage); + if (pos != sinfo.nPos) + { + SetScrollPos (hWnd, SB_VERT, pos, TRUE); + ScrollWindowEx (hWnd, 0, (sinfo.nPos - pos) / Divisor, NULL, NULL, NULL, NULL, SW_INVALIDATE|SW_ERASE); + UpdateWindow (hWnd); + } + return 0; + + case WM_MOUSEWHEEL: + sinfo.fMask = SIF_POS|SIF_RANGE|SIF_PAGE; + GetScrollInfo (hWnd, SB_VERT, &sinfo); + pos = sinfo.nPos - short(HIWORD(wParam))*64/WHEEL_DELTA; + + pos = clamp (pos, 0, sinfo.nMax - sinfo.nPage); + if (pos != sinfo.nPos) + { + SetScrollPos (hWnd, SB_VERT, pos, TRUE); + ScrollWindowEx (hWnd, 0, (sinfo.nPos - pos) / Divisor, NULL, NULL, NULL, NULL, SW_INVALIDATE|SW_ERASE); + UpdateWindow (hWnd); + } + return 0; + + case WM_RBUTTONDOWN: + dragging = true; + dragpos = MAKEPOINTS(lParam); + return 0; + + case WM_RBUTTONUP: + dragging = false; + return 0; + + case WM_MOUSEMOVE: + if (!(wParam & MK_RBUTTON)) + { + dragging = false; + } + else if (dragging) + { + POINTS newpos = MAKEPOINTS(lParam); + int delta; + + delta = (newpos.x - dragpos.x) * 8; + if (delta) + { + sinfo.fMask = SIF_POS|SIF_RANGE|SIF_PAGE; + GetScrollInfo (hWnd, SB_HORZ, &sinfo); + pos = sinfo.nPos - delta; + + pos = clamp (pos, 0, sinfo.nMax - sinfo.nPage); + if (pos != sinfo.nPos) + { + SetScrollPos (hWnd, SB_HORZ, pos, TRUE); + ScrollWindowEx (hWnd, (sinfo.nPos - pos) / Divisor, 0, NULL, NULL, NULL, NULL, SW_INVALIDATE|SW_ERASE); + UpdateWindow (hWnd); + } + } + delta = (newpos.y - dragpos.y) * 8; + if (delta) + { + sinfo.fMask = SIF_POS|SIF_RANGE|SIF_PAGE; + GetScrollInfo (hWnd, SB_VERT, &sinfo); + pos = sinfo.nPos - delta; + + pos = clamp (pos, 0, sinfo.nMax - sinfo.nPage); + if (pos != sinfo.nPos) + { + SetScrollPos (hWnd, SB_VERT, pos, TRUE); + ScrollWindowEx (hWnd, 0, (sinfo.nPos - pos) / Divisor, NULL, NULL, NULL, NULL, SW_INVALIDATE|SW_ERASE); + UpdateWindow (hWnd); + } + } + dragpos = newpos; + return 0; + } + } + return DefWindowProc (hWnd, uMsg, wParam, lParam); +} + +void ShowView (FLevel *level) +{ + LOGBRUSH WhiteBrush = { BS_SOLID, RGB(255,255,255), 0 }; + WNDCLASS MapViewerClass = + { + 0, + MapViewFunc, + 0, + 0, + GetModuleHandle(0), + 0, + LoadCursor (0, IDC_ARROW), + CreateBrushIndirect (&WhiteBrush), + 0, + "MapViewer" + }; + + if (RegisterClass (&MapViewerClass)) + { + Level = level; + + MapBounds.left = short(level->Blockmap[0]) - 8; + MapBounds.right = short(level->Blockmap[0]) + (level->Blockmap[2] << BLOCKBITS) + 8; + MapBounds.top = short(level->Blockmap[1]) - 8; + MapBounds.bottom = short(level->Blockmap[1]) + (level->Blockmap[3] << BLOCKBITS) + 8; + MapSize.x = MapBounds.right - MapBounds.left; + MapSize.y = MapBounds.bottom - MapBounds.top; + Divisor = 1; + + if (Level->Subsectors == NULL) + { + Viewing = ViewGLSubsectors; + } + else + { + Viewing = ViewSubsectors; + } + + ResetViews (); + DialogBox (GetModuleHandle(0), MAKEINTRESOURCE(IDD_MAPVIEW), NULL, ViewDialogFunc); + UnregisterClass ("MapViewer", GetModuleHandle(0)); + } +} + +void ResetViews () +{ + DesiredHistory.Clear (); + if (Viewing == ViewNodes) + { + DesiredNode = Level->NumNodes - 1; + } + else + { + DesiredNode = Level->NumGLNodes - 1; + } + DesiredHistory.Push (DesiredNode); + DesiredSubsector = 0; + DesiredSector = 0; +} diff --git a/wad.cpp b/wad.cpp new file mode 100644 index 0000000..719f4f3 --- /dev/null +++ b/wad.cpp @@ -0,0 +1,423 @@ +/* + WAD-handling routines. + Copyright (C) 2002,2003 Randy Heit + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#include "wad.h" + +static const char MapLumpNames[12][9] = +{ + "THINGS", + "LINEDEFS", + "SIDEDEFS", + "VERTEXES", + "SEGS", + "SSECTORS", + "NODES", + "SECTORS", + "REJECT", + "BLOCKMAP", + "BEHAVIOR", + "SCRIPTS" +}; + +static const bool MapLumpRequired[12] = +{ + true, // THINGS + true, // LINEDEFS + true, // SIDEDEFS + true, // VERTEXES + false, // SEGS + false, // SSECTORS + false, // NODES + true, // SECTORS + false, // REJECT + false, // BLOCKMAP + false, // BEHAVIOR + false // SCRIPTS +}; + +static const char GLLumpNames[5][9] = +{ + "GL_VERT", + "GL_SEGS", + "GL_SSECT", + "GL_NODES", + "GL_PVS" +}; + +FWadReader::FWadReader (const char *filename) + : Lumps (NULL), File (NULL) +{ + File = fopen (filename, "rb"); + if (File == NULL) + { + throw exception("Could not open input file"); + } + + SafeRead (&Header, sizeof(Header)); + if (Header.Magic[0] != 'P' && Header.Magic[0] != 'I' && + Header.Magic[1] != 'W' && + Header.Magic[2] != 'A' && + Header.Magic[3] != 'D') + { + fclose (File); + File = NULL; + throw exception("Input file is not a wad"); + } + + Header.NumLumps = LONG(Header.NumLumps); + Header.Directory = LONG(Header.Directory); + + if (fseek (File, Header.Directory, SEEK_SET)) + { + throw exception("Could not read wad directory"); + } + + Lumps = new WadLump[Header.NumLumps]; + SafeRead (Lumps, Header.NumLumps * sizeof(*Lumps)); + + for (int i = 0; i < Header.NumLumps; ++i) + { + Lumps[i].FilePos = LONG(Lumps[i].FilePos); + Lumps[i].Size = LONG(Lumps[i].Size); + } +} + +FWadReader::~FWadReader () +{ + if (File) fclose (File); + if (Lumps) delete[] Lumps; +} + +bool FWadReader::IsIWAD () const +{ + return Header.Magic[0] == 'I'; +} + +int FWadReader::NumLumps () const +{ + return Header.NumLumps; +} + +int FWadReader::FindLump (const char *name, int index) const +{ + if (index < 0) + { + index = 0; + } + for (; index < Header.NumLumps; ++index) + { + if (strnicmp (Lumps[index].Name, name, 8) == 0) + { + return index; + } + } + return -1; +} + +int FWadReader::FindMapLump (const char *name, int map) const +{ + int i, j, k; + ++map; + + for (i = 0; i < 12; ++i) + { + if (strnicmp (MapLumpNames[i], name, 8) == 0) + { + break; + } + } + if (i == 12) + { + return -1; + } + + for (j = k = 0; j < 12; ++j) + { + if (strnicmp (Lumps[map+k].Name, MapLumpNames[j], 8) == 0) + { + if (i == j) + { + return map+k; + } + k++; + } + } + return -1; +} + +bool FWadReader::IsMap (int index) const +{ + int i, j; + + index++; + for (i = j = 0; i < 12; ++i) + { + if (strnicmp (Lumps[index+j].Name, MapLumpNames[i], 8) != 0) + { + if (MapLumpRequired[i]) + { + return false; + } + } + else + { + j++; + } + } + return true; +} + +int FWadReader::FindGLLump (const char *name, int glheader) const +{ + int i, j, k; + ++glheader; + + for (i = 0; i < 5; ++i) + { + if (strnicmp (Lumps[glheader+i].Name, name, 8) == 0) + { + break; + } + } + if (i == 5) + { + return -1; + } + + for (j = k = 0; j < 5; ++j) + { + if (strnicmp (Lumps[glheader+k].Name, GLLumpNames[j], 8) == 0) + { + if (i == j) + { + return glheader+k; + } + k++; + } + } + return -1; +} + +bool FWadReader::IsGLNodes (int index) const +{ + if (index + 4 >= Header.NumLumps) + { + return false; + } + if (Lumps[index].Name[0] != 'G' || + Lumps[index].Name[1] != 'L' || + Lumps[index].Name[2] != '_') + { + return false; + } + index++; + for (int i = 0; i < 4; ++i) + { + if (strnicmp (Lumps[i+index].Name, GLLumpNames[i], 8) != 0) + { + return false; + } + } + return true; +} + +int FWadReader::SkipGLNodes (int index) const +{ + index++; + for (int i = 0; i < 5 && index < Header.NumLumps; ++i, ++index) + { + if (strnicmp (Lumps[index].Name, GLLumpNames[i], 8) != 0) + { + break; + } + } + return index; +} + +bool FWadReader::MapHasBehavior (int map) const +{ + return FindLump ("BEHAVIOR", map) != -1; +} + +int FWadReader::NextMap (int index) const +{ + if (index < 0) + { + index = 0; + } + else + { + index++; + } + for (; index < Header.NumLumps; ++index) + { + if (IsMap (index)) + { + return index; + } + } + return -1; +} + +int FWadReader::LumpAfterMap (int i) const +{ + int j, k; + + ++i; + for (j = k = 0; j < 12; ++j) + { + if (strnicmp (Lumps[i+k].Name, MapLumpNames[j], 8) != 0) + { + if (MapLumpRequired[j]) + { + break; + } + } + else + { + k++; + } + } + return i+k; +} + +void FWadReader::SafeRead (void *buffer, size_t size) +{ + if (fread (buffer, 1, size, File) != size) + { + throw exception("Failed to read"); + } +} + +const char *FWadReader::LumpName (int lump) +{ + static char name[9]; + strncpy (name, Lumps[lump].Name, 8); + name[8] = 0; + return name; +} + +FWadWriter::FWadWriter (const char *filename, bool iwad) + : File (NULL) +{ + File = fopen (filename, "wb"); + if (File == NULL) + { + throw exception("Could not open output file"); + } + + WadHeader head; + + if (iwad) + { + head.Magic[0] = 'I'; + } + else + { + head.Magic[0] = 'P'; + } + head.Magic[1] = 'W'; + head.Magic[2] = 'A'; + head.Magic[3] = 'D'; + + SafeWrite (&head, sizeof(head)); +} + +FWadWriter::~FWadWriter () +{ + if (File) + { + Close (); + } +} + +void FWadWriter::Close () +{ + if (File) + { + __int32 head[2]; + + head[0] = LONG(Lumps.Size()); + head[1] = LONG(ftell (File)); + + SafeWrite (&Lumps[0], sizeof(WadLump)*Lumps.Size()); + fseek (File, 4, SEEK_SET); + SafeWrite (head, 8); + fclose (File); + File = NULL; + } +} + +void FWadWriter::CreateLabel (const char *name) +{ + WadLump lump; + + strncpy (lump.Name, name, 8); + lump.FilePos = LONG(ftell (File)); + lump.Size = 0; + Lumps.Push (lump); +} + +void FWadWriter::WriteLump (const char *name, const void *data, int len) +{ + WadLump lump; + + strncpy (lump.Name, name, 8); + lump.FilePos = LONG(ftell (File)); + lump.Size = LONG(len); + Lumps.Push (lump); + + SafeWrite (data, len); +} + +void FWadWriter::CopyLump (FWadReader &wad, int lump) +{ + BYTE *data; + int size; + + ReadLump (wad, lump, data, size); + if (data != NULL) + { + WriteLump (wad.LumpName (lump), data, size); + delete[] data; + } +} + +void FWadWriter::StartWritingLump (const char *name) +{ + CreateLabel (name); +} + +void FWadWriter::AddToLump (const void *data, int len) +{ + SafeWrite (data, len); + Lumps[Lumps.Size()-1].Size += len; +} + +void FWadWriter::SafeWrite (const void *buffer, size_t size) +{ + if (fwrite (buffer, 1, size, File) != size) + { + fclose (File); + File = NULL; + throw exception( + "Failed to write. Check that this directory is writable and\n" + "that you have enough free disk space."); + } +} diff --git a/wad.h b/wad.h new file mode 100644 index 0000000..0e243b4 --- /dev/null +++ b/wad.h @@ -0,0 +1,107 @@ +#ifndef __WAD_H__ +#define __WAD_H__ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include + +#include "zdbsp.h" +#include "tarray.h" + +struct WadHeader +{ + char Magic[4]; + __int32 NumLumps; + __int32 Directory; +}; + +struct WadLump +{ + __int32 FilePos; + __int32 Size; + char Name[8]; +}; + +class FWadReader +{ +public: + FWadReader (const char *filename); + ~FWadReader (); + + bool IsIWAD () const; + int FindLump (const char *name, int index=0) const; + int FindMapLump (const char *name, int map) const; + int FindGLLump (const char *name, int glheader) const; + const char *LumpName (int lump); + bool IsMap (int index) const; + bool IsGLNodes (int index) const; + int SkipGLNodes (int index) const; + bool MapHasBehavior (int map) const; + int NextMap (int startindex) const; + int LumpAfterMap (int map) const; + int NumLumps () const; + + void SafeRead (void *buffer, size_t size); + +// VC++ 6 does not support template member functions in non-template classes! + template + friend void ReadLump (FWadReader &wad, int index, T *&data, int &size); + +private: + WadHeader Header; + WadLump *Lumps; + FILE *File; +}; + + +template +void ReadLump (FWadReader &wad, int index, T *&data, int &size) +{ + if ((unsigned)index >= (unsigned)wad.Header.NumLumps) + { + data = NULL; + size = 0; + return; + } + if (fseek (wad.File, wad.Lumps[index].FilePos, SEEK_SET)) + { + throw exception("Failed to seek"); + } + size = wad.Lumps[index].Size / sizeof(T); + data = new T[size]; + wad.SafeRead (data, size*sizeof(T)); +} + +template +void ReadMapLump (FWadReader &wad, const char *name, int index, T *&data, int &size) +{ + ReadLump (wad, wad.FindMapLump (name, index), data, size); +} + + +class FWadWriter +{ +public: + FWadWriter (const char *filename, bool iwad); + ~FWadWriter (); + + void CreateLabel (const char *name); + void WriteLump (const char *name, const void *data, int len); + void CopyLump (FWadReader &wad, int lump); + void Close (); + + // Routines to write a lump in segments. + void StartWritingLump (const char *name); + void AddToLump (const void *data, int len); + +private: + TArray Lumps; + FILE *File; + + void SafeWrite (const void *buffer, size_t size); +}; + +#endif //__WAD_H__ \ No newline at end of file diff --git a/workdata.h b/workdata.h new file mode 100644 index 0000000..20c8dad --- /dev/null +++ b/workdata.h @@ -0,0 +1,29 @@ +#ifndef __WORKDATA_H__ +#define __WORKDATA_H__ + +#ifdef _MSC_VER +#pragma once +#endif + +#include "zdbsp.h" + +struct vertex_t +{ + fixed_t x, y; +}; + +struct node_t +{ + fixed_t x, y, dx, dy; + fixed_t bbox[2][4]; + unsigned int intchildren[2]; +}; + +struct subsector_t +{ + DWORD numlines; + DWORD firstline; +}; + + +#endif //__WORKDATA_H__ \ No newline at end of file diff --git a/zdbsp.dsp b/zdbsp.dsp new file mode 100644 index 0000000..278420b --- /dev/null +++ b/zdbsp.dsp @@ -0,0 +1,398 @@ +# Microsoft Developer Studio Project File - Name="zdbsp" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=zdbsp - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "zdbsp.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "zdbsp.mak" CFG="zdbsp - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "zdbsp - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "zdbsp - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "zdbsp - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir ".\Release" +# PROP BASE Intermediate_Dir ".\Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir ".\Release" +# PROP Intermediate_Dir ".\Release" +# PROP Target_Dir "" +MTL=midl.exe +# ADD BASE MTL /nologo /tlb".\Release\zdbsp.tlb" /win32 +# ADD MTL /nologo /tlb".\Release\zdbsp.tlb" /win32 +# ADD BASE CPP /nologo /W3 /GX /Zi /Ot /Og /Oi /Oy /Ob2 /Gy /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /GA /GF /c +# ADD CPP /nologo /MD /W3 /GX /Zi /Ot /Og /Oi /Oy /Ob2 /Gy /I "zlib" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /GA /GF /c +# SUBTRACT CPP /YX /Yc /Yu +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# SUBTRACT BASE LINK32 /pdb:none +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "zdbsp - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir ".\Debug" +# PROP BASE Intermediate_Dir ".\Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir ".\Debug" +# PROP Intermediate_Dir ".\Debug" +# PROP Target_Dir "" +MTL=midl.exe +# ADD BASE MTL /nologo /tlb".\Debug\zdbsp.tlb" /win32 +# ADD MTL /nologo /tlb".\Debug\zdbsp.tlb" /win32 +# ADD BASE CPP /nologo /W3 /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /GZ /c +# ADD CPP /nologo /W3 /GX /ZI /Od /I "zlib" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# SUBTRACT BASE LINK32 /pdb:none +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "zdbsp - Win32 Release" +# Name "zdbsp - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Group "Reject(ed)" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Unused\rejectbuilder.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\Unused\rejectbuilder.h +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\Unused\vis.cpp +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\Unused\visflow.cpp +# PROP Exclude_From_Build 1 +# End Source File +# End Group +# Begin Source File + +SOURCE=.\blockmapbuilder.cpp +DEP_CPP_BLOCK=\ + ".\blockmapbuilder.h"\ + ".\doomdata.h"\ + ".\tarray.h"\ + ".\templates.h"\ + ".\workdata.h"\ + ".\zdbsp.h"\ + +# End Source File +# Begin Source File + +SOURCE=.\getopt.c +DEP_CPP_GETOP=\ + ".\getopt.h"\ + +# End Source File +# Begin Source File + +SOURCE=getopt1.c +DEP_CPP_GETOPT=\ + ".\getopt.h"\ + +# End Source File +# Begin Source File + +SOURCE=.\main.cpp +DEP_CPP_MAIN_=\ + ".\blockmapbuilder.h"\ + ".\doomdata.h"\ + ".\getopt.h"\ + ".\nodebuild.h"\ + ".\processor.h"\ + ".\tarray.h"\ + ".\wad.h"\ + ".\workdata.h"\ + ".\zdbsp.h"\ + +# End Source File +# Begin Source File + +SOURCE=.\nodebuild.cpp +DEP_CPP_NODEB=\ + ".\doomdata.h"\ + ".\nodebuild.h"\ + ".\tarray.h"\ + ".\templates.h"\ + ".\workdata.h"\ + ".\zdbsp.h"\ + +# End Source File +# Begin Source File + +SOURCE=nodebuild_events.cpp +DEP_CPP_NODEBU=\ + ".\doomdata.h"\ + ".\nodebuild.h"\ + ".\tarray.h"\ + ".\workdata.h"\ + ".\zdbsp.h"\ + +# End Source File +# Begin Source File + +SOURCE=nodebuild_extract.cpp +DEP_CPP_NODEBUI=\ + ".\doomdata.h"\ + ".\nodebuild.h"\ + ".\tarray.h"\ + ".\templates.h"\ + ".\workdata.h"\ + ".\zdbsp.h"\ + +# End Source File +# Begin Source File + +SOURCE=nodebuild_gl.cpp +DEP_CPP_NODEBUIL=\ + ".\doomdata.h"\ + ".\nodebuild.h"\ + ".\tarray.h"\ + ".\workdata.h"\ + ".\zdbsp.h"\ + +# End Source File +# Begin Source File + +SOURCE=nodebuild_utility.cpp +DEP_CPP_NODEBUILD=\ + ".\doomdata.h"\ + ".\nodebuild.h"\ + ".\tarray.h"\ + ".\workdata.h"\ + ".\zdbsp.h"\ + +# End Source File +# Begin Source File + +SOURCE=.\processor.cpp +DEP_CPP_PROCE=\ + ".\blockmapbuilder.h"\ + ".\doomdata.h"\ + ".\nodebuild.h"\ + ".\processor.h"\ + ".\tarray.h"\ + ".\wad.h"\ + ".\workdata.h"\ + ".\zdbsp.h"\ + +# End Source File +# Begin Source File + +SOURCE=.\view.cpp +DEP_CPP_VIEW_=\ + ".\doomdata.h"\ + ".\tarray.h"\ + ".\templates.h"\ + ".\zdbsp.h"\ + +# End Source File +# Begin Source File + +SOURCE=.\wad.cpp +DEP_CPP_WAD_C=\ + ".\tarray.h"\ + ".\wad.h"\ + ".\zdbsp.h"\ + +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\blockmapbuilder.h +# End Source File +# Begin Source File + +SOURCE=.\doomdata.h +# End Source File +# Begin Source File + +SOURCE=getopt.h +# End Source File +# Begin Source File + +SOURCE=.\nodebuild.h +# End Source File +# Begin Source File + +SOURCE=.\processor.h +# End Source File +# Begin Source File + +SOURCE=.\resource.h +# End Source File +# Begin Source File + +SOURCE=.\tarray.h +# End Source File +# Begin Source File + +SOURCE=.\templates.h +# End Source File +# Begin Source File + +SOURCE=.\wad.h +# End Source File +# Begin Source File + +SOURCE=.\workdata.h +# End Source File +# Begin Source File + +SOURCE=.\zdbsp.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\resource.rc +# End Source File +# End Group +# Begin Group "zlib" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\zlib\adler32.c +DEP_CPP_ADLER=\ + ".\zlib\zconf.h"\ + ".\zlib\zlib.h"\ + +# End Source File +# Begin Source File + +SOURCE=.\zlib\compress.c +DEP_CPP_COMPR=\ + ".\zlib\zconf.h"\ + ".\zlib\zlib.h"\ + +# End Source File +# Begin Source File + +SOURCE=.\zlib\crc32.c +DEP_CPP_CRC32=\ + ".\zlib\crc32.h"\ + ".\zlib\zconf.h"\ + ".\zlib\zlib.h"\ + ".\zlib\zutil.h"\ + +# End Source File +# Begin Source File + +SOURCE=.\zlib\crc32.h +# End Source File +# Begin Source File + +SOURCE=.\zlib\deflate.c +DEP_CPP_DEFLA=\ + ".\zlib\deflate.h"\ + ".\zlib\zconf.h"\ + ".\zlib\zlib.h"\ + ".\zlib\zutil.h"\ + +# End Source File +# Begin Source File + +SOURCE=.\zlib\deflate.h +# End Source File +# Begin Source File + +SOURCE=.\zlib\trees.c +DEP_CPP_TREES=\ + ".\zlib\deflate.h"\ + ".\zlib\trees.h"\ + ".\zlib\zconf.h"\ + ".\zlib\zlib.h"\ + ".\zlib\zutil.h"\ + +# End Source File +# Begin Source File + +SOURCE=.\zlib\trees.h +# End Source File +# Begin Source File + +SOURCE=.\zlib\zconf.h +# End Source File +# Begin Source File + +SOURCE=.\zlib\zlib.h +# End Source File +# Begin Source File + +SOURCE=.\zlib\zutil.c +DEP_CPP_ZUTIL=\ + ".\zlib\zconf.h"\ + ".\zlib\zlib.h"\ + ".\zlib\zutil.h"\ + +# End Source File +# Begin Source File + +SOURCE=.\zlib\zutil.h +# End Source File +# End Group +# Begin Source File + +SOURCE=.\zdbsp.html +# End Source File +# End Target +# End Project diff --git a/zdbsp.dsw b/zdbsp.dsw new file mode 100644 index 0000000..c4acad7 --- /dev/null +++ b/zdbsp.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "zdbsp"=zdbsp.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/zdbsp.h b/zdbsp.h new file mode 100644 index 0000000..5f86035 --- /dev/null +++ b/zdbsp.h @@ -0,0 +1,134 @@ +#ifndef __ZDBSP_H__ +#define __ZDBSP_H__ + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include + +#define ZDBSP_VERSION "1.5" + +enum EBlockmapMode +{ + EBM_Rebuild, + EBM_Create0 +}; + +enum ERejectMode +{ + ERM_DontTouch, + ERM_CreateZeroes, + ERM_Create0, + ERM_Rebuild +}; + +extern const char *Map; +extern const char *InName; +extern const char *OutName; +extern bool BuildNodes, BuildGLNodes, ConformNodes, GLOnly; +extern bool NoPrune; +extern EBlockmapMode BlockmapMode; +extern ERejectMode RejectMode; +extern int MaxSegs; +extern int SplitCost; +extern int AAPreference; +extern bool CheckPolyobjs; +extern bool ShowMap; +extern bool CompressNodes, CompressGLNodes; + + +#define FIXED_MAX INT_MAX +#define FIXED_MIN INT_MIN + +#define FRACBITS 16 + +typedef int fixed_t; +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef signed short SWORD; +typedef unsigned long DWORD; +typedef unsigned __int32 angle_t; + +angle_t PointToAngle (fixed_t x, fixed_t y); + +static const WORD NO_INDEX = 0xffff; +static const angle_t ANGLE_MAX = 0xffffffff; +static const DWORD DWORD_MAX = 0xffffffff; +static const angle_t ANGLE_180 = (1u<<31); +static const angle_t ANGLE_EPSILON = 5000; + +void Warn (const char *format, ...); + +#ifdef _MSC_VER + +#pragma warning (disable: 4035) + +inline fixed_t Scale (fixed_t a, fixed_t b, fixed_t c) +{ + __asm mov eax,a + __asm mov ecx,c + __asm imul b + __asm idiv ecx +} + +inline fixed_t DivScale30 (fixed_t a, fixed_t b) +{ + __asm mov edx,a + __asm sar edx,2 + __asm mov eax,a + __asm shl eax,30 + __asm idiv b +} + +inline fixed_t MulScale30 (fixed_t a, fixed_t b) +{ + __asm mov eax,a + __asm imul b + __asm shrd eax,edx,30 +} + +inline fixed_t DMulScale32 (fixed_t a, fixed_t b, fixed_t c, fixed_t d) +{ + __asm mov eax,a + __asm imul b + __asm mov ebx,eax + __asm mov eax,c + __asm mov esi,edx + __asm imul d + __asm add eax,ebx + __asm adc edx,esi + __asm mov eax,edx +} + +#pragma warning (default: 4035) + +#else + +inline fixed_t Scale (fixed_t a, fixed_t b, fixed_t c) +{ + return (fixed_t)(double(a)*double(b)/double(c)); +} + +inline fixed_t DivScale30 (fixed_t a, fixed_t b) +{ + return (fixed_t)(double(a)/double(b)*double(1<<30)); +} + +inline fixed_t MulScale30 (fixed_t a, fixed_t b) +{ + return (fixed_t)(double(a)*double(b)/double(1<<30)); +} + +inline fixed_t DMulScale30 (fixed_t a, fixed_t b, fixed_t c, fixed_t d) +{ + return (fixed_t)((double(a)*double(b)+double(c)*double(d))/double(1<<30)); +} + +#endif + +#define SHORT(x) (x) +#define LONG(x) (x) + +#endif //__ZDBSP_H__ \ No newline at end of file diff --git a/zdbsp.html b/zdbsp.html new file mode 100644 index 0000000..2f71301 --- /dev/null +++ b/zdbsp.html @@ -0,0 +1,476 @@ + + + ZDBSP Documentation + + + + +

About ZDBSP

+

ZDBSP is a stand-alone version of ZDoom's internal node builder. This node + builder was written with two design goals in mind:

+
    +
  1. + It must be fast. The original node builder was just going to be used to fix map + errors in ZDoom. After adding GL nodes support, ZDoomGL also started using it + as a preprocessing step for any maps without existing GL nodes. In both + cases, the node builder needed to be quick in order to minimize the + time the user was forced to wait before playing a map. +
  2. + Polyobject bleeding must be minimized. The node builder was tested with the + standard Hexen maps and several user maps, and it has been able to prevent + bleeding on all properly-formed maps. (Note that it is impossible to prevent + polyobject bleeding in all circumstances. See the polyobjects section below for + rules you should follow for best results.)
+

License

+

This program is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public + License as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version.

+

This program is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + PARTICULAR PURPOSE. See the GNU General Public License for more details.

+

You can also find a copy of the license as the file COPYING in the source + distribution.

+

Using ZDBSP with WadAuthor

+

First, copy zdbsp.exe to your WadAuthor directory. Then start WadAuthor and + choose "Options..." from its Tools menu. Select the "Files" tab and choose + "External" for Node Building. This activates the Command text box so that you + can type text into it. Copy and paste any of the following into it based on + what you want to do.

+

Create a map that can be played with Doom or any source port:

+
+

zdbsp -R -m$_Mapname $_Wadfile -o$_Wadfile

+
+

Create a map that will only be played with ZDoom. This will be + ZDoom-only because it creates a zero-length REJECT lump. ZDoom is smart enough + to know that a zero-length REJECT lump is the same thing as a REJECT lump + filled with zeros, so you can save space:

+
+

zdbsp -r -m$_Mapname $_Wadfile -o$_Wadfile

+
+

Create a map with both regular and GL nodes so that ZDoomGL does not + need to build GL nodes:

+
+

zdbsp -rg -m$_Mapname $_Wadfile -o$_Wadfile

+
+

ZDBSP command line options

+

ZDBSP supports several command line options to affect its behavior. If + you forget what they are, you can view a quick summary of them by running ZDBSP + without any options. They are also listed below in more detail than the listing + ZDBSP provides. Note that these options are case-sensitive, so -r is not the + same thing as -R. You can use either the long form of an option or the short + form depending on your preference.

+
+
--help
+
+ Displays a summary of all ZDBSP's command line options, as if you had run ZDBSP + without any options.
+
--version or -V
+
+ Displays ZDBSP's version number.
+
--map=MAP or -mMAP
+
+ When you use this option, ZDBSP will only build the nodes for one map in a wad + instead of rebuilding the nodes for every map contained in the wad. MAP should + be the map's full name (e.g. MAP01 or E1M1)
+
--output=FILE or -oFILE
+
+ Normally, ZDBSP creates a new wad named tmp.wad. You can use this option to + make it write to a different file instead. In the case of WadAuthor, this is + used to make ZDBSP overwrite the original file with the new one, because that + is what WadAuthor expects the nodebuilder to do.
+
--no-prune or -q
+
+ When you use this option, ZDBSP will not remove unused sidedefs or sectors from + a map. ZDBSP will always remove 0-length linedefs from a map even when you use + this option because it is possible for them to make the game crash under + certain circumstances. Moreover, ZDoom will itself remove 0-length linedefs and + rebuild the nodes if it finds any.
+
--no-nodes or -N
+
+ This option causes ZDBSP to take the node information from the old wad file and + write it to the new wad file. I can't think of any reason why you would want to + do this, but it is provided as an option nonetheless.
+
--gl or -g
+
+ This option causes ZDBSP to build two sets of nodes for a map: One set of + regular nodes and one set of GL nodes. Because ZDBSP is doing twice the work, + it will take twice as long to finish.
+
--gl-matching or -G
+
+ Like the previous option, this one will also make ZDBSP generate GL nodes. + However, it will only build one set of nodes and then strip the extra GL + information from them to create the normal nodes. Because of this, it is faster + than the previous option when you want to create GL nodes, but the normal nodes + will generally be less efficient because they were created from the GL nodes.
+
--compress or -z
+
Compresses the node information. Compressed nodes take up less space than + uncompressed nodes, and they are also the only way to save nodes with more than 65535 segs. + If GL nodes are built, they will be compressed as well.
+
--compress-normal or -Z
+
This is exactly like the previous option, except GL nodes will not be compressed.
+
--empty-blockmap or -b
+
+ This option writes a zero-length BLOCKMAP to the wad. As of this writing, ZDoom + is the only port that will detect this and build the BLOCKMAP itself.
+
--empty-reject or -r
+
+ When this option is used, ZDBSP will write a zero-length REJECT to the wad. As + of this writing, ZDoom is the only port that supports this. A zero-length + REJECT is the same thing as a reject filled with zeros. Since ZDBSP does not + generate a REJECT table, and the usefulness of having a REJECT is questionable, + you should always use this option if you intend for the map to be played solely + with ZDoom.
+
--zero-reject or -R
+
+ This option is similar to the previous one, except ZDBSP will actually write + out a full-sized REJECT lump filled with zeros. If you play with ZDoom, this is + just wasted space, but other ports and Doom itself require a full-sized REJECT + lump to work.
+
--no-reject or -E
+
+ This option makes ZDBSP copy the REJECT lump from the old wad to the new wad + unchanged. However, if it detects that the old REJECT is the wrong size for the + number of sectors in the map, it will be removed as if you had used the + --empty-reject option.
+
--no-polyobjs or -P
+
+ This option disables ZDBSP's polyobject detection code. If you are building + nodes for a map without polyobjects, you might be able to save a fraction of a + second from the build time by using this option, but there is generally no + reason to use it.
+
--no-timing or -t
+
+ If you don't care how long it takes to build nodes, use this option and ZDBSP + won't tell you.
+
--warn or -w
+
+ Displays extra warning messages that ZDBSP might generate while building GL + nodes. The nodes will still be usable if warnings occur, and warnings are not + unlikely. If you have strange display problems with GL nodes, turning on the + warning messages may help you locate the problem.
+
--partition=NNN or -pNNN
+
+ This option controls the maximum number of segs that will be considered for + splitters at each node. The default is 64. By increasing it, you might be able + to generate "better" nodes, but you get diminishing returns the higher you make + it. High values are also slower than low ones.
+
--split-cost=NNN or -sNNN
+
+ This option adjusts how hard ZDBSP tries to avoid splitting segs. The default + value is 8. By increasing this, ZDBSP will try harder to avoid splitting segs. + If you decrease it, ZDBSP will split segs more frequently. More splits mean the + BSP tree will usually be more balanced, but it will also take up more room.
+
--diagonal-cost=NNN or -dNNN
+
+ This option controls how hard ZDBSP tries to avoid using diagonal splitters. + The default value is 16. A higher value means that diagonal splitters will be + more likely to be used. This can sometimes help to reduce the number of segs in + a map. The reason avoiding diagonal splitters is important is because normal + nodes do not store any fractional information for the vertices that segs use, + so you are more likely to see slime trails with diagonal splitters than with + horizontal or vertical splitters.
+
--view or -v
+
+ Under Windows, this displays a viewer that will let you inspect a map's + subsectors, nodes, and REJECT. To scroll the map around, drag it with your + right mouse button. The viewer was created to assist with debugging with the GL + nodes. It is not very user-friendly nor is it bug-free. In fact, if you try to + build nodes for more than one map with this option, there is a good chance + ZDBSP will crash.
+
+

Polyobjects

+

One of ZDBSP's most important features is that it tries very hard to avoid + polyobject bleeding. To make this feature work, however, you must design your + maps properly. You do this be ensuring that your polyobjects are always shown + inside a convex sector. This is referring to the area where the + polyobjects appear in the game, not where they appear in an editor. If you do + not know what convex means, think of triangles, squares and circles. They are + all convex. If you have a sector shaped like the letter C, that is not convex.

+

Ironically, an example of what not to do is in Hexen's MAP01. The + polyobject doors at the very start of that map do not appear in a convex area. + ZDBSP has been tuned so that they will display properly with the default + options, but you should not count on this for your own maps.

+

If ZDBSP has to split a linedef around a polyobject's display area, it will + print a message indicating where this happened so that you can inspect the area + and fix it. For example, when you use ZDBSP on hexen.wad, it outputs these + warnings for MAP01:

+
   Split seg 929 (-1176,1312)-(-1088,1312) of sector 265 on line 572
+   Split seg 931 (-1088,1312)-(-1000,1312) of sector 265 on line 573
+

The important pieces of information here are the coordinates of the segs that + were split and the lines those segs were made from. In this case, both lines + 572 and 573 were split. Because they surround a polyobject's display area, + ideally they should not be split, so you need to look at the map in an editor + to determine what made them split. This happens because the area is not convex, + so you need to look for a line in the same "loop" that, if you made it longer, + could cross the line that was split. In this example, lines 575 and 579 are + responsible for splitting line 572, and lines 576 and 578 are responsible for + splitting line 573. You can fix the error by reworking the area so that it is + convex, either by moving lines 575, 579, 576, and 578, or by adding additional + lines to separate each half into four convex pieces. +

+

Below are some illustrations. The areas where the two polyobjects appear have + been highlighted.

+
+

This first image shows the area as it appears in hexen.wad, + unmodified. Note that neither side is convex, so ZDBSP will need to split each + side into two subsectors. The extra subsectors have been highlighted in a + brighter color to illustrate how ZDBSP splits them.
+ The Original Area

+

In the next image, the lines have been moved to make each side + convex.
+ Convex Areas made by Moving Lines +

+

In the final image, new two-sided lines have been added to make + each polyobject's display area rectangular. Unlike the line-moving method + above, this does not alter the area's appearance. Notice that this looks a lot + like the way ZDBSP split them in the first image. The difference is that by + adding the extra lines yourself, you have control over how the split is done + and ZDBSP does not need to guess about the best way to make the area convex.
+ Convex Areas made by Adding Lines

+
+

Unfortunately, if you use WadAuthor, ZDBSP's output window will not normally + appear long enough for you to actually read it. You can get around this by + adding the -v option to the zdbsp command line that WadAuthor uses (see the + Using ZDBSP with WadAuthor section). Then its output will stay around until you + close the ZDBSP viewer.

+

Compressed Nodes

+

The following information describes the format used to store compressed nodes. + If you are just a regular user, you can skip this section.

+

Compressed nodes contain all the same information as regular nodes but are + stored in a single lump and compressed as a zlib data stream. They also support + more than 65535 segs and vertices by storing them using four bytes instead of + two.

+

The first four bytes of the compressed nodes are a four-character (uncompressed) + signature. This can be either 'ZNOD' for regular nodes or 'ZGLN' for GL nodes. + Following the signature is the zlib data stream. For a description of its + format, see the zlib documentation. When you decompress it, the following + information is obtained in this order:

+
    +
  1. + Vertex information
  2. +
  3. + Subsector information
  4. +
  5. + Segs information
  6. +
  7. + Nodes information
+

Types

+

The following sections use these types:

+
+
BYTE
A single unsigned byte. The number can be in the range [0, + 255].
+
WORD
An unsigned integer stored in two bytes. The number can be + in the range [0, 65535].
+
SWORD
A signed integer stored in two bytes. The number can be + in the range [-32768, 32767].
+
DWORD
An unsigned integer stored in four bytes. The number can + be in the range [0, 4294967295].
+
FIXED
A signed 16.16 fixed-point number stored in four bytes. The + most-significant two bytes store the integer part of the number, and the + least-significant two bytes store the fractional part of the number. The number can + be in the approximate range [-32768.99998, 32767.99998].
+
+

All multi-byte numbers are stored with their least significant byte first (i.e. + little-endian).

+

Vertex information

+ + + + + + + + + + + + + + + + + + + + + + + + +
DWORDOrgVertsNumber of vertices used from the VERTEXES lump
DWORDNewVertsNumber of additional vertices that follow
Repeat NewVerts times:
FIXEDXX-Coordinate
FIXEDYY-Coordinate
+

These are all the additional vertices that the segs use. Unlike normal + nodes, extra vertices are not added to the VERTEXES lump. When determining + which vertex v a seg uses, if v < OrgVerts, then v is + a vertex in the VERTEXES lump. Otherwise, when v >= OrgVerts, v + - OrgVerts is the index of a vertex stored here.

+

Like version 2 GL nodes, the vertices stored here are represented as 16.16 fixed + point numbers in order to maintain the full precision of the Doom engine.

+

Subsector information

+ + + + + + + + + + + + + + +
DWORDNumSubsectorsNumber of subsectors
Repeat NumSubsectors times:
DWORDNumSegsNumber of segs for this subsector
+

Unlike normal nodes, the first seg in each subsector is not stored. This can be + inferred by the ordering of the subsectors. The first subsector always starts + at seg 0, and each following subsector begins with the seg immediately after + the previous subsector's last seg.

+

Segs information

+

The specific layout for this section depends on whether the data stream + represents normal nodes or GL nodes. In each case, the segs are stored in an 11 + byte structure, but their contents differ slightly. There is no padding to make + these align to DWORD boundaries. +

+

Normal nodes ('ZNOD' signature)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DWORDNumSegsNumber of segs
Repeat NumSegs times:
DWORDv1Seg's first vertex
DWORDv2Seg's second vertex
WORDlineThe linedef this seg came from
BYTEsideThe linedef's side this seg came from (0=front, 1=back)
+

Note that unlike uncompressed nodes, the seg's angle and offset are not + included. ZDoom does not need this information, and other ports that wish to + support compressed nodes can recompute them trivially.

+

GL nodes ('ZGLN' signature)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DWORDNumSegsNumber of segs
Repeat NumSegs times:
DWORDv1Seg's first vertex
DWORDpartnerSeg's partner seg (0xFFFFFFFF if none)
WORDlineThe linedef this seg came from (0xFFFFFFFF if none)
BYTEsideThe linedef's side this seg came from (0=front, 1=back) (ignored if no line)
+

Unlike standard GL nodes, each seg's second vertex is not stored. This is + because GL subsectors must form a closed area. In other words, one seg's second + vertex is the same as the next seg's first vertex. The subsector information + contains everything you need to know to close each area and start a new one.

+

Example: Suppose you have not read any segs yet, and the first subsector has + four segs. Therefore, the second vertex for the first four segs can be + determined to be:

+
+ + + + + + + + + + + + + + + + +
Seg 0Second vertex is Seg 1's first vertex
Seg 1Second vertex is Seg 2's first vertex
Seg 2Second vertex is Seg 3's first vertex
Seg 3Second vertex is Seg 0's first vertex (because this is the last seg in the + subsector)
+
+

So for each subsector, all but the last seg's second vertex will be the same as + the next seg's first vertex. The last seg's second vertex will be the same as + the first seg's first vertex.

+

Node information

+

This is really no different from standard nodes.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DWORDNumNodesNumber of nodes
Repeat NumNodes times:
4 SWORDsX, Y, dX, dYSplitter for this node
4 SWORDsTop, Bottom, Left, RightBounding box for Child0
4 SWORDsTop, Bottom, Left, RightBounding box for Child1
2 DWORDsChild0, Child1References to child nodes or subsectors
+

As with standard nodes, a child's high bit is set to indicate that it is a + subsector and cleared to indicate that it is another node. Just remember that + the child references are stored using four bytes each instead of two.

+

Compressed node storage

+

Normal nodes (signature 'ZNOD') are stored in a map's NODES lump. Bear in mind + that compressed nodes are contained entirely within a single lump rather than + in three separate lumps.

+

GL nodes (signature 'ZGLN') are stored in a map's SSECTORS lump. Unlike regular + GL nodes, no new lumps are created for compressed GL nodes.

+

The SEGS lump is left unused and should be written with a length of 0. I may + find some way to "hijack" this lump in the future, so programs that read + compressed nodes must not assume that if the SEGS lump is non-empty, then + compressed nodes are not used.

+ + diff --git a/zdbsp.sln b/zdbsp.sln new file mode 100644 index 0000000..41aa278 --- /dev/null +++ b/zdbsp.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zdbsp", "zdbsp.vcproj", "{3C32111E-76E8-47C9-95A6-A103C861F45F}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {3C32111E-76E8-47C9-95A6-A103C861F45F}.Debug.ActiveCfg = Debug|Win32 + {3C32111E-76E8-47C9-95A6-A103C861F45F}.Debug.Build.0 = Debug|Win32 + {3C32111E-76E8-47C9-95A6-A103C861F45F}.Release.ActiveCfg = Release|Win32 + {3C32111E-76E8-47C9-95A6-A103C861F45F}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/zdbsp.vcproj b/zdbsp.vcproj new file mode 100644 index 0000000..e26457a --- /dev/null +++ b/zdbsp.vcproj @@ -0,0 +1,334 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/zipbin.bat b/zipbin.bat new file mode 100644 index 0000000..48437df --- /dev/null +++ b/zipbin.bat @@ -0,0 +1 @@ +kzip %1 release\zdbsp.exe zdbsp.html *.png \ No newline at end of file diff --git a/zipsrc.bat b/zipsrc.bat new file mode 100644 index 0000000..afda798 --- /dev/null +++ b/zipsrc.bat @@ -0,0 +1,4 @@ +zip -9 %1 COPYING *.cpp *.h *.c *.rc *.ds? *.sln *.vcproj unused/* zlib/* +kzip b%1 COPYING *.cpp *.h *.c *.rc *.ds? *.sln *.vcproj +zipmix %1 b%1 +del b%1 \ No newline at end of file diff --git a/zlib/adler32.c b/zlib/adler32.c new file mode 100644 index 0000000..dd6d60f --- /dev/null +++ b/zlib/adler32.c @@ -0,0 +1,74 @@ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995-2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +#define BASE 65521UL /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf,i) {s1 += buf[i]; s2 += s1;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +#ifdef NO_DIVIDE +# define MOD(a) \ + do { \ + if (a >= (BASE << 16)) a -= (BASE << 16); \ + if (a >= (BASE << 15)) a -= (BASE << 15); \ + if (a >= (BASE << 14)) a -= (BASE << 14); \ + if (a >= (BASE << 13)) a -= (BASE << 13); \ + if (a >= (BASE << 12)) a -= (BASE << 12); \ + if (a >= (BASE << 11)) a -= (BASE << 11); \ + if (a >= (BASE << 10)) a -= (BASE << 10); \ + if (a >= (BASE << 9)) a -= (BASE << 9); \ + if (a >= (BASE << 8)) a -= (BASE << 8); \ + if (a >= (BASE << 7)) a -= (BASE << 7); \ + if (a >= (BASE << 6)) a -= (BASE << 6); \ + if (a >= (BASE << 5)) a -= (BASE << 5); \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +#else +# define MOD(a) a %= BASE +#endif + +/* ========================================================================= */ +uLong ZEXPORT adler32(adler, buf, len) + uLong adler; + const Bytef *buf; + uInt len; +{ + unsigned long s1 = adler & 0xffff; + unsigned long s2 = (adler >> 16) & 0xffff; + int k; + + if (buf == Z_NULL) return 1L; + + while (len > 0) { + k = len < NMAX ? (int)len : NMAX; + len -= k; + while (k >= 16) { + DO16(buf); + buf += 16; + k -= 16; + } + if (k != 0) do { + s1 += *buf++; + s2 += s1; + } while (--k); + MOD(s1); + MOD(s2); + } + return (s2 << 16) | s1; +} diff --git a/zlib/compress.c b/zlib/compress.c new file mode 100644 index 0000000..5a7eeee --- /dev/null +++ b/zlib/compress.c @@ -0,0 +1,79 @@ +/* compress.c -- compress a memory buffer + * Copyright (C) 1995-2002 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least 0.1% larger than sourceLen plus + 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ +int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; + int level; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; +#ifdef MAXSEG_64K + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; +#endif + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + err = deflateInit(&stream, level); + if (err != Z_OK) return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = deflateEnd(&stream); + return err; +} + +/* =========================================================================== + */ +int ZEXPORT compress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); +} + +/* =========================================================================== + If the default memLevel or windowBits for deflateInit() is changed, then + this function needs to be updated. + */ +uLong ZEXPORT compressBound (sourceLen) + uLong sourceLen; +{ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + 11; +} diff --git a/zlib/crc32.c b/zlib/crc32.c new file mode 100644 index 0000000..235fa9e --- /dev/null +++ b/zlib/crc32.c @@ -0,0 +1,311 @@ +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Thanks to Rodney Brown for his contribution of faster + * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing + * tables for updating the shift register in one step with three exclusive-ors + * instead of four steps with four exclusive-ors. This results about a factor + * of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. + */ + +/* @(#) $Id$ */ + +#ifdef MAKECRCH +# include +# ifndef DYNAMIC_CRC_TABLE +# define DYNAMIC_CRC_TABLE +# endif /* !DYNAMIC_CRC_TABLE */ +#endif /* MAKECRCH */ + +#include "zutil.h" /* for STDC and FAR definitions */ + +#define local static + +/* Find a four-byte integer type for crc32_little() and crc32_big(). */ +#ifndef NOBYFOUR +# ifdef STDC /* need ANSI C limits.h to determine sizes */ +# include +# define BYFOUR +# if (UINT_MAX == 0xffffffffUL) + typedef unsigned int u4; +# else +# if (ULONG_MAX == 0xffffffffUL) + typedef unsigned long u4; +# else +# if (USHRT_MAX == 0xffffffffUL) + typedef unsigned short u4; +# else +# undef BYFOUR /* can't find a four-byte integer type! */ +# endif +# endif +# endif +# endif /* STDC */ +#endif /* !NOBYFOUR */ + +/* Definitions for doing the crc four data bytes at a time. */ +#ifdef BYFOUR +# define REV(w) (((w)>>24)+(((w)>>8)&0xff00)+ \ + (((w)&0xff00)<<8)+(((w)&0xff)<<24)) + local unsigned long crc32_little OF((unsigned long, + const unsigned char FAR *, unsigned)); + local unsigned long crc32_big OF((unsigned long, + const unsigned char FAR *, unsigned)); +# define TBLS 8 +#else +# define TBLS 1 +#endif /* BYFOUR */ + +#ifdef DYNAMIC_CRC_TABLE + +local int crc_table_empty = 1; +local unsigned long FAR crc_table[TBLS][256]; +local void make_crc_table OF((void)); +#ifdef MAKECRCH + local void write_table OF((FILE *, const unsigned long FAR *)); +#endif /* MAKECRCH */ + +/* + Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The first table is simply the CRC of all possible eight bit values. This is + all the information needed to generate CRCs on data a byte at a time for all + combinations of CRC register values and incoming bytes. The remaining tables + allow for word-at-a-time CRC calculation for both big-endian and little- + endian machines, where a word is four bytes. +*/ +local void make_crc_table() +{ + unsigned long c; + int n, k; + unsigned long poly; /* polynomial exclusive-or pattern */ + /* terms of polynomial defining this crc (except x^32): */ + static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* make exclusive-or pattern from polynomial (0xedb88320UL) */ + poly = 0UL; + for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++) + poly |= 1UL << (31 - p[n]); + + /* generate a crc for every 8-bit value */ + for (n = 0; n < 256; n++) { + c = (unsigned long)n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[0][n] = c; + } + +#ifdef BYFOUR + /* generate crc for each value followed by one, two, and three zeros, and + then the byte reversal of those as well as the first table */ + for (n = 0; n < 256; n++) { + c = crc_table[0][n]; + crc_table[4][n] = REV(c); + for (k = 1; k < 4; k++) { + c = crc_table[0][c & 0xff] ^ (c >> 8); + crc_table[k][n] = c; + crc_table[k + 4][n] = REV(c); + } + } +#endif /* BYFOUR */ + + crc_table_empty = 0; + +#ifdef MAKECRCH + /* write out CRC tables to crc32.h */ + { + FILE *out; + + out = fopen("crc32.h", "w"); + if (out == NULL) return; + fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); + fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); + fprintf(out, "local const unsigned long FAR "); + fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); + write_table(out, crc_table[0]); +# ifdef BYFOUR + fprintf(out, "#ifdef BYFOUR\n"); + for (k = 1; k < 8; k++) { + fprintf(out, " },\n {\n"); + write_table(out, crc_table[k]); + } + fprintf(out, "#endif\n"); +# endif /* BYFOUR */ + fprintf(out, " }\n};\n"); + fclose(out); + } +#endif /* MAKECRCH */ +} + +#ifdef MAKECRCH +local void write_table(out, table) + FILE *out; + const unsigned long FAR *table; +{ + int n; + + for (n = 0; n < 256; n++) + fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", table[n], + n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); +} +#endif /* MAKECRCH */ + +#else /* !DYNAMIC_CRC_TABLE */ +/* ======================================================================== + * Tables of CRC-32s of all single-byte values, made by make_crc_table(). + */ +#include "crc32.h" +#endif /* DYNAMIC_CRC_TABLE */ + +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const unsigned long FAR * ZEXPORT get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + return (const unsigned long FAR *)crc_table; +} + +/* ========================================================================= */ +#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) +#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 + +/* ========================================================================= */ +unsigned long ZEXPORT crc32(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + if (buf == Z_NULL) return 0UL; + +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + +#ifdef BYFOUR + if (sizeof(void *) == sizeof(ptrdiff_t)) { + u4 endian; + + endian = 1; + if (*((unsigned char *)(&endian))) + return crc32_little(crc, buf, len); + else + return crc32_big(crc, buf, len); + } +#endif /* BYFOUR */ + crc = crc ^ 0xffffffffUL; + while (len >= 8) { + DO8; + len -= 8; + } + if (len) do { + DO1; + } while (--len); + return crc ^ 0xffffffffUL; +} + +#ifdef BYFOUR + +/* ========================================================================= */ +#define DOLIT4 c ^= *buf4++; \ + c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ + crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] +#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 + +/* ========================================================================= */ +local unsigned long crc32_little(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = (u4)crc; + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + len--; + } + + buf4 = (const u4 FAR *)buf; + while (len >= 32) { + DOLIT32; + len -= 32; + } + while (len >= 4) { + DOLIT4; + len -= 4; + } + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + } while (--len); + c = ~c; + return (unsigned long)c; +} + +/* ========================================================================= */ +#define DOBIG4 c ^= *++buf4; \ + c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ + crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] +#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 + +/* ========================================================================= */ +local unsigned long crc32_big(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = REV((u4)crc); + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + len--; + } + + buf4 = (const u4 FAR *)buf; + buf4--; + while (len >= 32) { + DOBIG32; + len -= 32; + } + while (len >= 4) { + DOBIG4; + len -= 4; + } + buf4++; + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + } while (--len); + c = ~c; + return (unsigned long)(REV(c)); +} + +#endif /* BYFOUR */ diff --git a/zlib/crc32.h b/zlib/crc32.h new file mode 100644 index 0000000..5de49bc --- /dev/null +++ b/zlib/crc32.h @@ -0,0 +1,441 @@ +/* crc32.h -- tables for rapid CRC calculation + * Generated automatically by crc32.c + */ + +local const unsigned long FAR crc_table[TBLS][256] = +{ + { + 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, + 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, + 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, + 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, + 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, + 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, + 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, + 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, + 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, + 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, + 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, + 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, + 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, + 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, + 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, + 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, + 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, + 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, + 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, + 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, + 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, + 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, + 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, + 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, + 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, + 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, + 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, + 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, + 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, + 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, + 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, + 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, + 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, + 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, + 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, + 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, + 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, + 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, + 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, + 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, + 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, + 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, + 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, + 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, + 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, + 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, + 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, + 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, + 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, + 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, + 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, + 0x2d02ef8dUL +#ifdef BYFOUR + }, + { + 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, + 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, + 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, + 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, + 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, + 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, + 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, + 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, + 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, + 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, + 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, + 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, + 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, + 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, + 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, + 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, + 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, + 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, + 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, + 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, + 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, + 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, + 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, + 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, + 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, + 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, + 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, + 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, + 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, + 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, + 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, + 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, + 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, + 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, + 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, + 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, + 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, + 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, + 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, + 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, + 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, + 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, + 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, + 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, + 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, + 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, + 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, + 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, + 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, + 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, + 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, + 0x9324fd72UL + }, + { + 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, + 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, + 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, + 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, + 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, + 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, + 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, + 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, + 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, + 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, + 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, + 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, + 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, + 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, + 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, + 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, + 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, + 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, + 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, + 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, + 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, + 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, + 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, + 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, + 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, + 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, + 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, + 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, + 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, + 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, + 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, + 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, + 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, + 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, + 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, + 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, + 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, + 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, + 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, + 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, + 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, + 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, + 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, + 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, + 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, + 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, + 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, + 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, + 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, + 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, + 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, + 0xbe9834edUL + }, + { + 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, + 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, + 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, + 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, + 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, + 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, + 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, + 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, + 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, + 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, + 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, + 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, + 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, + 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, + 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, + 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, + 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, + 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, + 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, + 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, + 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, + 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, + 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, + 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, + 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, + 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, + 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, + 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, + 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, + 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, + 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, + 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, + 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, + 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, + 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, + 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, + 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, + 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, + 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, + 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, + 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, + 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, + 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, + 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, + 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, + 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, + 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, + 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, + 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, + 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, + 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, + 0xde0506f1UL + }, + { + 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, + 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, + 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, + 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, + 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, + 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, + 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, + 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, + 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, + 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, + 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, + 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, + 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, + 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, + 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, + 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, + 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, + 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, + 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, + 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, + 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, + 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, + 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, + 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, + 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, + 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, + 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, + 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, + 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, + 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, + 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, + 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, + 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, + 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, + 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, + 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, + 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, + 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, + 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, + 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, + 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, + 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, + 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, + 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, + 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, + 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, + 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, + 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, + 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, + 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, + 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, + 0x8def022dUL + }, + { + 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, + 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, + 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, + 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, + 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, + 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, + 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, + 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, + 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, + 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, + 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, + 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, + 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, + 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, + 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, + 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, + 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, + 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, + 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, + 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, + 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, + 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, + 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, + 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, + 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, + 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, + 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, + 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, + 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, + 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, + 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, + 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, + 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, + 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, + 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, + 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, + 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, + 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, + 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, + 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, + 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, + 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, + 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, + 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, + 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, + 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, + 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, + 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, + 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, + 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, + 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, + 0x72fd2493UL + }, + { + 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, + 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, + 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, + 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, + 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, + 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, + 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, + 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, + 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, + 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, + 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, + 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, + 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, + 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, + 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, + 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, + 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, + 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, + 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, + 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, + 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, + 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, + 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, + 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, + 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, + 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, + 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, + 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, + 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, + 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, + 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, + 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, + 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, + 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, + 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, + 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, + 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, + 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, + 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, + 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, + 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, + 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, + 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, + 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, + 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, + 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, + 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, + 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, + 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, + 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, + 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, + 0xed3498beUL + }, + { + 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, + 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, + 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, + 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, + 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, + 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, + 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, + 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, + 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, + 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, + 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, + 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, + 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, + 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, + 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, + 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, + 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, + 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, + 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, + 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, + 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, + 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, + 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, + 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, + 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, + 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, + 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, + 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, + 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, + 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, + 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, + 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, + 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, + 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, + 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, + 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, + 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, + 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, + 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, + 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, + 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, + 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, + 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, + 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, + 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, + 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, + 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, + 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, + 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, + 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, + 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, + 0xf10605deUL +#endif + } +}; diff --git a/zlib/deflate.c b/zlib/deflate.c new file mode 100644 index 0000000..022d6b8 --- /dev/null +++ b/zlib/deflate.c @@ -0,0 +1,1502 @@ +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many people for bug reports and testing. + * + * REFERENCES + * + * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + * Available in http://www.ietf.org/rfc/rfc1951.txt + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + +/* @(#) $Id$ */ + +#include "deflate.h" + +const char deflate_copyright[] = + " deflate 1.2.1 Copyright 1995-2003 Jean-loup Gailly "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* =========================================================================== + * Function prototypes. + */ +typedef enum { + need_more, /* block not completed, need more input or more output */ + block_done, /* block flush performed */ + finish_started, /* finish started, need only more output at next deflate */ + finish_done /* finish done, accept no more input or output */ +} block_state; + +typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +/* Compression function. Returns the block state after the call. */ + +local void fill_window OF((deflate_state *s)); +local block_state deflate_stored OF((deflate_state *s, int flush)); +local block_state deflate_fast OF((deflate_state *s, int flush)); +#ifndef FASTEST +local block_state deflate_slow OF((deflate_state *s, int flush)); +#endif +local void lm_init OF((deflate_state *s)); +local void putShortMSB OF((deflate_state *s, uInt b)); +local void flush_pending OF((z_streamp strm)); +local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +#ifndef FASTEST +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ + uInt longest_match OF((deflate_state *s, IPos cur_match)); +#else +local uInt longest_match OF((deflate_state *s, IPos cur_match)); +#endif +#endif +local uInt longest_match_fast OF((deflate_state *s, IPos cur_match)); + +#ifdef DEBUG +local void check_match OF((deflate_state *s, IPos start, IPos match, + int length)); +#endif + +/* =========================================================================== + * Local data + */ + +#define NIL 0 +/* Tail of hash chains */ + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +typedef struct config_s { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; + compress_func func; +} config; + +#ifdef FASTEST +local const config configuration_table[2] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ +#else +local const config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8, deflate_fast}, +/* 3 */ {4, 6, 32, 32, deflate_fast}, + +/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32, deflate_slow}, +/* 6 */ {8, 16, 128, 128, deflate_slow}, +/* 7 */ {8, 32, 128, 256, deflate_slow}, +/* 8 */ {32, 128, 258, 1024, deflate_slow}, +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ +#endif + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +#ifndef NO_DUMMY_DECL +struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ +#endif + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) + + +/* =========================================================================== + * Insert string str in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * If this file is compiled with -DFASTEST, the compression level is forced + * to 1, and no hash chains are maintained. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of str are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#ifdef FASTEST +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#else +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#endif + +/* =========================================================================== + * Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ +#define CLEAR_HASH(s) \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + +/* ========================================================================= */ +int ZEXPORT deflateInit_(strm, level, version, stream_size) + z_streamp strm; + int level; + const char *version; + int stream_size; +{ + return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, version, stream_size); + /* To do: ignore strm->next_in if we use it as window */ +} + +/* ========================================================================= */ +int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + version, stream_size) + z_streamp strm; + int level; + int method; + int windowBits; + int memLevel; + int strategy; + const char *version; + int stream_size; +{ + deflate_state *s; + int wrap = 1; + static const char my_version[] = ZLIB_VERSION; + + ushf *overlay; + /* We overlay pending_buf and d_buf+l_buf. This works since the average + * output size for (length,distance) codes is <= 24 bits. + */ + + if (version == Z_NULL || version[0] != my_version[0] || + stream_size != sizeof(z_stream)) { + return Z_VERSION_ERROR; + } + if (strm == Z_NULL) return Z_STREAM_ERROR; + + strm->msg = Z_NULL; + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + + if (windowBits < 0) { /* suppress zlib wrapper */ + wrap = 0; + windowBits = -windowBits; + } +#ifdef GZIP + else if (windowBits > 15) { + wrap = 2; /* write gzip wrapper instead */ + windowBits -= 16; + } +#endif + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_RLE) { + return Z_STREAM_ERROR; + } + if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ + s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); + if (s == Z_NULL) return Z_MEM_ERROR; + strm->state = (struct internal_state FAR *)s; + s->strm = strm; + + s->wrap = wrap; + s->w_bits = windowBits; + s->w_size = 1 << s->w_bits; + s->w_mask = s->w_size - 1; + + s->hash_bits = memLevel + 7; + s->hash_size = 1 << s->hash_bits; + s->hash_mask = s->hash_size - 1; + s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + + s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + s->pending_buf = (uchf *) overlay; + s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); + + if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || + s->pending_buf == Z_NULL) { + s->status = FINISH_STATE; + strm->msg = (char*)ERR_MSG(Z_MEM_ERROR); + deflateEnd (strm); + return Z_MEM_ERROR; + } + s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + + s->level = level; + s->strategy = strategy; + s->method = (Byte)method; + + return deflateReset(strm); +} + +/* ========================================================================= */ +int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) + z_streamp strm; + const Bytef *dictionary; + uInt dictLength; +{ + deflate_state *s; + uInt length = dictLength; + uInt n; + IPos hash_head = 0; + + if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL || + strm->state->wrap == 2 || + (strm->state->wrap == 1 && strm->state->status != INIT_STATE)) + return Z_STREAM_ERROR; + + s = strm->state; + if (s->wrap) + strm->adler = adler32(strm->adler, dictionary, dictLength); + + if (length < MIN_MATCH) return Z_OK; + if (length > MAX_DIST(s)) { + length = MAX_DIST(s); +#ifndef USE_DICT_HEAD + dictionary += dictLength - length; /* use the tail of the dictionary */ +#endif + } + zmemcpy(s->window, dictionary, length); + s->strstart = length; + s->block_start = (long)length; + + /* Insert all strings in the hash table (except for the last two bytes). + * s->lookahead stays null, so s->ins_h will be recomputed at the next + * call of fill_window. + */ + s->ins_h = s->window[0]; + UPDATE_HASH(s, s->ins_h, s->window[1]); + for (n = 0; n <= length - MIN_MATCH; n++) { + INSERT_STRING(s, n, hash_head); + } + if (hash_head) hash_head = 0; /* to make compiler happy */ + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateReset (strm) + z_streamp strm; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { + return Z_STREAM_ERROR; + } + + strm->total_in = strm->total_out = 0; + strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ + strm->data_type = Z_UNKNOWN; + + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; + + if (s->wrap < 0) { + s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ + } + s->status = s->wrap ? INIT_STATE : BUSY_STATE; + strm->adler = +#ifdef GZIP + s->wrap == 2 ? crc32(0L, Z_NULL, 0) : +#endif + adler32(0L, Z_NULL, 0); + s->last_flush = Z_NO_FLUSH; + + _tr_init(s); + lm_init(s); + + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePrime (strm, bits, value) + z_streamp strm; + int bits; + int value; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + strm->state->bi_valid = bits; + strm->state->bi_buf = (ush)(value & ((1 << bits) - 1)); + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateParams(strm, level, strategy) + z_streamp strm; + int level; + int strategy; +{ + deflate_state *s; + compress_func func; + int err = Z_OK; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + if (level < 0 || level > 9 || strategy < 0 || strategy > Z_RLE) { + return Z_STREAM_ERROR; + } + func = configuration_table[s->level].func; + + if (func != configuration_table[level].func && strm->total_in != 0) { + /* Flush the last buffer: */ + err = deflate(strm, Z_PARTIAL_FLUSH); + } + if (s->level != level) { + s->level = level; + s->max_lazy_match = configuration_table[level].max_lazy; + s->good_match = configuration_table[level].good_length; + s->nice_match = configuration_table[level].nice_length; + s->max_chain_length = configuration_table[level].max_chain; + } + s->strategy = strategy; + return err; +} + +/* ========================================================================= + * For the default windowBits of 15 and memLevel of 8, this function returns + * a close to exact, as well as small, upper bound on the compressed size. + * They are coded as constants here for a reason--if the #define's are + * changed, then this function needs to be changed as well. The return + * value for 15 and 8 only works for those exact settings. + * + * For any setting other than those defaults for windowBits and memLevel, + * the value returned is a conservative worst case for the maximum expansion + * resulting from using fixed blocks instead of stored blocks, which deflate + * can emit on compressed data for some combinations of the parameters. + * + * This function could be more sophisticated to provide closer upper bounds + * for every combination of windowBits and memLevel, as well as wrap. + * But even the conservative upper bound of about 14% expansion does not + * seem onerous for output buffer allocation. + */ +uLong ZEXPORT deflateBound(strm, sourceLen) + z_streamp strm; + uLong sourceLen; +{ + deflate_state *s; + uLong destLen; + + /* conservative upper bound */ + destLen = sourceLen + + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 11; + + /* if can't get parameters, return conservative bound */ + if (strm == Z_NULL || strm->state == Z_NULL) + return destLen; + + /* if not default parameters, return conservative bound */ + s = strm->state; + if (s->w_bits != 15 || s->hash_bits != 8 + 7) + return destLen; + + /* default settings: return tight bound for that case */ + return compressBound(sourceLen); +} + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +local void putShortMSB (s, b) + deflate_state *s; + uInt b; +{ + put_byte(s, (Byte)(b >> 8)); + put_byte(s, (Byte)(b & 0xff)); +} + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->next_out buffer and copying into it. + * (See also read_buf()). + */ +local void flush_pending(strm) + z_streamp strm; +{ + unsigned len = strm->state->pending; + + if (len > strm->avail_out) len = strm->avail_out; + if (len == 0) return; + + zmemcpy(strm->next_out, strm->state->pending_out, len); + strm->next_out += len; + strm->state->pending_out += len; + strm->total_out += len; + strm->avail_out -= len; + strm->state->pending -= len; + if (strm->state->pending == 0) { + strm->state->pending_out = strm->state->pending_buf; + } +} + +/* ========================================================================= */ +int ZEXPORT deflate (strm, flush) + z_streamp strm; + int flush; +{ + int old_flush; /* value of flush param for previous deflate call */ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + flush > Z_FINISH || flush < 0) { + return Z_STREAM_ERROR; + } + s = strm->state; + + if (strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0) || + (s->status == FINISH_STATE && flush != Z_FINISH)) { + ERR_RETURN(strm, Z_STREAM_ERROR); + } + if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + + s->strm = strm; /* just in case */ + old_flush = s->last_flush; + s->last_flush = flush; + + /* Write the header */ + if (s->status == INIT_STATE) { +#ifdef GZIP + if (s->wrap == 2) { + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, 255); + s->status = BUSY_STATE; + strm->adler = crc32(0L, Z_NULL, 0); + } + else +#endif + { + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags; + + if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) + level_flags = 0; + else if (s->level < 6) + level_flags = 1; + else if (s->level == 6) + level_flags = 2; + else + level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + s->status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = adler32(0L, Z_NULL, 0); + } + } + + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if (strm->avail_in == 0 && flush <= old_flush && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm->avail_in != 0 || s->lookahead != 0 || + (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { + block_state bstate; + + bstate = (*(configuration_table[s->level].func))(s, flush); + + if (bstate == finish_started || bstate == finish_done) { + s->status = FINISH_STATE; + } + if (bstate == need_more || bstate == finish_started) { + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate == block_done) { + if (flush == Z_PARTIAL_FLUSH) { + _tr_align(s); + } else { /* FULL_FLUSH or SYNC_FLUSH */ + _tr_stored_block(s, (char*)0, 0L, 0); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush == Z_FULL_FLUSH) { + CLEAR_HASH(s); /* forget history */ + } + } + flush_pending(strm); + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + Assert(strm->avail_out > 0, "bug2"); + + if (flush != Z_FINISH) return Z_OK; + if (s->wrap <= 0) return Z_STREAM_END; + + /* Write the trailer */ +#ifdef GZIP + if (s->wrap == 2) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); + put_byte(s, (Byte)(strm->total_in & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); + } + else +#endif + { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ + return s->pending != 0 ? Z_OK : Z_STREAM_END; +} + +/* ========================================================================= */ +int ZEXPORT deflateEnd (strm) + z_streamp strm; +{ + int status; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + + status = strm->state->status; + if (status != INIT_STATE && status != BUSY_STATE && + status != FINISH_STATE) { + return Z_STREAM_ERROR; + } + + /* Deallocate in reverse order of allocations: */ + TRY_FREE(strm, strm->state->pending_buf); + TRY_FREE(strm, strm->state->head); + TRY_FREE(strm, strm->state->prev); + TRY_FREE(strm, strm->state->window); + + ZFREE(strm, strm->state); + strm->state = Z_NULL; + + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; +} + +/* ========================================================================= + * Copy the source state to the destination state. + * To simplify the source, this is not supported for 16-bit MSDOS (which + * doesn't have enough memory anyway to duplicate compression states). + */ +int ZEXPORT deflateCopy (dest, source) + z_streamp dest; + z_streamp source; +{ +#ifdef MAXSEG_64K + return Z_STREAM_ERROR; +#else + deflate_state *ds; + deflate_state *ss; + ushf *overlay; + + + if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { + return Z_STREAM_ERROR; + } + + ss = source->state; + + *dest = *source; + + ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); + if (ds == Z_NULL) return Z_MEM_ERROR; + dest->state = (struct internal_state FAR *) ds; + *ds = *ss; + ds->strm = dest; + + ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); + ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); + ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); + overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); + ds->pending_buf = (uchf *) overlay; + + if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || + ds->pending_buf == Z_NULL) { + deflateEnd (dest); + return Z_MEM_ERROR; + } + /* following zmemcpy do not work for 16-bit MSDOS */ + zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); + zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + + ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); + ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); + ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; + + ds->l_desc.dyn_tree = ds->dyn_ltree; + ds->d_desc.dyn_tree = ds->dyn_dtree; + ds->bl_desc.dyn_tree = ds->bl_tree; + + return Z_OK; +#endif /* MAXSEG_64K */ +} + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local int read_buf(strm, buf, size) + z_streamp strm; + Bytef *buf; + unsigned size; +{ + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + if (strm->state->wrap == 1) { + strm->adler = adler32(strm->adler, strm->next_in, len); + } +#ifdef GZIP + else if (strm->state->wrap == 2) { + strm->adler = crc32(strm->adler, strm->next_in, len); + } +#endif + zmemcpy(buf, strm->next_in, len); + strm->next_in += len; + strm->total_in += len; + + return (int)len; +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init (s) + deflate_state *s; +{ + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif +} + +#ifndef FASTEST +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +#ifndef ASMV +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + int best_len = s->prev_length; /* best match length so far */ + int nice_match = s->nice_match; /* stop if match long enough */ + IPos limit = s->strstart > (IPos)MAX_DIST(s) ? + s->strstart - (IPos)MAX_DIST(s) : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + Posf *prev = s->prev; + uInt wmask = s->w_mask; + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; + register ush scan_start = *(ushf*)scan; + register ush scan_end = *(ushf*)(scan+best_len-1); +#else + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end = scan[best_len]; +#endif + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s->prev_length >= s->good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2: + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ushf*)(match+best_len-1) != scan_end || + *(ushf*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + Assert(scan[2] == match[2], "scan[2]?"); + scan++, match++; + do { + } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + s->match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ushf*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & wmask]) > limit + && --chain_length != 0); + + if ((uInt)best_len <= s->lookahead) return (uInt)best_len; + return s->lookahead; +} +#endif /* ASMV */ +#endif /* FASTEST */ + +/* --------------------------------------------------------------------------- + * Optimized version for level == 1 or strategy == Z_RLE only + */ +local uInt longest_match_fast(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + Assert(cur_match < s->strstart, "no future"); + + match = s->window + cur_match; + + /* Return failure if the match length is less than 2: + */ + if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match += 2; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + + if (len < MIN_MATCH) return MIN_MATCH - 1; + + s->match_start = cur_match; + return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; +} + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(s, start, match, length) + deflate_state *s; + IPos start, match; + int length; +{ + /* check that the match is indeed a match */ + if (zmemcmp(s->window + match, + s->window + start, length) != EQUAL) { + fprintf(stderr, " start %u, match %u, length %d\n", + start, match, length); + do { + fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); + } while (--length != 0); + z_error("invalid match"); + } + if (z_verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(s->window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(s, start, match, length) +#endif /* DEBUG */ + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(s) + deflate_state *s; +{ + register unsigned n, m; + register Posf *p; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (sizeof(int) <= 2) { + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if + * strstart == 0 && lookahead == 1 (input done a byte at time) + */ + more--; + } + } + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s->strstart >= wsize+MAX_DIST(s)) { + + zmemcpy(s->window, s->window+wsize, (unsigned)wsize); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + } while (--n); + + n = wsize; +#ifndef FASTEST + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); +#endif + more += wsize; + } + if (s->strm->avail_in == 0) return; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead >= MIN_MATCH) { + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK_ONLY(s, eof) { \ + _tr_flush_block(s, (s->block_start >= 0L ? \ + (charf *)&s->window[(unsigned)s->block_start] : \ + (charf *)Z_NULL), \ + (ulg)((long)s->strstart - s->block_start), \ + (eof)); \ + s->block_start = s->strstart; \ + flush_pending(s->strm); \ + Tracev((stderr,"[FLUSH]")); \ +} + +/* Same but force premature exit if necessary. */ +#define FLUSH_BLOCK(s, eof) { \ + FLUSH_BLOCK_ONLY(s, eof); \ + if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \ +} + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +local block_state deflate_stored(s, flush) + deflate_state *s; + int flush; +{ + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + ulg max_block_size = 0xffff; + ulg max_start; + + if (max_block_size > s->pending_buf_size - 5) { + max_block_size = s->pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s->lookahead <= 1) { + + Assert(s->strstart < s->w_size+MAX_DIST(s) || + s->block_start >= (long)s->w_size, "slide too late"); + + fill_window(s); + if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; + + if (s->lookahead == 0) break; /* flush the current block */ + } + Assert(s->block_start >= 0L, "block gone"); + + s->strstart += s->lookahead; + s->lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + max_start = s->block_start + max_block_size; + if (s->strstart == 0 || (ulg)s->strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s->lookahead = (uInt)(s->strstart - max_start); + s->strstart = (uInt)max_start; + FLUSH_BLOCK(s, 0); + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { + FLUSH_BLOCK(s, 0); + } + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local block_state deflate_fast(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ +#ifdef FASTEST + if ((s->strategy < Z_HUFFMAN_ONLY) || + (s->strategy == Z_RLE && s->strstart - hash_head == 1)) { + s->match_length = longest_match_fast (s, hash_head); + } +#else + if (s->strategy < Z_HUFFMAN_ONLY) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } +#endif + /* longest_match() or longest_match_fast() sets match_start */ + } + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->match_start, s->match_length); + + _tr_tally_dist(s, s->strstart - s->match_start, + s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ +#ifndef FASTEST + if (s->match_length <= s->max_insert_length && + s->lookahead >= MIN_MATCH) { + s->match_length--; /* string at strstart already in table */ + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s->match_length != 0); + s->strstart++; + } else +#endif + { + s->strstart += s->match_length; + s->match_length = 0; + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +#ifndef FASTEST +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +local block_state deflate_slow(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + */ + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + + if (hash_head != NIL && s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + if (s->strategy < Z_HUFFMAN_ONLY) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } + /* longest_match() or longest_match_fast() sets match_start */ + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED +#if TOO_FAR <= 32767 + || (s->match_length == MIN_MATCH && + s->strstart - s->match_start > TOO_FAR) +#endif + )) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s->match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + + _tr_tally_dist(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH, bflush); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s->lookahead -= s->prev_length-1; + s->prev_length -= 2; + do { + if (++s->strstart <= max_insert) { + INSERT_STRING(s, s->strstart, hash_head); + } + } while (--s->prev_length != 0); + s->match_available = 0; + s->match_length = MIN_MATCH-1; + s->strstart++; + + if (bflush) FLUSH_BLOCK(s, 0); + + } else if (s->match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + if (bflush) { + FLUSH_BLOCK_ONLY(s, 0); + } + s->strstart++; + s->lookahead--; + if (s->strm->avail_out == 0) return need_more; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s->match_available = 1; + s->strstart++; + s->lookahead--; + } + } + Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s->match_available) { + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + s->match_available = 0; + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif /* FASTEST */ diff --git a/zlib/deflate.h b/zlib/deflate.h new file mode 100644 index 0000000..ee00773 --- /dev/null +++ b/zlib/deflate.h @@ -0,0 +1,326 @@ +/* deflate.h -- internal compression state + * Copyright (C) 1995-2002 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef DEFLATE_H +#define DEFLATE_H + +#include "zutil.h" + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer creation by deflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip encoding + should be left enabled. */ +#ifndef NO_GZIP +# define GZIP +#endif + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define INIT_STATE 42 +#define BUSY_STATE 113 +#define FINISH_STATE 666 +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct internal_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Bytef *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Bytef *pending_out; /* next pending byte to output to the stream */ + int pending; /* nb of bytes in the pending buffer */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + Byte data_type; /* UNKNOWN, BINARY or ASCII */ + Byte method; /* STORED (for zip only) or DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Bytef *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to supress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + uInt matches; /* number of string matches in current block */ + int last_eob_len; /* bit length of EOB code for last block */ + +#ifdef DEBUG + ulg compressed_len; /* total bit length of compressed file mod 2^32 */ + ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + + /* in trees.c */ +void _tr_init OF((deflate_state *s)); +int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +void _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); +void _tr_align OF((deflate_state *s)); +void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); + +#define d_code(dist) \ + ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. _dist_code[256] and _dist_code[257] are never + * used. + */ + +#ifndef DEBUG +/* Inline versions of _tr_tally for speed: */ + +#if defined(GEN_TREES_H) || !defined(STDC) + extern uch _length_code[]; + extern uch _dist_code[]; +#else + extern const uch _length_code[]; + extern const uch _dist_code[]; +#endif + +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->last_lit] = 0; \ + s->l_buf[s->last_lit++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (length); \ + ush dist = (distance); \ + s->d_buf[s->last_lit] = dist; \ + s->l_buf[s->last_lit++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +#else +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) +# define _tr_tally_dist(s, distance, length, flush) \ + flush = _tr_tally(s, distance, length) +#endif + +#endif /* DEFLATE_H */ diff --git a/zlib/trees.c b/zlib/trees.c new file mode 100644 index 0000000..1d36eaa --- /dev/null +++ b/zlib/trees.c @@ -0,0 +1,1215 @@ +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1995-2003 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * REFERENCES + * + * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + +/* @(#) $Id$ */ + +/* #define GEN_TREES_H */ + +#include "deflate.h" + +#ifdef DEBUG +# include +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local const int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local const uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +#define Buf_size (8 * 2*sizeof(char)) +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ + +#if defined(GEN_TREES_H) || !defined(STDC) +/* non ANSI compilers may not accept trees.h */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +uch _dist_code[DIST_CODE_LEN]; +/* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +uch _length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#else +# include "trees.h" +#endif /* GEN_TREES_H */ + +struct static_tree_desc_s { + const ct_data *static_tree; /* static tree or NULL */ + const intf *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ +}; + +local static_tree_desc static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local static_tree_desc static_d_desc = +{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; + +local static_tree_desc static_bl_desc = +{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void tr_static_init OF((void)); +local void init_block OF((deflate_state *s)); +local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); +local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); +local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); +local void build_tree OF((deflate_state *s, tree_desc *desc)); +local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local int build_bl_tree OF((deflate_state *s)); +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, + int blcodes)); +local void compress_block OF((deflate_state *s, ct_data *ltree, + ct_data *dtree)); +local void set_data_type OF((deflate_state *s)); +local unsigned bi_reverse OF((unsigned value, int length)); +local void bi_windup OF((deflate_state *s)); +local void bi_flush OF((deflate_state *s)); +local void copy_block OF((deflate_state *s, charf *buf, unsigned len, + int header)); + +#ifdef GEN_TREES_H +local void gen_trees_header OF((void)); +#endif + +#ifndef DEBUG +# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +# define send_code(s, c, tree) \ + { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(s, tree[c].Code, tree[c].Len); } +#endif + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +#ifdef DEBUG +local void send_bits OF((deflate_state *s, int value, int length)); + +local void send_bits(s, value, length) + deflate_state *s; + int value; /* value to send */ + int length; /* number of bits */ +{ + Tracevv((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + s->bits_sent += (ulg)length; + + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (s->bi_valid > (int)Buf_size - length) { + s->bi_buf |= (value << s->bi_valid); + put_short(s, s->bi_buf); + s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); + s->bi_valid += length - Buf_size; + } else { + s->bi_buf |= value << s->bi_valid; + s->bi_valid += length; + } +} +#else /* !DEBUG */ + +#define send_bits(s, value, length) \ +{ int len = length;\ + if (s->bi_valid > (int)Buf_size - len) {\ + int val = value;\ + s->bi_buf |= (val << s->bi_valid);\ + put_short(s, s->bi_buf);\ + s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ + s->bi_valid += len - Buf_size;\ + } else {\ + s->bi_buf |= (value) << s->bi_valid;\ + s->bi_valid += len;\ + }\ +} +#endif /* DEBUG */ + + +/* the arguments must not have side effects */ + +/* =========================================================================== + * Initialize the various 'constant' tables. + */ +local void tr_static_init() +{ +#if defined(GEN_TREES_H) || !defined(STDC) + static int static_init_done = 0; + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + if (static_init_done) return; + + /* For some embedded targets, global variables are not initialized: */ + static_l_desc.static_tree = static_ltree; + static_l_desc.extra_bits = extra_lbits; + static_d_desc.static_tree = static_dtree; + static_d_desc.extra_bits = extra_dbits; + static_bl_desc.extra_bits = extra_blbits; + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + _dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse((unsigned)n, 5); + } + static_init_done = 1; + +# ifdef GEN_TREES_H + gen_trees_header(); +# endif +#endif /* defined(GEN_TREES_H) || !defined(STDC) */ +} + +/* =========================================================================== + * Genererate the file trees.h describing the static trees. + */ +#ifdef GEN_TREES_H +# ifndef DEBUG +# include +# endif + +# define SEPARATOR(i, last, width) \ + ((i) == (last)? "\n};\n\n" : \ + ((i) % (width) == (width)-1 ? ",\n" : ", ")) + +void gen_trees_header() +{ + FILE *header = fopen("trees.h", "w"); + int i; + + Assert (header != NULL, "Can't open trees.h"); + fprintf(header, + "/* header created automatically with -DGEN_TREES_H */\n\n"); + + fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); + for (i = 0; i < L_CODES+2; i++) { + fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, + static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); + } + + fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, + static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); + } + + fprintf(header, "const uch _dist_code[DIST_CODE_LEN] = {\n"); + for (i = 0; i < DIST_CODE_LEN; i++) { + fprintf(header, "%2u%s", _dist_code[i], + SEPARATOR(i, DIST_CODE_LEN-1, 20)); + } + + fprintf(header, "const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); + for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { + fprintf(header, "%2u%s", _length_code[i], + SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); + } + + fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); + for (i = 0; i < LENGTH_CODES; i++) { + fprintf(header, "%1u%s", base_length[i], + SEPARATOR(i, LENGTH_CODES-1, 20)); + } + + fprintf(header, "local const int base_dist[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "%5u%s", base_dist[i], + SEPARATOR(i, D_CODES-1, 10)); + } + + fclose(header); +} +#endif /* GEN_TREES_H */ + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +void _tr_init(s) + deflate_state *s; +{ + tr_static_init(); + + s->l_desc.dyn_tree = s->dyn_ltree; + s->l_desc.stat_desc = &static_l_desc; + + s->d_desc.dyn_tree = s->dyn_dtree; + s->d_desc.stat_desc = &static_d_desc; + + s->bl_desc.dyn_tree = s->bl_tree; + s->bl_desc.stat_desc = &static_bl_desc; + + s->bi_buf = 0; + s->bi_valid = 0; + s->last_eob_len = 8; /* enough lookahead for inflate */ +#ifdef DEBUG + s->compressed_len = 0L; + s->bits_sent = 0L; +#endif + + /* Initialize the first block of the first file: */ + init_block(s); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(s) + deflate_state *s; +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->last_lit = s->matches = 0; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(s, tree, top) \ +{\ + top = s->heap[SMALLEST]; \ + s->heap[SMALLEST] = s->heap[s->heap_len--]; \ + pqdownheap(s, tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m, depth) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(s, tree, k) + deflate_state *s; + ct_data *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = s->heap[k]; + int j = k << 1; /* left son of k */ + while (j <= s->heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s->heap_len && + smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s->heap[j], s->depth)) break; + + /* Exchange v with the smallest son */ + s->heap[k] = s->heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s->heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + const ct_data *stree = desc->stat_desc->static_tree; + const intf *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + + for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + n = s->heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s->bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + s->opt_len += (ulg)f * (bits + xbits); + if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s->bl_count[bits] == 0) bits--; + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = s->bl_count[bits]; + while (n != 0) { + m = s->heap[--h]; + if (m > max_code) continue; + if (tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((long)bits - (long)tree[m].Len) + *(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (tree, max_code, bl_count) + ct_data *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ + ushf *bl_count; /* number of codes at each bit length */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; + const ct_data *stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s->heap_len = 0, s->heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + s->heap[++(s->heap_len)] = max_code = n; + s->depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s->heap_len < 2) { + node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); + tree[node].Freq = 1; + s->depth[node] = 0; + s->opt_len--; if (stree) s->static_len -= stree[node].Len; + /* node is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + pqremove(s, tree, n); /* n = node of least frequency */ + m = s->heap[SMALLEST]; /* m = node of next least frequency */ + + s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ + s->heap[--(s->heap_max)] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? + s->depth[n] : s->depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == s->bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + s->heap[SMALLEST] = node++; + pqdownheap(s, tree, SMALLEST); + + } while (s->heap_len >= 2); + + s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, (tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +local void scan_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + s->bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) s->bl_tree[curlen].Freq++; + s->bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + s->bl_tree[REPZ_3_10].Freq++; + } else { + s->bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s->bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree(s) + deflate_state *s; +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); + scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, (tree_desc *)(&(s->bl_desc))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + s->opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + s->opt_len, s->static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(s, lcodes, dcodes, blcodes) + deflate_state *s; + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +} + +/* =========================================================================== + * Send a stored block + */ +void _tr_stored_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */ +#ifdef DEBUG + s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; + s->compressed_len += (stored_len + 4) << 3; +#endif + copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ +} + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + * The current inflate code requires 9 bits of lookahead. If the + * last two codes for the previous block (real code plus EOB) were coded + * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + * the last real code. In this case we send two empty static blocks instead + * of one. (There are no problems if the previous block is stored or fixed.) + * To simplify the code, we assume the worst case of last real code encoded + * on one bit only. + */ +void _tr_align(s) + deflate_state *s; +{ + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ +#endif + bi_flush(s); + /* Of the 10 bits for the empty block, we have already sent + * (10 - bi_valid) bits. The lookahead for the last real code (before + * the EOB of the previous block) was thus at least one plus the length + * of the EOB plus what we have just sent of the empty static block. + */ + if (1 + s->last_eob_len + 10 - s->bi_valid < 9) { + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; +#endif + bi_flush(s); + } + s->last_eob_len = 7; +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ +void _tr_flush_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s->level > 0) { + + /* Check if the file is ascii or binary */ + if (s->data_type == Z_UNKNOWN) set_data_type(s); + + /* Construct the literal and distance trees */ + build_tree(s, (tree_desc *)(&(s->l_desc))); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + + build_tree(s, (tree_desc *)(&(s->d_desc))); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute the block lengths in bytes. */ + opt_lenb = (s->opt_len+3+7)>>3; + static_lenb = (s->static_len+3+7)>>3; + + Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + s->last_lit)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + } else { + Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + +#ifdef FORCE_STORED + if (buf != (char*)0) { /* force stored block */ +#else + if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, eof); + +#ifdef FORCE_STATIC + } else if (static_lenb >= 0) { /* force static trees */ +#else + } else if (static_lenb == opt_lenb) { +#endif + send_bits(s, (STATIC_TREES<<1)+eof, 3); + compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->static_len; +#endif + } else { + send_bits(s, (DYN_TREES<<1)+eof, 3); + send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, + max_blindex+1); + compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->opt_len; +#endif + } + Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (eof) { + bi_windup(s); +#ifdef DEBUG + s->compressed_len += 7; /* align on byte boundary */ +#endif + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + s->compressed_len-7*eof)); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int _tr_tally (s, dist, lc) + deflate_state *s; + unsigned dist; /* distance of matched string */ + unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + s->d_buf[s->last_lit] = (ush)dist; + s->l_buf[s->last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + s->dyn_ltree[lc].Freq++; + } else { + s->matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST(s) && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; + s->dyn_dtree[d_code(dist)].Freq++; + } + +#ifdef TRUNCATE_BLOCK + /* Try to guess if it is profitable to stop the current block here */ + if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)s->last_lit*8L; + ulg in_length = (ulg)((long)s->strstart - s->block_start); + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)s->dyn_dtree[dcode].Freq * + (5L+extra_dbits[dcode]); + } + out_length >>= 3; + Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + s->last_lit, in_length, out_length, + 100L - out_length*100L/in_length)); + if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; + } +#endif + return (s->last_lit == s->lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(s, ltree, dtree) + deflate_state *s; + ct_data *ltree; /* literal tree */ + ct_data *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->last_lit != 0) do { + dist = s->d_buf[lx]; + lc = s->l_buf[lx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, + "pendingBuf overflow"); + + } while (lx < s->last_lit); + + send_code(s, END_BLOCK, ltree); + s->last_eob_len = ltree[END_BLOCK].Len; +} + +/* =========================================================================== + * Set the data type to ASCII or BINARY, using a crude approximation: + * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + * IN assertion: the fields freq of dyn_ltree are set and the total of all + * frequencies does not exceed 64K (to fit in an int on 16 bit machines). + */ +local void set_data_type(s) + deflate_state *s; +{ + int n = 0; + unsigned ascii_freq = 0; + unsigned bin_freq = 0; + while (n < 7) bin_freq += s->dyn_ltree[n++].Freq; + while (n < 128) ascii_freq += s->dyn_ltree[n++].Freq; + while (n < LITERALS) bin_freq += s->dyn_ltree[n++].Freq; + s->data_type = (Byte)(bin_freq > (ascii_freq >> 2) ? Z_BINARY : Z_ASCII); +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(s) + deflate_state *s; +{ + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(s) + deflate_state *s; +{ + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +local void copy_block(s, buf, len, header) + deflate_state *s; + charf *buf; /* the input data */ + unsigned len; /* its length */ + int header; /* true if block header must be written */ +{ + bi_windup(s); /* align on byte boundary */ + s->last_eob_len = 8; /* enough lookahead for inflate */ + + if (header) { + put_short(s, (ush)len); + put_short(s, (ush)~len); +#ifdef DEBUG + s->bits_sent += 2*16; +#endif + } +#ifdef DEBUG + s->bits_sent += (ulg)len<<3; +#endif + while (len--) { + put_byte(s, *buf++); + } +} diff --git a/zlib/trees.h b/zlib/trees.h new file mode 100644 index 0000000..1ca868b --- /dev/null +++ b/zlib/trees.h @@ -0,0 +1,128 @@ +/* header created automatically with -DGEN_TREES_H */ + +local const ct_data static_ltree[L_CODES+2] = { +{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, +{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, +{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, +{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, +{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, +{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, +{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, +{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, +{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, +{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, +{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, +{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, +{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, +{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, +{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, +{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, +{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, +{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, +{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, +{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, +{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, +{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, +{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, +{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, +{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, +{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, +{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, +{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, +{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, +{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, +{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, +{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, +{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, +{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, +{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, +{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, +{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, +{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, +{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, +{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, +{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, +{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, +{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, +{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, +{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, +{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, +{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, +{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, +{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, +{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, +{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, +{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, +{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, +{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, +{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, +{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, +{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, +{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} +}; + +local const ct_data static_dtree[D_CODES] = { +{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, +{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, +{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, +{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, +{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, +{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} +}; + +const uch _dist_code[DIST_CODE_LEN] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, +10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 +}; + +const uch _length_code[MAX_MATCH-MIN_MATCH+1]= { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +}; + +local const int base_length[LENGTH_CODES] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, +64, 80, 96, 112, 128, 160, 192, 224, 0 +}; + +local const int base_dist[D_CODES] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 +}; + diff --git a/zlib/zconf.h b/zlib/zconf.h new file mode 100644 index 0000000..759d757 --- /dev/null +++ b/zlib/zconf.h @@ -0,0 +1,323 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +# define deflateInit_ z_deflateInit_ +# define deflate z_deflate +# define deflateEnd z_deflateEnd +# define inflateInit_ z_inflateInit_ +# define inflate z_inflate +# define inflateEnd z_inflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateSetDictionary z_deflateSetDictionary +# define deflateCopy z_deflateCopy +# define deflateReset z_deflateReset +# define deflatePrime z_deflatePrime +# define deflateParams z_deflateParams +# define deflateBound z_deflateBound +# define inflateInit2_ z_inflateInit2_ +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateCopy z_inflateCopy +# define inflateReset z_inflateReset +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# define uncompress z_uncompress +# define adler32 z_adler32 +# define crc32 z_crc32 +# define get_crc_table z_get_crc_table + +# define Byte z_Byte +# define uInt z_uInt +# define uLong z_uLong +# define Bytef z_Bytef +# define charf z_charf +# define intf z_intf +# define uIntf z_uIntf +# define uLongf z_uLongf +# define voidpf z_voidpf +# define voidp z_voidp +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) +# define WIN32 +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */ +# include /* for off_t */ +# include /* for SEEK_* and off_t */ +# ifdef VMS +# include /* for off_t */ +# endif +# define z_off_t off_t +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif +#ifndef z_off_t +# define z_off_t long +#endif + +#if defined(__OS400__) +#define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +# ifdef FAR +# undef FAR +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) +# pragma map(deflateInit_,"DEIN") +# pragma map(deflateInit2_,"DEIN2") +# pragma map(deflateEnd,"DEEND") +# pragma map(deflateBound,"DEBND") +# pragma map(inflateInit_,"ININ") +# pragma map(inflateInit2_,"ININ2") +# pragma map(inflateEnd,"INEND") +# pragma map(inflateSync,"INSY") +# pragma map(inflateSetDictionary,"INSEDI") +# pragma map(compressBound,"CMBND") +# pragma map(inflate_table,"INTABL") +# pragma map(inflate_fast,"INFA") +# pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/zlib/zlib.h b/zlib/zlib.h new file mode 100644 index 0000000..d54ac94 --- /dev/null +++ b/zlib/zlib.h @@ -0,0 +1,1200 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.1, November 17th, 2003 + + Copyright (C) 1995-2003 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.1" +#define ZLIB_VERNUM 0x1210 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms will be added later and will have the same + stream interface. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by the in-memory functions is the zlib + format, which is a zlib wrapper documented in RFC 1950, wrapped around a + deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + This library does not provide any functions to write gzip files in memory. + However such functions could be easily written using zlib's deflate function, + the documentation in the gzip RFC, and the examples in gzio.c. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never + crash even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: ascii or binary */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative + * values are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_ASCII 1 +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + This check is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. + If zalloc and zfree are set to Z_NULL, deflateInit updates them to + use default allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at + all (the input data is simply copied a block at a time). + Z_DEFAULT_COMPRESSION requests a default compromise between speed and + compression (currently equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if level is not a valid compression level, + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). + msg is set to null if there is no error message. deflateInit does not + perform any compression: this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce some + output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). + Some output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating avail_in or avail_out accordingly; avail_out + should never be zero before the call. The application can consume the + compressed output when it wants, for example when the output buffer is full + (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK + and with zero avail_out, it must be called again after making room in the + output buffer because there might be more output pending. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In particular + avail_in is zero after the call if enough output space has been provided + before the call.) Flushing may degrade compression for some compression + algorithms and so it should be used only when necessary. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + the compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there + was enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the + stream are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least + the value returned by deflateBound (see below). If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update data_type if it can make a good guess about + the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered + binary. This field is only for information purposes and does not affect + the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, + msg may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the exact + value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller. msg is set to null if there is no error + message. inflateInit does not perform any decompression apart from reading + the zlib header if present: this will be done by inflate(). (So next_in and + avail_in may be modified, but next_out and avail_out are unchanged.) +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there + is no more input data or no more space in the output buffer (see below + about the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). If inflate returns Z_OK and with zero avail_out, it + must be called again after making room in the output buffer because there + might be more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, + Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() stop + if and when it get to the next deflate block boundary. When decoding the zlib + or gzip format, this will cause inflate() to return immediately after the + header and before the first block. When doing a raw inflate, inflate() will + go ahead and process the first block, and will return when it gets to the end + of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 + if inflate() is currently decoding the last block in the deflate stream, + plus 128 if inflate() returned immediately after decoding an end-of-block + code or decoding the complete header up to just before the first byte of the + deflate stream. The end-of-block will not be indicated until all of the + uncompressed data from that block has been written to strm->next_out. The + number of unused bits may in general be greater than seven, except when + bit 7 of data_type is set, in which case the number of unused bits will be + less than eight. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster approach + may be used for the single inflate() call. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the only effect of the flush parameter in this implementation + is on the return value of inflate(), as noted below, or when it returns early + because Z_BLOCK is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm-adler to the adler32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the adler32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() will decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically. Any information + contained in the gzip header is not retained, so applications that need that + information should instead use raw inflate, see inflateInit2() below, or + inflateBack() and perform their own processing of the gzip header and + trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may then + call inflateSync() to look for a good compression block if a partial recovery + of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by + the caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), + no header crc, and the operating system will be set to 255 (unknown). + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but + is slow and reduces compression ratio; memLevel=9 uses maximum memory + for optimal speed. The default value is 8. See zconf.h for total memory + usage as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as + Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy + parameter only affects the compression ratio but not the correctness of the + compressed output even if it is not set appropriately. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid + method). msg is set to null if there is no error message. deflateInit2 does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any + call of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size in + deflate or deflate2. Thus the strings most likely to be useful should be + put at the end of the dictionary, not at the front. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and + can consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. + The stream will keep the same compression level and any other attributes + that may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different + strategy. If the compression level is changed, the input available so far + is compressed with the old level (and may be flushed); the new level will + take effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to + be compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR + if strm->avail_out was zero. +*/ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() + or deflateInit2(). This would be used to allocate an output buffer + for deflation in a single pass, and so would be called before deflate(). +*/ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the + bits leftover from a previous deflate stream when appending to it. As such, + this function can only be used for raw deflate, and must be used before the + first deflate() call after a deflateInit2() or deflateReset(). bits must be + less than or equal to 16, and that many of the least significant bits of + value will be inserted in the output. + + deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as a negative + memLevel). msg is set to null if there is no error message. inflateInit2 + does not perform any decompression apart from reading the zlib header if + present: this will be done by inflate(). (So next_in and avail_in may be + modified, but next_out and avail_out are unchanged.) +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate + if this call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by this call of + inflate. The compressor and decompressor must use exactly the same + dictionary (see deflateSetDictionary). + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_stream FAR *strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the paramaters are invalid, Z_MEM_ERROR if the internal state could not + be allocated, or Z_VERSION_ERROR if the version of the library does not + match the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_stream FAR *strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is more efficient than inflate() for + file i/o applications in that it avoids copying between the output and the + sliding window by simply making the window itself the output buffer. This + function trusts the application to not change the output buffer passed by + the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free + the allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects + only the raw deflate stream to decompress. This is different from the + normal behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format + error in the deflate stream (in which case strm->msg is set to indicate the + nature of the error), or Z_STREAM_ERROR if the stream was not properly + initialized. In the case of Z_BUF_ERROR, an input or output error can be + distinguished using strm->next_in which will be Z_NULL only if in() returned + an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to + out() returning non-zero. (in() will always be called before out(), so + strm->next_in is assured to be defined if out() returns non-zero.) Note + that inflateBack() cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_stream FAR *strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + + + /* utility functions */ + +/* + The following utility functions are implemented on top of the + basic stream-oriented functions. To simplify the interface, some + default options are assumed (compression level and memory usage, + standard memory allocation functions). The source code of these + utility functions can easily be modified if you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be at least the value returned + by compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + This function can be used to compress a whole file at once if the + input file is mmap'ed. + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before + a compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. +*/ + + +typedef voidp gzFile; + +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); +/* + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb") but can also include a compression level + ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for + Huffman only compression as in "wb1h", or 'R' for run-length encoding + as in "wb1R". (See the description of deflateInit2 for more information + about the strategy parameter.) + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). */ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen() associates a gzFile with the file descriptor fd. File + descriptors are obtained from calls like open, dup, creat, pipe or + fileno (in the file has been previously opened with fopen). + The mode parameter is as in gzopen. + The next call of gzclose on the returned gzFile will also close the + file descriptor fd, just like fclose(fdopen(fd), mode) closes the file + descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). + gzdopen returns NULL if there was insufficient memory to allocate + the (de)compression state. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. + If the input file was not in gzip format, gzread copies the given number + of bytes into the buffer. + gzread returns the number of uncompressed bytes actually read (0 for + end of file, -1 for error). */ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes actually written + (0 in case of error). +*/ + +ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). The number of + uncompressed bytes written is limited to 4095. The caller should assure that + this limit is not exceeded. If it is exceeded, then gzprintf() will return + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or + a newline character is read and transferred to buf, or an end-of-file + condition is encountered. The string is then terminated with a null + character. + gzgets returns buf, or Z_NULL in case of error. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read again later. + Only one character of push-back is allowed. gzungetc() returns the + character pushed, or -1 on failure. gzungetc() will fail if a + character has been pushed but not read yet, or if c is -1. The pushed + character will be discarded if the stream is repositioned with gzseek() + or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. The return value is the zlib + error number (see function gzerror below). gzflush returns Z_OK if + the flush parameter is Z_FINISH and all output could be flushed. + gzflush should be called only when strictly necessary because it can + degrade compression. +*/ + +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); +/* + Sets the starting position for the next gzread or gzwrite on the + given compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); +/* + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. The return value is the zlib + error number (see function gzerror below). +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the + compression library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); + +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running crc with the bytes buf[0..len-1] and return the updated + crc. If buf is NULL, this function returns the required initial value + for the crc. Pre- and post-conditioning (one's complement) is performed + within this function so it shouldn't be done by the application. + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_stream FAR *strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, sizeof(z_stream)) + + +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; /* hack for buggy compilers */ +#endif + +ZEXTERN const char * ZEXPORT zError OF((int err)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z)); +ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/zlib/zutil.c b/zlib/zutil.c new file mode 100644 index 0000000..a94cdb8 --- /dev/null +++ b/zlib/zutil.c @@ -0,0 +1,319 @@ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include "zutil.h" + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +#ifndef STDC +extern void exit OF((int)); +#endif + +const char * const z_errmsg[10] = { +"need dictionary", /* Z_NEED_DICT 2 */ +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +"incompatible version",/* Z_VERSION_ERROR (-6) */ +""}; + + +const char * ZEXPORT zlibVersion() +{ + return ZLIB_VERSION; +} + +uLong ZEXPORT zlibCompileFlags() +{ + uLong flags; + + flags = 0; + switch (sizeof(uInt)) { + case 2: break; + case 4: flags += 1; break; + case 8: flags += 2; break; + default: flags += 3; + } + switch (sizeof(uLong)) { + case 2: break; + case 4: flags += 1 << 2; break; + case 8: flags += 2 << 2; break; + default: flags += 3 << 2; + } + switch (sizeof(voidpf)) { + case 2: break; + case 4: flags += 1 << 4; break; + case 8: flags += 2 << 4; break; + default: flags += 3 << 4; + } + switch (sizeof(z_off_t)) { + case 2: break; + case 4: flags += 1 << 6; break; + case 8: flags += 2 << 6; break; + default: flags += 3 << 6; + } +#ifdef DEBUG + flags += 1 << 8; +#endif +#if defined(ASMV) || defined(ASMINF) + flags += 1 << 9; +#endif +#ifdef ZLIB_WINAPI + flags += 1 << 10; +#endif +#ifdef BUILDFIXED + flags += 1 << 12; +#endif +#ifdef DYNAMIC_CRC_TABLE + flags += 1 << 13; +#endif +#ifdef NO_GZCOMPRESS + flags += 1 << 16; +#endif +#ifdef NO_GZIP + flags += 1 << 17; +#endif +#ifdef PKZIP_BUG_WORKAROUND + flags += 1 << 20; +#endif +#ifdef FASTEST + flags += 1 << 21; +#endif +#ifdef STDC +# ifdef NO_vsnprintf + flags += 1 << 25; +# ifdef HAS_vsprintf_void + flags += 1 << 26; +# endif +# else +# ifdef HAS_vsnprintf_void + flags += 1 << 26; +# endif +# endif +#else + flags += 1 << 24; +# ifdef NO_snprintf + flags += 1 << 25; +# ifdef HAS_sprintf_void + flags += 1 << 26; +# endif +# else +# ifdef HAS_snprintf_void + flags += 1 << 26; +# endif +# endif +#endif + return flags; +} + +#ifdef DEBUG + +# ifndef verbose +# define verbose 0 +# endif +int z_verbose = verbose; + +void z_error (m) + char *m; +{ + fprintf(stderr, "%s\n", m); + exit(1); +} +#endif + +/* exported to allow conversion of error code to string for compress() and + * uncompress() + */ +const char * ZEXPORT zError(err) + int err; +{ + return ERR_MSG(err); +} + +#if defined(_WIN32_WCE) + /* does not exist on WCE */ + int errno = 0; +#endif + +#ifndef HAVE_MEMCPY + +void zmemcpy(dest, source, len) + Bytef* dest; + const Bytef* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int zmemcmp(s1, s2, len) + const Bytef* s1; + const Bytef* s2; + uInt len; +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void zmemzero(dest, len) + Bytef* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + + +#ifdef SYS16BIT + +#ifdef __TURBOC__ +/* Turbo C in 16-bit mode */ + +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf = opaque; /* just to make some compilers happy */ + ulg bsize = (ulg)items*size; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == NULL || next_ptr >= MAX_PTR) return NULL; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + int n; + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + ptr = opaque; /* just to make some compilers happy */ + Assert(0, "zcfree: ptr not found"); +} + +#endif /* __TURBOC__ */ + + +#ifdef M_I86 +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + return _halloc((long)items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + _hfree(ptr); +} + +#endif /* M_I86 */ + +#endif /* SYS16BIT */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern voidp calloc OF((uInt items, uInt size)); +extern void free OF((voidpf ptr)); +#endif + +voidpf zcalloc (opaque, items, size) + voidpf opaque; + unsigned items; + unsigned size; +{ + if (opaque) items += size - size; /* make compiler happy */ + return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : + (voidpf)calloc(items, size); +} + +void zcfree (opaque, ptr) + voidpf opaque; + voidpf ptr; +{ + free(ptr); + if (opaque) return; /* make compiler happy */ +} + +#endif /* MY_ZCALLOC */ diff --git a/zlib/zutil.h b/zlib/zutil.h new file mode 100644 index 0000000..06cb62e --- /dev/null +++ b/zlib/zutil.h @@ -0,0 +1,258 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef ZUTIL_H +#define ZUTIL_H + +#define ZLIB_INTERNAL +#include "zlib.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) +# define OS_CODE 0x00 +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include +# endif +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 0x07 +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#ifdef WIN32 +# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ +# define OS_CODE 0x0b +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0f +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) +# if defined(_WIN32_WCE) +# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef _PTRDIFF_T_DEFINED + typedef int ptrdiff_t; +# define _PTRDIFF_T_DEFINED +# endif +# else +# define fdopen(fd,type) _fdopen(fd,type) +# endif +#endif + + /* common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS + /* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# ifdef WIN32 + /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) +# define vsnprintf _vsnprintf +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +#endif + +#ifdef HAVE_STRERROR + extern char *strerror OF((int)); +# define zstrerror(errnum) strerror(errnum) +#else +# define zstrerror(errnum) "" +#endif + +#if defined(pyr) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + extern void zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include + extern int z_verbose; + extern void z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); +void zcfree OF((voidpf opaque, voidpf ptr)); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +#endif /* ZUTIL_H */