Subversion Repositories Games.Descent

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
/*
2
 * Portions of this file are copyright Rebirth contributors and licensed as
3
 * described in COPYING.txt.
4
 * Portions of this file are copyright Parallax Software and licensed
5
 * according to the Parallax license below.
6
 * See COPYING.txt for license details.
7
 
8
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
9
SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
10
END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
11
ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
12
IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
13
SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
14
FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
15
CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
16
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
17
COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
18
*/
19
 
20
/*
21
 *
22
 * Functions to dump text description of mine.
23
 * An editor-only function, called at mine load time.
24
 * To be read by a human to verify the correctness and completeness of a mine.
25
 *
26
 */
27
 
28
#include <bitset>
29
#include <stdio.h>
30
#include <cinttypes>
31
#include <string.h>
32
#include <stdarg.h>
33
#include <errno.h>
34
 
35
#include "pstypes.h"
36
#include "console.h"
37
#include "physfsx.h"
38
#include "key.h"
39
#include "gr.h"
40
#include "palette.h"
41
#include "fmtcheck.h"
42
 
43
#include "inferno.h"
44
#if DXX_USE_EDITOR
45
#include "editor/editor.h"
46
#endif
47
#include "dxxerror.h"
48
#include "object.h"
49
#include "wall.h"
50
#include "gamemine.h"
51
#include "gameseg.h"
52
#include "robot.h"
53
#include "player.h"
54
#include "newmenu.h"
55
#include "textures.h"
56
 
57
#include "bm.h"
58
#include "menu.h"
59
#include "switch.h"
60
#include "fuelcen.h"
61
#include "powerup.h"
62
#include "gameseq.h"
63
#include "polyobj.h"
64
#include "gamesave.h"
65
#include "piggy.h"
66
 
67
#include "compiler-range_for.h"
68
#include "d_enumerate.h"
69
#include "d_range.h"
70
#include "d_zip.h"
71
#include "segiter.h"
72
 
73
#if DXX_USE_EDITOR
74
 
75
namespace dsx {
76
namespace {
77
#if defined(DXX_BUILD_DESCENT_I)
78
using perm_tmap_buffer_type = std::array<int, MAX_TEXTURES>;
79
using level_tmap_buffer_type = std::array<int8_t, MAX_TEXTURES>;
80
using wall_buffer_type = std::array<int, MAX_WALL_ANIMS>;
81
#elif defined(DXX_BUILD_DESCENT_II)
82
using perm_tmap_buffer_type = std::array<int, MAX_BITMAP_FILES>;
83
using level_tmap_buffer_type = std::array<int8_t, MAX_BITMAP_FILES>;
84
using wall_buffer_type = std::array<int, MAX_BITMAP_FILES>;
85
#endif
86
}
87
}
88
 
89
namespace dcx {
90
const std::array<char[10], 7> Wall_names{{
91
        "NORMAL   ",
92
        "BLASTABLE",
93
        "DOOR     ",
94
        "ILLUSION ",
95
        "OPEN     ",
96
        "CLOSED   ",
97
        "EXTERNAL "
98
}};
99
}
100
 
101
static void dump_used_textures_level(PHYSFS_File *my_file, int level_num, const char *Gamesave_current_filename);
102
static void say_totals(fvcobjptridx &vcobjptridx, PHYSFS_File *my_file, const char *level_name);
103
 
104
namespace dsx {
105
const std::array<char[9], MAX_OBJECT_TYPES> Object_type_names{{
106
        "WALL    ",
107
        "FIREBALL",
108
        "ROBOT   ",
109
        "HOSTAGE ",
110
        "PLAYER  ",
111
        "WEAPON  ",
112
        "CAMERA  ",
113
        "POWERUP ",
114
        "DEBRIS  ",
115
        "CNTRLCEN",
116
        "FLARE   ",
117
        "CLUTTER ",
118
        "GHOST   ",
119
        "LIGHT   ",
120
        "COOP    ",
121
#if defined(DXX_BUILD_DESCENT_II)
122
        "MARKER  ",
123
#endif
124
}};
125
 
126
// ----------------------------------------------------------------------------
127
static const char *object_types(const object_base &objp)
128
{
129
        const auto type = objp.type;
130
        assert(type == OBJ_NONE || type < MAX_OBJECT_TYPES);
131
        return &Object_type_names[type][0];
132
}
133
}
134
 
135
// ----------------------------------------------------------------------------
136
static const char *object_ids(const object_base &objp)
137
{
138
        switch (objp.type)
139
        {
140
                case OBJ_ROBOT:
141
                        return Robot_names[get_robot_id(objp)].data();
142
                case OBJ_POWERUP:
143
                        return Powerup_names[get_powerup_id(objp)].data();
144
                default:
145
                        return nullptr;
146
        }
147
}
148
 
149
static void err_puts(PHYSFS_File *f, const char *str, size_t len) __attribute_nonnull();
150
static void err_puts(PHYSFS_File *f, const char *str, size_t len)
151
#define err_puts(A1,S,...)      (err_puts(A1,S, _dxx_call_puts_parameter2(1, ## __VA_ARGS__, strlen(S))))
152
{
153
        con_puts(CON_CRITICAL, str, len);
154
        PHYSFSX_puts(f, str);
155
        Errors_in_mine++;
156
}
157
 
158
template <size_t len>
159
static void err_puts_literal(PHYSFS_File *f, const char (&str)[len]) __attribute_nonnull();
160
template <size_t len>
161
static void err_puts_literal(PHYSFS_File *f, const char (&str)[len])
162
{
163
        err_puts(f, str, len);
164
}
165
 
166
static void err_printf(PHYSFS_File *my_file, const char * format, ... ) __attribute_format_printf(2, 3);
167
static void err_printf(PHYSFS_File *my_file, const char * format, ... )
168
#define err_printf(A1,F,...)    dxx_call_printf_checked(err_printf,err_puts_literal,(A1),(F),##__VA_ARGS__)
169
{
170
        va_list args;
171
        char            message[256];
172
 
173
        va_start(args, format );
174
        size_t len = vsnprintf(message,sizeof(message),format,args);
175
        va_end(args);
176
        err_puts(my_file, message, len);
177
}
178
 
179
static void warning_puts(PHYSFS_File *f, const char *str, size_t len) __attribute_nonnull();
180
static void warning_puts(PHYSFS_File *f, const char *str, size_t len)
181
#define warning_puts(A1,S,...)  (warning_puts(A1,S, _dxx_call_puts_parameter2(1, ## __VA_ARGS__, strlen(S))))
182
{
183
        con_puts(CON_URGENT, str, len);
184
        PHYSFSX_puts(f, str);
185
}
186
 
187
template <size_t len>
188
static void warning_puts_literal(PHYSFS_File *f, const char (&str)[len]) __attribute_nonnull();
189
template <size_t len>
190
static void warning_puts_literal(PHYSFS_File *f, const char (&str)[len])
191
{
192
        warning_puts(f, str, len);
193
}
194
 
195
static void warning_printf(PHYSFS_File *my_file, const char * format, ... ) __attribute_format_printf(2, 3);
196
static void warning_printf(PHYSFS_File *my_file, const char * format, ... )
197
#define warning_printf(A1,F,...)        dxx_call_printf_checked(warning_printf,warning_puts_literal,(A1),(F),##__VA_ARGS__)
198
{
199
        va_list args;
200
        char            message[256];
201
 
202
        va_start(args, format );
203
        vsnprintf(message,sizeof(message),format,args);
204
        va_end(args);
205
        warning_puts(my_file, message);
206
}
207
 
208
// ----------------------------------------------------------------------------
209
namespace dsx {
210
static void write_exit_text(fvcsegptridx &vcsegptridx, fvcwallptridx &vcwallptridx, PHYSFS_File *my_file)
211
{
212
        int     count;
213
 
214
        PHYSFSX_printf(my_file, "-----------------------------------------------------------------------------\n");
215
        PHYSFSX_printf(my_file, "Exit stuff\n");
216
 
217
        //      ---------- Find exit triggers ----------
218
        count=0;
219
        auto &Triggers = LevelUniqueWallSubsystemState.Triggers;
220
        auto &vctrgptridx = Triggers.vcptridx;
221
        range_for (const auto &&t, vctrgptridx)
222
        {
223
                if (trigger_is_exit(t))
224
                {
225
                        int     count2;
226
 
227
                        const auto i = t.get_unchecked_index();
228
                        PHYSFSX_printf(my_file, "Trigger %2i, is an exit trigger with %i links.\n", i, t->num_links);
229
                        count++;
230
                        if (t->num_links != 0)
231
                                err_printf(my_file, "Error: Exit triggers must have 0 links, this one has %i links.", t->num_links);
232
 
233
                        //      Find wall pointing to this trigger.
234
                        count2 = 0;
235
                        range_for (const auto &&w, vcwallptridx)
236
                        {
237
                                if (w->trigger == i)
238
                                {
239
                                        count2++;
240
                                        PHYSFSX_printf(my_file, "Exit trigger %i is in segment %i, on side %i, bound to wall %i\n", i, w->segnum, w->sidenum, static_cast<wallnum_t>(w));
241
                                }
242
                        }
243
                        if (count2 == 0)
244
                                err_printf(my_file, "Error: Trigger %i is not bound to any wall.", i);
245
                        else if (count2 > 1)
246
                                err_printf(my_file, "Error: Trigger %i is bound to %i walls.", i, count2);
247
 
248
                }
249
        }
250
 
251
        if (count == 0)
252
                err_printf(my_file, "Error: No exit trigger in this mine.");
253
        else if (count != 1)
254
                err_printf(my_file, "Error: More than one exit trigger in this mine.");
255
        else
256
                PHYSFSX_printf(my_file, "\n");
257
 
258
        //      ---------- Find exit doors ----------
259
        count = 0;
260
        range_for (const auto &&segp, vcsegptridx)
261
        {
262
                range_for (const auto &&es, enumerate(segp->children))
263
                {
264
                        if (es.value == segment_exit)
265
                        {
266
                                PHYSFSX_printf(my_file, "Segment %3hu, side %" PRIuFAST32 " is an exit door.\n", segp.get_unchecked_index(), es.idx);
267
                                count++;
268
                        }
269
                }
270
        }
271
 
272
        if (count == 0)
273
                err_printf(my_file, "Error: No external wall in this mine.");
274
        else if (count != 1) {
275
#if defined(DXX_BUILD_DESCENT_I)
276
                warning_printf(my_file, "Warning: %i external walls in this mine.", count);
277
                warning_printf(my_file, "(If %i are secret exits, then no problem.)", count-1);
278
#endif
279
        } else
280
                PHYSFSX_printf(my_file, "\n");
281
}
282
}
283
 
284
namespace {
285
 
286
class key_stat
287
{
288
        const char *const label;
289
        unsigned wall_count = 0, powerup_count = 0;
290
        segnum_t seg = segment_none;
291
        uint8_t side = 0;
292
public:
293
        key_stat(const char *const p) :
294
                label(p)
295
        {
296
        }
297
        void check_wall(const segment_array &segments, PHYSFS_File *const fp, const vcwallptridx_t wpi, const wall_key_t key)
298
        {
299
                auto &w = *wpi;
300
                if (!(w.keys & key))
301
                        return;
302
                PHYSFSX_printf(fp, "Wall %i (seg=%i, side=%i) is keyed to the %s key.\n", static_cast<wallnum_t>(wpi), w.segnum, w.sidenum, label);
303
                if (seg == segment_none)
304
                {
305
                        seg = w.segnum;
306
                        side = w.sidenum;
307
                }
308
                else
309
                {
310
                        const auto &&connect_side = find_connect_side(segments.vcptridx(w.segnum), segments.vcptr(seg));
311
                        if (connect_side == side)
312
                                return;
313
                        warning_printf(fp, "Warning: This door at seg %i, is different than the one at seg %i, side %i", w.segnum, seg, side);
314
                }
315
                ++wall_count;
316
        }
317
        void report_walls(PHYSFS_File *const fp) const
318
        {
319
                if (wall_count > 1)
320
                        warning_printf(fp, "Warning: %i doors are keyed to the %s key.", wall_count, label);
321
        }
322
        void found_powerup(const unsigned amount = 1)
323
        {
324
                powerup_count += amount;
325
        }
326
        void report_keys(PHYSFS_File *const fp) const
327
        {
328
                if (!powerup_count)
329
                {
330
                        if (wall_count)
331
                                err_printf(fp, "Error: There is a door keyed to the %s key, but no %s key!", label, label);
332
                }
333
                else if (powerup_count > 1)
334
                        err_printf(fp, "Error: There are %i %s keys!", powerup_count, label);
335
        }
336
};
337
 
338
}
339
 
340
// ----------------------------------------------------------------------------
341
static void write_key_text(fvcobjptridx &vcobjptridx, segment_array &segments, fvcwallptridx &vcwallptridx, PHYSFS_File *my_file)
342
{
343
        PHYSFSX_printf(my_file, "-----------------------------------------------------------------------------\n");
344
        PHYSFSX_printf(my_file, "Key stuff:\n");
345
 
346
        key_stat blue("blue"), gold("gold"), red("red");
347
 
348
        range_for (const auto &&w, vcwallptridx)
349
        {
350
                blue.check_wall(segments, my_file, w, KEY_BLUE);
351
                gold.check_wall(segments, my_file, w, KEY_GOLD);
352
                red.check_wall(segments, my_file, w, KEY_RED);
353
        }
354
 
355
        blue.report_walls(my_file);
356
        gold.report_walls(my_file);
357
        red.report_walls(my_file);
358
 
359
        range_for (const auto &&objp, vcobjptridx)
360
        {
361
                if (objp->type == OBJ_POWERUP)
362
                {
363
                        const char *color;
364
                        const auto id = get_powerup_id(objp);
365
                        if (
366
                                (id == POW_KEY_BLUE && (blue.found_powerup(), color = "BLUE", true)) ||
367
                                (id == POW_KEY_RED && (red.found_powerup(), color = "RED", true)) ||
368
                                (id == POW_KEY_GOLD && (gold.found_powerup(), color = "GOLD", true))
369
                        )
370
                        {
371
                                PHYSFSX_printf(my_file, "The %s key is object %hu in segment %i\n", color, static_cast<objnum_t>(objp), objp->segnum);
372
                        }
373
                }
374
 
375
                if (const auto contains_count = objp->contains_count)
376
                {
377
                        if (objp->contains_type == OBJ_POWERUP)
378
                        {
379
                                const char *color;
380
                                const auto id = objp->contains_id;
381
                                if (
382
                                        (id == POW_KEY_BLUE && (blue.found_powerup(contains_count), color = "BLUE", true)) ||
383
                                        (id == POW_KEY_RED && (red.found_powerup(contains_count), color = "RED", true)) ||
384
                                        (id == POW_KEY_GOLD && (gold.found_powerup(contains_count), color = "GOLD", true))
385
                                )
386
                                        PHYSFSX_printf(my_file, "The %s key is contained in object %hu (a %s %s) in segment %hu\n", color, static_cast<objnum_t>(objp), object_types(objp), Robot_names[get_robot_id(objp)].data(), objp->segnum);
387
                        }
388
                }
389
        }
390
 
391
        blue.report_keys(my_file);
392
        gold.report_keys(my_file);
393
        red.report_keys(my_file);
394
}
395
 
396
// ----------------------------------------------------------------------------
397
static void write_control_center_text(fvcsegptridx &vcsegptridx, PHYSFS_File *my_file)
398
{
399
        auto &Objects = LevelUniqueObjectState.Objects;
400
        auto &vcobjptridx = Objects.vcptridx;
401
        int     count, count2;
402
 
403
        PHYSFSX_printf(my_file, "-----------------------------------------------------------------------------\n");
404
        PHYSFSX_printf(my_file, "Control Center stuff:\n");
405
 
406
        count = 0;
407
        range_for (const auto &&segp, vcsegptridx)
408
        {
409
                if (segp->special == SEGMENT_IS_CONTROLCEN)
410
                {
411
                        count++;
412
                        PHYSFSX_printf(my_file, "Segment %3hu is a control center.\n", static_cast<uint16_t>(segp));
413
                        count2 = 0;
414
                        range_for (const object &objp, objects_in(segp, vcobjptridx, vcsegptr))
415
                        {
416
                                if (objp.type == OBJ_CNTRLCEN)
417
                                        count2++;
418
                        }
419
                        if (count2 == 0)
420
                                PHYSFSX_printf(my_file, "No control center object in control center segment.\n");
421
                        else if (count2 != 1)
422
                                PHYSFSX_printf(my_file, "%i control center objects in control center segment.\n", count2);
423
                }
424
        }
425
 
426
        if (count == 0)
427
                err_printf(my_file, "Error: No control center in this mine.");
428
        else if (count != 1)
429
                err_printf(my_file, "Error: More than one control center in this mine.");
430
}
431
 
432
// ----------------------------------------------------------------------------
433
static void write_fuelcen_text(PHYSFS_File *my_file)
434
{
435
        auto &Station = LevelUniqueFuelcenterState.Station;
436
        int     i;
437
 
438
        PHYSFSX_printf(my_file, "-----------------------------------------------------------------------------\n");
439
        PHYSFSX_printf(my_file, "Fuel Center stuff: (Note: This means fuel, repair, materialize, control centers!)\n");
440
 
441
        const auto Num_fuelcenters = LevelUniqueFuelcenterState.Num_fuelcenters;
442
        for (i=0; i<Num_fuelcenters; i++) {
443
                PHYSFSX_printf(my_file, "Fuelcenter %i: Type=%i (%s), segment = %3i\n", i, Station[i].Type, Special_names[Station[i].Type], Station[i].segnum);
444
                if (Segments[Station[i].segnum].special != Station[i].Type)
445
                        err_printf(my_file, "Error: Conflicting data: Segment %i has special type %i (%s), expected to be %i", Station[i].segnum, Segments[Station[i].segnum].special, Special_names[Segments[Station[i].segnum].special], Station[i].Type);
446
        }
447
}
448
 
449
// ----------------------------------------------------------------------------
450
static void write_segment_text(fvcsegptridx &vcsegptridx, PHYSFS_File *my_file)
451
{
452
        auto &Objects = LevelUniqueObjectState.Objects;
453
        auto &vcobjptridx = Objects.vcptridx;
454
        PHYSFSX_printf(my_file, "-----------------------------------------------------------------------------\n");
455
        PHYSFSX_printf(my_file, "Segment stuff:\n");
456
 
457
        range_for (const auto &&segp, vcsegptridx)
458
        {
459
                PHYSFSX_printf(my_file, "Segment %4hu:", static_cast<uint16_t>(segp));
460
                if (segp->special != 0)
461
                        PHYSFSX_printf(my_file, " special = %3i (%s), station_idx=%3i", segp->special, Special_names[segp->special], segp->station_idx);
462
                if (segp->matcen_num != -1)
463
                        PHYSFSX_printf(my_file, " matcen = %3i", segp->matcen_num);
464
                PHYSFSX_printf(my_file, "\n");
465
        }
466
 
467
        range_for (const auto &&segp, vcsegptridx)
468
        {
469
                int     depth;
470
 
471
                PHYSFSX_printf(my_file, "Segment %4hu: ", static_cast<uint16_t>(segp));
472
                depth=0;
473
                        PHYSFSX_printf(my_file, "Objects: ");
474
                range_for (const auto objp, objects_in(segp, vcobjptridx, vcsegptr))
475
                        {
476
                                short objnum = objp;
477
                                PHYSFSX_printf(my_file, "[%8s %8s %3i] ", object_types(objp), object_ids(objp), objnum);
478
                                if (depth++ > 30) {
479
                                        PHYSFSX_printf(my_file, "\nAborted after %i links\n", depth);
480
                                        break;
481
                                }
482
                        }
483
                PHYSFSX_printf(my_file, "\n");
484
        }
485
}
486
 
487
// ----------------------------------------------------------------------------
488
// This routine is bogus.  It assumes that all centers are matcens,
489
// which is not true.  The setting of segnum is bogus.
490
static void write_matcen_text(PHYSFS_File *my_file)
491
{
492
        int     i;
493
 
494
        PHYSFSX_printf(my_file, "-----------------------------------------------------------------------------\n");
495
        PHYSFSX_printf(my_file, "Materialization centers:\n");
496
        auto &RobotCenters = LevelSharedRobotcenterState.RobotCenters;
497
        auto &Station = LevelUniqueFuelcenterState.Station;
498
        auto &Triggers = LevelUniqueWallSubsystemState.Triggers;
499
        auto &vctrgptridx = Triggers.vcptridx;
500
        const auto Num_robot_centers = LevelSharedRobotcenterState.Num_robot_centers;
501
        for (i=0; i<Num_robot_centers; i++) {
502
                int     trigger_count=0, fuelcen_num;
503
 
504
                PHYSFSX_printf(my_file, "FuelCenter[%02i].Segment = %04i  ", i, Station[i].segnum);
505
                PHYSFSX_printf(my_file, "Segment[%04i].matcen_num = %02i  ", Station[i].segnum, Segments[Station[i].segnum].matcen_num);
506
 
507
                fuelcen_num = RobotCenters[i].fuelcen_num;
508
                if (Station[fuelcen_num].Type != SEGMENT_IS_ROBOTMAKER)
509
                        err_printf(my_file, "Error: Matcen %i corresponds to Station %i, which has type %i (%s).", i, fuelcen_num, Station[fuelcen_num].Type, Special_names[Station[fuelcen_num].Type]);
510
 
511
                auto segnum = Station[fuelcen_num].segnum;
512
 
513
                //      Find trigger for this materialization center.
514
                range_for (auto &&t, vctrgptridx)
515
                {
516
                        if (trigger_is_matcen(t))
517
                        {
518
                                range_for (auto &k, partial_const_range(t->seg, t->num_links))
519
                                        if (k == segnum)
520
                                        {
521
                                                PHYSFSX_printf(my_file, "Trigger = %2i  ", t.get_unchecked_index());
522
                                                trigger_count++;
523
                                        }
524
                        }
525
                }
526
                PHYSFSX_printf(my_file, "\n");
527
 
528
                if (trigger_count == 0)
529
                        err_printf(my_file, "Error: Matcen %i in segment %i has no trigger!", i, segnum);
530
 
531
        }
532
}
533
 
534
// ----------------------------------------------------------------------------
535
namespace dsx {
536
static void write_wall_text(fvcsegptridx &vcsegptridx, fvcwallptridx &vcwallptridx, PHYSFS_File *my_file)
537
{
538
        std::array<int8_t, MAX_WALLS> wall_flags;
539
 
540
        PHYSFSX_printf(my_file, "-----------------------------------------------------------------------------\n");
541
        PHYSFSX_printf(my_file, "Walls:\n");
542
#if defined(DXX_BUILD_DESCENT_II)
543
        auto &Triggers = LevelUniqueWallSubsystemState.Triggers;
544
#endif
545
        range_for (auto &&wp, vcwallptridx)
546
        {
547
                auto &w = *wp;
548
                int     sidenum;
549
 
550
                const auto i = static_cast<wallnum_t>(wp);
551
                PHYSFSX_printf(my_file, "Wall %03i: seg=%3i, side=%2i, linked_wall=%3i, type=%s, flags=%4x, hps=%3i, trigger=%2i, clip_num=%2i, keys=%2i, state=%i\n", i,
552
                        w.segnum, w.sidenum, w.linked_wall, Wall_names[w.type], w.flags, w.hps >> 16, w.trigger, w.clip_num, w.keys, w.state);
553
 
554
#if defined(DXX_BUILD_DESCENT_II)
555
                if (w.trigger >= Triggers.get_count())
556
                        PHYSFSX_printf(my_file, "Wall %03d points to invalid trigger %d\n",i,w.trigger);
557
#endif
558
 
559
                auto segnum = w.segnum;
560
                sidenum = w.sidenum;
561
 
562
                if (Segments[segnum].shared_segment::sides[sidenum].wall_num != wp)
563
                        err_printf(my_file, "Error: Wall %u points at segment %i, side %i, but that segment doesn't point back (it's wall_num = %hi)", i, segnum, sidenum, static_cast<int16_t>(Segments[segnum].shared_segment::sides[sidenum].wall_num));
564
        }
565
 
566
        wall_flags = {};
567
 
568
        range_for (const auto &&segp, vcsegptridx)
569
        {
570
                range_for (const auto &&es, enumerate(segp->shared_segment::sides))
571
                {
572
                        const auto sidep = &es.value;
573
                        if (sidep->wall_num != wall_none)
574
                        {
575
                                if (wall_flags[sidep->wall_num])
576
                                        err_printf(my_file, "Error: Wall %hu appears in two or more segments, including segment %hu, side %" PRIuFAST32 ".", static_cast<int16_t>(sidep->wall_num), static_cast<segnum_t>(segp), es.idx);
577
                                else
578
                                        wall_flags[sidep->wall_num] = 1;
579
                        }
580
                }
581
        }
582
 
583
}
584
}
585
 
586
// ----------------------------------------------------------------------------
587
namespace dsx {
588
static void write_player_text(fvcobjptridx &vcobjptridx, PHYSFS_File *my_file)
589
{
590
        int     num_players=0;
591
 
592
        PHYSFSX_printf(my_file, "-----------------------------------------------------------------------------\n");
593
        PHYSFSX_printf(my_file, "Players:\n");
594
        range_for (const auto &&objp, vcobjptridx)
595
        {
596
                if (objp->type == OBJ_PLAYER)
597
                {
598
                        num_players++;
599
                        PHYSFSX_printf(my_file, "Player %2i is object #%3hu in segment #%3i.\n", get_player_id(objp), static_cast<uint16_t>(objp), objp->segnum);
600
                }
601
        }
602
 
603
#if defined(DXX_BUILD_DESCENT_II)
604
        if (num_players != MAX_PLAYERS)
605
                err_printf(my_file, "Error: %i player objects.  %i are required.", num_players, MAX_PLAYERS);
606
#endif
607
        if (num_players > MAX_MULTI_PLAYERS)
608
                err_printf(my_file, "Error: %i player objects.  %i are required.", num_players, MAX_PLAYERS);
609
}
610
}
611
 
612
// ----------------------------------------------------------------------------
613
namespace dsx {
614
static void write_trigger_text(PHYSFS_File *my_file)
615
{
616
        PHYSFSX_printf(my_file, "-----------------------------------------------------------------------------\n");
617
        PHYSFSX_printf(my_file, "Triggers:\n");
618
        auto &Triggers = LevelUniqueWallSubsystemState.Triggers;
619
        auto &vctrgptridx = Triggers.vcptridx;
620
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
621
        auto &vcwallptr = Walls.vcptr;
622
        range_for (auto &&t, vctrgptridx)
623
        {
624
                const auto i = static_cast<trgnum_t>(t);
625
#if defined(DXX_BUILD_DESCENT_I)
626
                PHYSFSX_printf(my_file, "Trigger %03i: flags=%04x, value=%08x, time=%8x, num_links=%i ", i, t->flags, static_cast<unsigned>(t->value), 0, t->num_links);
627
#elif defined(DXX_BUILD_DESCENT_II)
628
                PHYSFSX_printf(my_file, "Trigger %03i: type=%02x flags=%04x, value=%08x, time=%8x, num_links=%i ", i,
629
                        static_cast<uint8_t>(t->type), static_cast<uint8_t>(t->flags), t->value, 0, t->num_links);
630
#endif
631
 
632
                for (unsigned j = 0; j < t->num_links; ++j)
633
                        PHYSFSX_printf(my_file, "[%03i:%i] ", t->seg[j], t->side[j]);
634
 
635
                //      Find which wall this trigger is connected to.
636
                const auto &&we = vcwallptr.end();
637
                const auto &&wi = std::find_if(vcwallptr.begin(), we, [i](const wall *const p) { return p->trigger == i; });
638
                if (wi == we)
639
                        err_printf(my_file, "Error: Trigger %i is not connected to any wall, so it can never be triggered.", i);
640
                else
641
                {
642
                        const auto &&w = *wi;
643
                        PHYSFSX_printf(my_file, "Attached to seg:side = %i:%i, wall %hi\n", w->segnum, w->sidenum, static_cast<int16_t>(vcsegptr(w->segnum)->shared_segment::sides[w->sidenum].wall_num));
644
                }
645
        }
646
}
647
}
648
 
649
// ----------------------------------------------------------------------------
650
void write_game_text_file(const char *filename)
651
{
652
        auto &Objects = LevelUniqueObjectState.Objects;
653
        auto &vcobjptridx = Objects.vcptridx;
654
        char    my_filename[128];
655
        int     namelen;
656
        Errors_in_mine = 0;
657
 
658
        namelen = strlen(filename);
659
 
660
        Assert (namelen > 4);
661
 
662
        Assert (filename[namelen-4] == '.');
663
 
664
        strcpy(my_filename, filename);
665
        strcpy( &my_filename[namelen-4], ".txm");
666
 
667
        auto my_file = PHYSFSX_openWriteBuffered(my_filename);
668
        if (!my_file)   {
669
                gr_palette_load(gr_palette);
670
                nm_messagebox( NULL, 1, "Ok", "ERROR: Unable to open %s\nErrno = %i", my_filename, errno);
671
 
672
                return;
673
        }
674
 
675
        dump_used_textures_level(my_file, 0, filename);
676
        say_totals(vcobjptridx, my_file, filename);
677
 
678
        PHYSFSX_printf(my_file, "\nNumber of segments:   %4i\n", Highest_segment_index+1);
679
        PHYSFSX_printf(my_file, "Number of objects:    %4i\n", Highest_object_index+1);
680
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
681
        auto &vcwallptridx = Walls.vcptridx;
682
        PHYSFSX_printf(my_file, "Number of walls:      %4i\n", Walls.get_count());
683
        auto &ActiveDoors = LevelUniqueWallSubsystemState.ActiveDoors;
684
        PHYSFSX_printf(my_file, "Number of open doors: %4i\n", ActiveDoors.get_count());
685
        {
686
        auto &Triggers = LevelUniqueWallSubsystemState.Triggers;
687
        PHYSFSX_printf(my_file, "Number of triggers:   %4i\n", Triggers.get_count());
688
        }
689
        PHYSFSX_printf(my_file, "Number of matcens:    %4i\n", LevelSharedRobotcenterState.Num_robot_centers);
690
        PHYSFSX_printf(my_file, "\n");
691
 
692
        write_segment_text(vcsegptridx, my_file);
693
 
694
        write_fuelcen_text(my_file);
695
 
696
        write_matcen_text(my_file);
697
 
698
        write_player_text(vcobjptridx, my_file);
699
 
700
        write_wall_text(vcsegptridx, vcwallptridx, my_file);
701
 
702
        write_trigger_text(my_file);
703
 
704
        write_exit_text(vcsegptridx, vcwallptridx, my_file);
705
 
706
        //      ---------- Find control center segment ----------
707
        write_control_center_text(vcsegptridx, my_file);
708
 
709
        //      ---------- Show keyed walls ----------
710
        write_key_text(vcobjptridx, Segments, vcwallptridx, my_file);
711
}
712
 
713
#if defined(DXX_BUILD_DESCENT_II)
714
//      Adam: Change NUM_ADAM_LEVELS to the number of levels.
715
#define NUM_ADAM_LEVELS 30
716
 
717
//      Adam: Stick the names here.
718
constexpr char Adam_level_names[NUM_ADAM_LEVELS][13] = {
719
        "D2LEVA-1.LVL",
720
        "D2LEVA-2.LVL",
721
        "D2LEVA-3.LVL",
722
        "D2LEVA-4.LVL",
723
        "D2LEVA-S.LVL",
724
 
725
        "D2LEVB-1.LVL",
726
        "D2LEVB-2.LVL",
727
        "D2LEVB-3.LVL",
728
        "D2LEVB-4.LVL",
729
        "D2LEVB-S.LVL",
730
 
731
        "D2LEVC-1.LVL",
732
        "D2LEVC-2.LVL",
733
        "D2LEVC-3.LVL",
734
        "D2LEVC-4.LVL",
735
        "D2LEVC-S.LVL",
736
 
737
        "D2LEVD-1.LVL",
738
        "D2LEVD-2.LVL",
739
        "D2LEVD-3.LVL",
740
        "D2LEVD-4.LVL",
741
        "D2LEVD-S.LVL",
742
 
743
        "D2LEVE-1.LVL",
744
        "D2LEVE-2.LVL",
745
        "D2LEVE-3.LVL",
746
        "D2LEVE-4.LVL",
747
        "D2LEVE-S.LVL",
748
 
749
        "D2LEVF-1.LVL",
750
        "D2LEVF-2.LVL",
751
        "D2LEVF-3.LVL",
752
        "D2LEVF-4.LVL",
753
        "D2LEVF-S.LVL",
754
};
755
 
756
static int Ignore_tmap_num2_error;
757
#endif
758
 
759
// ----------------------------------------------------------------------------
760
namespace dsx {
761
#if defined(DXX_BUILD_DESCENT_I)
762
#define determine_used_textures_level(LevelSharedDestructibleLightState,load_level_flag,shareware_flag,level_num,tmap_buf,wall_buffer_type,level_tmap_buf,max_tmap)     determine_used_textures_level(load_level_flag,shareware_flag,level_num,tmap_buf,wall_buffer_type,level_tmap_buf,max_tmap)
763
#endif
764
static void determine_used_textures_level(d_level_shared_destructible_light_state &LevelSharedDestructibleLightState, int load_level_flag, int shareware_flag, int level_num, perm_tmap_buffer_type &tmap_buf, wall_buffer_type &wall_buf, level_tmap_buffer_type &level_tmap_buf, int max_tmap)
765
{
766
#if defined(DXX_BUILD_DESCENT_II)
767
        auto &Objects = LevelUniqueObjectState.Objects;
768
        auto &vcobjptr = Objects.vcptr;
769
#endif
770
        int     j;
771
 
772
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
773
        auto &WallAnims = GameSharedState.WallAnims;
774
#if defined(DXX_BUILD_DESCENT_I)
775
        tmap_buf = {};
776
 
777
        if (load_level_flag) {
778
                load_level(shareware_flag ? Shareware_level_names[level_num] : Registered_level_names[level_num]);
779
        }
780
 
781
        range_for (const cscusegment segp, vcsegptr)
782
        {
783
                range_for (const auto &&z, zip(segp.s.sides, segp.u.sides))
784
                {
785
                        auto &sside = std::get<0>(z);
786
                        if (sside.wall_num != wall_none)
787
                        {
788
                                const auto clip_num = Walls.vcptr(sside.wall_num)->clip_num;
789
                                if (clip_num != -1) {
790
 
791
                                        const auto num_frames = WallAnims[clip_num].num_frames;
792
 
793
                                        wall_buf[clip_num] = 1;
794
 
795
                                        for (j=0; j<num_frames; j++) {
796
                                                int     tmap_num;
797
 
798
                                                tmap_num = WallAnims[clip_num].frames[j];
799
                                                tmap_buf[tmap_num]++;
800
                                                if (level_tmap_buf[tmap_num] == -1)
801
                                                        level_tmap_buf[tmap_num] = level_num + (!shareware_flag) * NUM_SHAREWARE_LEVELS;
802
                                        }
803
                                }
804
                        }
805
 
806
                        auto &uside = std::get<1>(z);
807
                        if (uside.tmap_num >= 0)
808
                         {
809
                                if (uside.tmap_num < max_tmap)
810
                                 {
811
                                        tmap_buf[uside.tmap_num]++;
812
                                        if (level_tmap_buf[uside.tmap_num] == -1)
813
                                                level_tmap_buf[uside.tmap_num] = level_num + (!shareware_flag) * NUM_SHAREWARE_LEVELS;
814
                                 }
815
                                else
816
                                 {
817
                                        Int3(); //      Error, bogus texture map.  Should not be greater than max_tmap.
818
                                 }
819
                         }
820
 
821
                        if (const auto tmap_num2 = uside.tmap_num2 & 0x3fff)
822
                         {
823
                                if (tmap_num2 < max_tmap) {
824
                                        ++tmap_buf[tmap_num2];
825
                                        if (level_tmap_buf[tmap_num2] == -1)
826
                                                level_tmap_buf[tmap_num2] = level_num + (!shareware_flag) * NUM_SHAREWARE_LEVELS;
827
                                } else
828
                                        Int3(); //      Error, bogus texture map.  Should not be greater than max_tmap.
829
                         }
830
                 }
831
         }
832
#elif defined(DXX_BUILD_DESCENT_II)
833
        (void)max_tmap;
834
        (void)shareware_flag;
835
 
836
        tmap_buf = {};
837
 
838
        if (load_level_flag) {
839
                load_level(LevelSharedDestructibleLightState, Adam_level_names[level_num]);
840
        }
841
 
842
        auto &Polygon_models = LevelSharedPolygonModelState.Polygon_models;
843
        //      Process robots.
844
        range_for (const auto &&objp, vcobjptr)
845
        {
846
                if (objp->render_type == RT_POLYOBJ) {
847
                        polymodel *po = &Polygon_models[objp->rtype.pobj_info.model_num];
848
 
849
                        for (unsigned i = 0; i < po->n_textures; ++i)
850
                        {
851
                                unsigned tli = ObjBitmaps[ObjBitmapPtrs[po->first_texture+i]].index;
852
 
853
                                if (tli < tmap_buf.size())
854
                                {
855
                                        tmap_buf[tli]++;
856
                                        if (level_tmap_buf[tli] == -1)
857
                                                level_tmap_buf[tli] = level_num;
858
                                } else
859
                                        Int3(); //      Hmm, It seems this texture is bogus!
860
                        }
861
 
862
                }
863
        }
864
 
865
 
866
        Ignore_tmap_num2_error = 0;
867
 
868
        //      Process walls and segment sides.
869
        range_for (const csmusegment segp, vmsegptr)
870
        {
871
                range_for (const auto &&z, zip(segp.s.sides, segp.u.sides, segp.s.children))
872
                {
873
                        auto &sside = std::get<0>(z);
874
                        auto &uside = std::get<1>(z);
875
                        const auto child = std::get<2>(z);
876
                        if (sside.wall_num != wall_none) {
877
                                const auto clip_num = Walls.vcptr(sside.wall_num)->clip_num;
878
                                if (clip_num != -1) {
879
 
880
                                        // -- int num_frames = WallAnims[clip_num].num_frames;
881
 
882
                                        wall_buf[clip_num] = 1;
883
 
884
                                        for (j=0; j<1; j++) {   //      Used to do through num_frames, but don't really want all the door01#3 stuff.
885
                                                unsigned tmap_num = Textures[WallAnims[clip_num].frames[j]].index;
886
                                                Assert(tmap_num < tmap_buf.size());
887
                                                tmap_buf[tmap_num]++;
888
                                                if (level_tmap_buf[tmap_num] == -1)
889
                                                        level_tmap_buf[tmap_num] = level_num;
890
                                        }
891
                                }
892
                        } else if (child == segment_none) {
893
 
894
                                if (uside.tmap_num >= 0)
895
                                {
896
                                        if (uside.tmap_num < Textures.size()) {
897
                                                const auto ti = Textures[uside.tmap_num].index;
898
                                                assert(ti < tmap_buf.size());
899
                                                ++tmap_buf[ti];
900
                                                if (level_tmap_buf[ti] == -1)
901
                                                        level_tmap_buf[ti] = level_num;
902
                                        } else
903
                                                Int3(); //      Error, bogus texture map.  Should not be greater than max_tmap.
904
                                }
905
 
906
                                if (const auto masked_tmap_num2 = (uside.tmap_num2 & 0x3fff))
907
                                {
908
                                        if (masked_tmap_num2 < Textures.size())
909
                                        {
910
                                                const auto ti = Textures[masked_tmap_num2].index;
911
                                                assert(ti < tmap_buf.size());
912
                                                ++tmap_buf[ti];
913
                                                if (level_tmap_buf[ti] == -1)
914
                                                        level_tmap_buf[ti] = level_num;
915
                                        } else {
916
                                                if (!Ignore_tmap_num2_error)
917
                                                        Int3(); //      Error, bogus texture map.  Should not be greater than max_tmap.
918
                                                Ignore_tmap_num2_error = 1;
919
                                                uside.tmap_num2 = 0;
920
                                        }
921
                                }
922
                        }
923
                }
924
        }
925
#endif
926
}
927
}
928
 
929
// ----------------------------------------------------------------------------
930
template <std::size_t N>
931
static void merge_buffers(std::array<int, N> &dest, const std::array<int, N> &src)
932
{
933
        std::transform(dest.begin(), dest.end(), src.begin(), dest.begin(), std::plus<int>());
934
}
935
 
936
// ----------------------------------------------------------------------------
937
namespace dsx {
938
static void say_used_tmaps(PHYSFS_File *const my_file, const perm_tmap_buffer_type &tb)
939
{
940
        int     i;
941
#if defined(DXX_BUILD_DESCENT_I)
942
        int     count = 0;
943
 
944
        auto &TmapInfo = LevelUniqueTmapInfoState.TmapInfo;
945
        const auto Num_tmaps = LevelUniqueTmapInfoState.Num_tmaps;
946
        for (i=0; i<Num_tmaps; i++)
947
                if (tb[i]) {
948
                        PHYSFSX_printf(my_file, "[%3i %8s (%4i)] ", i, static_cast<const char *>(TmapInfo[i].filename), tb[i]);
949
                        if (count++ >= 4) {
950
                                PHYSFSX_printf(my_file, "\n");
951
                                count = 0;
952
                        }
953
                }
954
#elif defined(DXX_BUILD_DESCENT_II)
955
        for (i = 0; i < tb.size(); ++i)
956
                if (tb[i]) {
957
                        PHYSFSX_printf(my_file, "[%3i %8s (%4i)]\n", i, AllBitmaps[i].name.data(), tb[i]);
958
                }
959
#endif
960
}
961
}
962
 
963
#if defined(DXX_BUILD_DESCENT_I)
964
//      -----------------------------------------------------------------------------
965
static void say_used_once_tmaps(PHYSFS_File *const my_file, const perm_tmap_buffer_type &tb, const level_tmap_buffer_type &tb_lnum)
966
{
967
        int     i;
968
        const char      *level_name;
969
 
970
        auto &TmapInfo = LevelUniqueTmapInfoState.TmapInfo;
971
        const auto Num_tmaps = LevelUniqueTmapInfoState.Num_tmaps;
972
        for (i=0; i<Num_tmaps; i++)
973
                if (tb[i] == 1) {
974
                        int     level_num = tb_lnum[i];
975
                        if (level_num >= NUM_SHAREWARE_LEVELS) {
976
                                Assert((level_num - NUM_SHAREWARE_LEVELS >= 0) && (level_num - NUM_SHAREWARE_LEVELS < NUM_REGISTERED_LEVELS));
977
                                level_name = Registered_level_names[level_num - NUM_SHAREWARE_LEVELS];
978
                        } else {
979
                                Assert((level_num >= 0) && (level_num < NUM_SHAREWARE_LEVELS));
980
                                level_name = Shareware_level_names[level_num];
981
                        }
982
 
983
                        PHYSFSX_printf(my_file, "Texture %3i %8s used only once on level %s\n", i, static_cast<const char *>(TmapInfo[i].filename), level_name);
984
                }
985
}
986
#endif
987
 
988
// ----------------------------------------------------------------------------
989
namespace dsx {
990
static void say_unused_tmaps(PHYSFS_File *my_file, perm_tmap_buffer_type &tb)
991
{
992
        int     i;
993
        int     count = 0;
994
 
995
#if defined(DXX_BUILD_DESCENT_I)
996
        const unsigned bound = LevelUniqueTmapInfoState.Num_tmaps;
997
        auto &TmapInfo = LevelUniqueTmapInfoState.TmapInfo;
998
#elif defined(DXX_BUILD_DESCENT_II)
999
        const unsigned bound = MAX_BITMAP_FILES;
1000
#endif
1001
        for (i=0; i < bound; i++)
1002
                if (!tb[i]) {
1003
                        if (GameBitmaps[Textures[i].index].bm_data == bogus_data.data())
1004
                                PHYSFSX_printf(my_file, "U");
1005
                        else
1006
                                PHYSFSX_printf(my_file, " ");
1007
 
1008
#if defined(DXX_BUILD_DESCENT_I)
1009
                        PHYSFSX_printf(my_file, "[%3i %8s] ", i, static_cast<const char *>(TmapInfo[i].filename));
1010
#elif defined(DXX_BUILD_DESCENT_II)
1011
                        PHYSFSX_printf(my_file, "[%3i %8s] ", i, AllBitmaps[i].name.data());
1012
#endif
1013
                        if (count++ >= 4) {
1014
                                PHYSFSX_printf(my_file, "\n");
1015
                                count = 0;
1016
                        }
1017
                }
1018
}
1019
}
1020
 
1021
#if defined(DXX_BUILD_DESCENT_I)
1022
// ----------------------------------------------------------------------------
1023
static void say_unused_walls(PHYSFS_File *my_file, const wall_buffer_type &tb)
1024
{
1025
        int     i;
1026
        for (i=0; i<Num_wall_anims; i++)
1027
                if (!tb[i])
1028
                        PHYSFSX_printf(my_file, "Wall %3i is unused.\n", i);
1029
}
1030
#endif
1031
 
1032
static void say_totals(fvcobjptridx &vcobjptridx, PHYSFS_File *my_file, const char *level_name)
1033
{
1034
        auto &Objects = LevelUniqueObjectState.Objects;
1035
        int     total_robots = 0;
1036
        int     objects_processed = 0;
1037
 
1038
        PHYSFSX_printf(my_file, "\nLevel %s\n", level_name);
1039
        std::bitset<MAX_OBJECTS> used_objects;
1040
        while (objects_processed < Highest_object_index+1) {
1041
                int     objtype, objid, objcount, min_obj_val;
1042
 
1043
                //      Find new min objnum.
1044
                min_obj_val = 0x7fff0000;
1045
                const object_base *min_objp = nullptr;
1046
 
1047
                range_for (const auto &&objp, vcobjptridx)
1048
                {
1049
                        if (!used_objects[objp] && objp->type != OBJ_NONE)
1050
                        {
1051
                                const auto cur_obj_val = (objp->type << 10) + objp->id;
1052
                                if (cur_obj_val < min_obj_val) {
1053
                                        min_objp = &*objp;
1054
                                        min_obj_val = cur_obj_val;
1055
                                }
1056
                        }
1057
                }
1058
                if (!min_objp || min_objp->type == OBJ_NONE)
1059
                        break;
1060
 
1061
                objcount = 0;
1062
 
1063
                objtype = min_objp->type;
1064
                objid = min_objp->id;
1065
 
1066
                range_for (const auto &&objp, vcobjptridx)
1067
                {
1068
                        if (auto &&uo = used_objects[objp])
1069
                        {
1070
                        }
1071
                        else
1072
                        {
1073
                                if ((objp->type == objtype && objp->id == objid) ||
1074
                                                (objp->type == objtype && objtype == OBJ_PLAYER) ||
1075
                                                (objp->type == objtype && objtype == OBJ_COOP) ||
1076
                                                (objp->type == objtype && objtype == OBJ_HOSTAGE)) {
1077
                                        if (objp->type == OBJ_ROBOT)
1078
                                                total_robots++;
1079
                                        uo = true;
1080
                                        objcount++;
1081
                                        objects_processed++;
1082
                                }
1083
                        }
1084
                }
1085
 
1086
                if (objcount) {
1087
                        PHYSFSX_printf(my_file, "Object: %8s %8s %3i\n", object_types(*min_objp), object_ids(*min_objp), objcount);
1088
                }
1089
        }
1090
 
1091
        PHYSFSX_printf(my_file, "Total robots = %3i\n", total_robots);
1092
}
1093
 
1094
#if defined(DXX_BUILD_DESCENT_II)
1095
int     First_dump_level = 0;
1096
int     Last_dump_level = NUM_ADAM_LEVELS-1;
1097
#endif
1098
 
1099
// ----------------------------------------------------------------------------
1100
namespace dsx {
1101
static void say_totals_all(void)
1102
{
1103
        auto &Objects = LevelUniqueObjectState.Objects;
1104
        auto &vcobjptridx = Objects.vcptridx;
1105
        int     i;
1106
        auto my_file = PHYSFSX_openWriteBuffered("levels.all");
1107
        if (!my_file)   {
1108
                gr_palette_load(gr_palette);
1109
                nm_messagebox( NULL, 1, "Ok", "ERROR: Unable to open levels.all\nErrno=%i", errno );
1110
 
1111
                return;
1112
        }
1113
 
1114
#if defined(DXX_BUILD_DESCENT_I)
1115
        for (i=0; i<NUM_SHAREWARE_LEVELS; i++) {
1116
                load_level(Shareware_level_names[i]);
1117
                say_totals(vcobjptridx, my_file, Shareware_level_names[i]);
1118
        }
1119
 
1120
        for (i=0; i<NUM_REGISTERED_LEVELS; i++) {
1121
                load_level(Registered_level_names[i]);
1122
                say_totals(vcobjptridx, my_file, Registered_level_names[i]);
1123
        }
1124
#elif defined(DXX_BUILD_DESCENT_II)
1125
        for (i=First_dump_level; i<=Last_dump_level; i++) {
1126
                load_level(LevelSharedSegmentState.DestructibleLights, Adam_level_names[i]);
1127
                say_totals(vcobjptridx, my_file, Adam_level_names[i]);
1128
        }
1129
#endif
1130
}
1131
}
1132
 
1133
static void dump_used_textures_level(PHYSFS_File *my_file, int level_num, const char *const Gamesave_current_filename)
1134
{
1135
        perm_tmap_buffer_type temp_tmap_buf;
1136
        level_tmap_buffer_type level_tmap_buf;
1137
 
1138
        level_tmap_buf.fill(-1);
1139
 
1140
        wall_buffer_type temp_wall_buf;
1141
        determine_used_textures_level(LevelSharedSegmentState.DestructibleLights, 0, 1, level_num, temp_tmap_buf, temp_wall_buf, level_tmap_buf, level_tmap_buf.size());
1142
        PHYSFSX_printf(my_file, "\nTextures used in [%s]\n", Gamesave_current_filename);
1143
        say_used_tmaps(my_file, temp_tmap_buf);
1144
}
1145
 
1146
// ----------------------------------------------------------------------------
1147
namespace dsx {
1148
void dump_used_textures_all(void)
1149
{
1150
        int     i;
1151
 
1152
say_totals_all();
1153
 
1154
        auto my_file = PHYSFSX_openWriteBuffered("textures.dmp");
1155
 
1156
        if (!my_file)   {
1157
                gr_palette_load(gr_palette);
1158
                nm_messagebox( NULL, 1, "Ok", "ERROR: Can't open textures.dmp\nErrno=%i", errno);
1159
 
1160
                return;
1161
        }
1162
 
1163
        perm_tmap_buffer_type perm_tmap_buf{};
1164
        level_tmap_buffer_type level_tmap_buf;
1165
        level_tmap_buf.fill(-1);
1166
 
1167
        perm_tmap_buffer_type temp_tmap_buf;
1168
#if defined(DXX_BUILD_DESCENT_I)
1169
        wall_buffer_type perm_wall_buf{};
1170
 
1171
        for (i=0; i<NUM_SHAREWARE_LEVELS; i++) {
1172
                wall_buffer_type temp_wall_buf;
1173
                determine_used_textures_level(LevelSharedDestructibleLightState, 1, 1, i, temp_tmap_buf, temp_wall_buf, level_tmap_buf, level_tmap_buf.size());
1174
                PHYSFSX_printf(my_file, "\nTextures used in [%s]\n", Shareware_level_names[i]);
1175
                say_used_tmaps(my_file, temp_tmap_buf);
1176
                merge_buffers(perm_tmap_buf, temp_tmap_buf);
1177
                merge_buffers(perm_wall_buf, temp_wall_buf);
1178
        }
1179
 
1180
        PHYSFSX_printf(my_file, "\n\nUsed textures in all shareware mines:\n");
1181
        say_used_tmaps(my_file, perm_tmap_buf);
1182
 
1183
        PHYSFSX_printf(my_file, "\nUnused textures in all shareware mines:\n");
1184
        say_unused_tmaps(my_file, perm_tmap_buf);
1185
 
1186
        PHYSFSX_printf(my_file, "\nTextures used exactly once in all shareware mines:\n");
1187
        say_used_once_tmaps(my_file, perm_tmap_buf, level_tmap_buf);
1188
 
1189
        PHYSFSX_printf(my_file, "\nWall anims (eg, doors) unused in all shareware mines:\n");
1190
        say_unused_walls(my_file, perm_wall_buf);
1191
 
1192
        for (i=0; i<NUM_REGISTERED_LEVELS; i++)
1193
#elif defined(DXX_BUILD_DESCENT_II)
1194
        for (i=First_dump_level; i<=Last_dump_level; i++)
1195
#endif
1196
        {
1197
                wall_buffer_type temp_wall_buf;
1198
                determine_used_textures_level(LevelSharedSegmentState.DestructibleLights, 1, 0, i, temp_tmap_buf, temp_wall_buf, level_tmap_buf, level_tmap_buf.size());
1199
#if defined(DXX_BUILD_DESCENT_I)
1200
                PHYSFSX_printf(my_file, "\nTextures used in [%s]\n", Registered_level_names[i]);
1201
#elif defined(DXX_BUILD_DESCENT_II)
1202
                PHYSFSX_printf(my_file, "\nTextures used in [%s]\n", Adam_level_names[i]);
1203
#endif
1204
                say_used_tmaps(my_file, temp_tmap_buf);
1205
                merge_buffers(perm_tmap_buf, temp_tmap_buf);
1206
        }
1207
 
1208
        PHYSFSX_printf(my_file, "\n\nUsed textures in all (including registered) mines:\n");
1209
        say_used_tmaps(my_file, perm_tmap_buf);
1210
 
1211
        PHYSFSX_printf(my_file, "\nUnused textures in all (including registered) mines:\n");
1212
        say_unused_tmaps(my_file, perm_tmap_buf);
1213
}
1214
}
1215
 
1216
#endif