• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

polserver / polserver / 21047054485

15 Jan 2026 09:31PM UTC coverage: 60.507% (-0.001%) from 60.508%
21047054485

push

github

web-flow
Clang Tidy default constructor  (#852)

* trigger clang tidy

* Automated clang-tidy change: modernize-use-equals-default

* compile test

---------

Co-authored-by: Clang Tidy <clang-tidy@users.noreply.github.com>

58 of 75 new or added lines in 41 files covered. (77.33%)

1 existing line in 1 file now uncovered.

44460 of 73479 relevant lines covered (60.51%)

507363.1 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

70.3
/pol-core/uoconvert/UoConvertMain.cpp
1
#include "UoConvertMain.h"
2

3
#include <stdio.h>
4
#include <string.h>
5
#include <string>
6

7
#include "clib/Program/ProgramMain.h"
8
#include "clib/cfgelem.h"
9
#include "clib/cfgfile.h"
10
#include "clib/fileutil.h"
11
#include "clib/logfacility.h"
12
#include "clib/passert.h"
13
#include "clib/rawtypes.h"
14
#include "clib/stlutil.h"
15
#include "clib/timer.h"
16
#include "plib/clidata.h"
17
#include "plib/mapcell.h"
18
#include "plib/mapfunc.h"
19
#include "plib/mapshape.h"
20
#include "plib/mapsolid.h"
21
#include "plib/maptile.h"
22
#include "plib/mapwriter.h"
23
#include "plib/mul/map.h"
24
#include "plib/objtype.h"
25
#include "plib/polfile.h"
26
#include "plib/realmdescriptor.h"
27
#include "plib/systemstate.h"
28
#include "plib/udatfile.h"
29
#include "plib/uofile.h"
30
#include "plib/uofilei.h"
31
#include "plib/uoinstallfinder.h"
32
#include "plib/uopreader/uop.h"
33
#include "plib/uopreader/uophash.h"
34
#include "plib/ustruct.h"
35
#include "pol/landtile.h"
36

37

38
namespace Pol
39
{
40
namespace UoConvert
41
{
42
using namespace std;
43
using namespace Pol::Core;
44
using namespace Pol::Plib;
45

46
///////////////////////////////////////////////////////////////////////////////
47

48

49
///////////////////////////////////////////////////////////////////////////////
50

51
UoConvertMain::UoConvertMain()
11✔
52
    : Pol::Clib::ProgramMain(), cfg_use_no_shoot( false ), cfg_LOS_through_windows( false )
11✔
53
{
54
}
11✔
NEW
55
UoConvertMain::~UoConvertMain() = default;
×
56
///////////////////////////////////////////////////////////////////////////////
57

58
void UoConvertMain::showHelp()
×
59
{
60
  ERROR_PRINTLN( std::string{
×
61
      "Usage:\n"
62
      "    \n"
63
      "  UOCONVERT command [options ...]\n"
64
      "    \n"
65
      "  Commands: \n"
66
      "    map {uodata=Dir} {realm=realmname} {width=Width}        {height=Height} {mapid=0} "
67
      "{readuop=1} {x=X} {y=Y}\n"
68
      "    statics {uodata=Dir} {realm=realmname}\n"
69
      "    maptile {uodata=Dir} {realm=realmname}\n"
70
      "    multis {uodata=Dir} {outdir=dir}\n"
71
      "    tiles {uodata=Dir} {outdir=dir}\n"
72
      "    landtiles {uodata=Dir} {outdir=dir}" } );
73
}
×
74

75
using namespace Core;
76
using namespace Plib;
77

78
void UoConvertMain::display_flags()
×
79
{
80
  for ( unsigned blocking = 0; blocking <= 1; ++blocking )
×
81
  {
82
    for ( unsigned platform = 0; platform <= 1; ++platform )
×
83
    {
84
      for ( unsigned walk = 0; walk <= 1; ++walk )
×
85
      {
86
        for ( unsigned wall = 0; wall <= 1; ++wall )
×
87
        {
88
          for ( unsigned half = 0; half <= 1; ++half )
×
89
          {
90
            for ( unsigned floor = 0; floor <= 1; ++floor )
×
91
            {
92
              unsigned flags = 0;
×
93
              if ( blocking )
×
94
                flags |= USTRUCT_TILE::FLAG_BLOCKING;
×
95
              if ( platform )
×
96
                flags |= USTRUCT_TILE::FLAG_PLATFORM;
×
97
              if ( walk )
×
98
                flags |= USTRUCT_TILE::FLAG__WALK;
×
99
              if ( wall )
×
100
                flags |= USTRUCT_TILE::FLAG_WALL;
×
101
              if ( half )
×
102
                flags |= USTRUCT_TILE::FLAG_HALF_HEIGHT;
×
103
              if ( floor )
×
104
                flags |= USTRUCT_TILE::FLAG_FLOOR;
×
105

106
              unsigned int polflags = Plib::polflags_from_tileflags(
×
107
                  0x4000, flags, cfg_use_no_shoot, cfg_LOS_through_windows );
×
108
              unsigned moveland = ( polflags & Plib::FLAG::MOVELAND ) ? 1 : 0;
×
109
              INFO_PRINTLN( "{} {} {} {} {} {}: {}", blocking, platform, walk, wall, half, floor,
×
110
                            moveland );
111
            }
112
          }
113
        }
114
      }
115
    }
116
  }
117
}
×
118

119
void UoConvertMain::create_maptile( const std::string& realmname )
2✔
120
{
121
  Plib::RealmDescriptor descriptor = Plib::RealmDescriptor::Load( realmname );
2✔
122
  uo_map_height = static_cast<unsigned short>( descriptor.height );
2✔
123
  uo_map_width = static_cast<unsigned short>( descriptor.width );
2✔
124

125
  INFO_PRINTLN(
2✔
126
      "Creating maptile file.\n"
127
      "  Realm: {}\n"
128
      "  Map ID: {}\n"
129
      "  Use Dif files: {}\n"
130
      "  Size: {}x{}",
131
      realmname, descriptor.uomapid, ( descriptor.uodif ? "Yes" : "No" ), uo_map_width,
2✔
132
      uo_map_height );
133

134
  auto writer = new Plib::MapWriter();
2✔
135
  writer->OpenExistingFiles( realmname );
2✔
136

137
  for ( unsigned short y_base = 0; y_base < uo_map_height; y_base += Plib::MAPTILE_CHUNK )
30✔
138
  {
139
    for ( unsigned short x_base = 0; x_base < uo_map_width; x_base += Plib::MAPTILE_CHUNK )
662✔
140
    {
141
      for ( unsigned short x_add = 0; x_add < Plib::MAPTILE_CHUNK; ++x_add )
41,210✔
142
      {
143
        for ( unsigned short y_add = 0; y_add < Plib::MAPTILE_CHUNK; ++y_add )
2,637,440✔
144
        {
145
          unsigned short x = x_base + x_add;
2,596,864✔
146
          unsigned short y = y_base + y_add;
2,596,864✔
147

148
          short z;
149
          USTRUCT_MAPINFO mi;
150

151
          safe_getmapinfo( x, y, &z, &mi );
2,596,864✔
152

153
          if ( mi.landtile > 0x3FFF )
2,596,864✔
154
            INFO_PRINTLN( "Tile {:#x} at ({},{},{}) is an invalid ID!", mi.landtile, x, y, z );
×
155

156
          // for water, don't average with surrounding tiles.
157
          if ( Plib::landtile_uoflags_read( mi.landtile ) & Plib::USTRUCT_TILE::FLAG_LIQUID )
2,596,864✔
158
            z = mi.z;
194,432✔
159

160
          Plib::MAPTILE_CELL cell;
161
          cell.landtile = mi.landtile;
2,596,864✔
162
          cell.z = static_cast<signed char>( z );
2,596,864✔
163
          writer->SetMapTile( x, y, cell );
2,596,864✔
164
        }
165
      }
166
    }
167
    INFO_PRINT( "\rConverting: {}%", y_base * 100 / uo_map_height );
28✔
168
  }
169
  writer->Flush();
2✔
170
  delete writer;
2✔
171
  INFO_PRINTLN( "\rConversion complete." );
2✔
172
}
2✔
173

174
class StaticsByZ
175
{
176
public:
177
  bool operator()( const StaticRec& a, const StaticRec& b )
496✔
178
  {
179
    return ( a.z < b.z ) || ( ( a.z == b.z && a.height < b.height ) );
496✔
180
  }
181
};
182

183

184
bool flags_match( unsigned int f1, unsigned int f2, unsigned char bits_compare )
48✔
185
{
186
  return ( f1 & bits_compare ) == ( f2 & bits_compare );
48✔
187
}
188

189
/*
190
bool otherflags_match( unsigned char f1, unsigned char f2, unsigned char bits_exclude )
191
{
192
  return ( f1 & ~bits_exclude ) == ( f2 & ~bits_exclude );
193
}
194
bool differby_exactly( unsigned char f1, unsigned char f2, unsigned char bits )
195
{
196
  return ( ( f1 ^ f2 ) == bits );
197
}*/
198

199
void UoConvertMain::update_map( const std::string& realm, unsigned short x, unsigned short y )
×
200
{
201
  auto mapwriter = new MapWriter();
×
202
  mapwriter->OpenExistingFiles( realm );
×
203
  rawmapfullread();
×
204
  rawstaticfullread();
×
205
  unsigned short x_base = x / SOLIDX_X_SIZE * SOLIDX_X_SIZE;
×
206
  unsigned short y_base = y / SOLIDX_Y_SIZE * SOLIDX_Y_SIZE;
×
207

208
  ProcessSolidBlock( x_base, y_base, *mapwriter );
×
209
  delete mapwriter;
×
210
  INFO_PRINTLN( "empty={}, nonempty={}\nwith more_solids: {}\ntotal statics={}", empty, nonempty,
×
211
                with_more_solids, total_statics );
×
212
}
×
213

214
void UoConvertMain::create_map( const std::string& realm, unsigned short width,
4✔
215
                                unsigned short height )
216
{
217
  auto mapwriter = new MapWriter();
4✔
218
  INFO_PRINT(
4✔
219
      "Creating map base and solids files.\n"
220
      "  Realm: {}\n"
221
      "  Map ID: {}\n"
222
      "  Reading UOP file: {}\n"
223
      "  Use Dif files: {}\n"
224
      "  Size: {}x{}\n"
225
      "Initializing files: ",
226
      realm, uo_mapid, ( uo_readuop ? "Yes" : "No" ), ( uo_usedif ? "Yes" : "No" ), uo_map_width,
4✔
227
      uo_map_height );
228
  mapwriter->CreateNewFiles( realm, width, height );
4✔
229
  INFO_PRINTLN( "Done." );
4✔
230
  Tools::Timer<> timer;
4✔
231
  rawmapfullread();
4✔
232
  rawstaticfullread();
4✔
233
  INFO_PRINTLN( "  Reading mapfiles time: {} ms.", timer.ellapsed() );
4✔
234
  for ( unsigned short y_base = 0; y_base < height; y_base += SOLIDX_Y_SIZE )
1,252✔
235
  {
236
    for ( unsigned short x_base = 0; x_base < width; x_base += SOLIDX_X_SIZE )
131,936✔
237
    {
238
      ProcessSolidBlock( x_base, y_base, *mapwriter );
130,688✔
239
    }
240
    INFO_PRINT( "\rConverting: {}%", y_base * 100 / height );
1,248✔
241
  }
242
  timer.stop();
4✔
243

244
  mapwriter->WriteConfigFile();
4✔
245
  delete mapwriter;
4✔
246

247
  INFO_PRINTLN(
4✔
248
      "\rConversion complete.\n"
249
      "Conversion details:\n"
250
      "  Total blocks: {}\n"
251
      "  Blocks with solids: {} ({}%)\n"
252
      "  Blocks without solids: {} ({}%)\n"
253
      "  Locations with solids: {}\n"
254
      "  Total number of solids: {}\n"
255
      "  Elapsed time: {} ms.",
256
      empty + nonempty, nonempty, ( nonempty * 100 / ( empty + nonempty ) ), empty,
×
257
      ( empty * 100 / ( empty + nonempty ) ), with_more_solids, total_statics, timer.ellapsed() );
4✔
258
}
4✔
259

260
static bool is_no_draw( USTRUCT_MAPINFO& mi )
66,833,168✔
261
{
262
  return ( mi.landtile == 0x2 );
66,833,168✔
263
}
264

265
static bool is_cave_exit( USTRUCT_MAPINFO& mi )
66,833,168✔
266
{
267
  return ( mi.landtile == 0x7ec || mi.landtile == 0x7ed || mi.landtile == 0x7ee ||
66,833,168✔
268
           mi.landtile == 0x7ef || mi.landtile == 0x7f0 || mi.landtile == 0x7f1 ||
66,833,168✔
269
           mi.landtile == 0x834 || mi.landtile == 0x835 || mi.landtile == 0x836 ||
66,833,168✔
270
           mi.landtile == 0x837 || mi.landtile == 0x838 || mi.landtile == 0x839 ||
66,833,168✔
271
           mi.landtile == 0x1d3 || mi.landtile == 0x1d4 || mi.landtile == 0x1d5 ||
66,833,168✔
272
           mi.landtile == 0x1d6 || mi.landtile == 0x1d7 || mi.landtile == 0x1d8 ||
66,833,168✔
273
           mi.landtile == 0x1d9 || mi.landtile == 0x1da );
133,666,336✔
274
}
275

276
static bool is_cave_shadow( USTRUCT_MAPINFO& mi )
75,197,200✔
277
{
278
  return ( mi.landtile == 0x1db ||  // shadows above caves
150,394,400✔
279
           mi.landtile == 0x1ae ||  // more shadows above caves
75,197,200✔
280
           mi.landtile == 0x1af || mi.landtile == 0x1b0 || mi.landtile == 0x1b1 ||
75,197,200✔
281
           mi.landtile == 0x1b2 || mi.landtile == 0x1b3 || mi.landtile == 0x1b4 ||
225,591,600✔
282
           mi.landtile == 0x1b5 );
150,394,400✔
283
}
284

285
short get_lowestadjacentz( unsigned short x, unsigned short y, short z )
8,364,032✔
286
{
287
  USTRUCT_MAPINFO mi;
288
  short z0;
289
  short lowest_z = z;
8,364,032✔
290
  bool cave_override = false;
8,364,032✔
291

292
  if ( ( x - 1 >= 0 ) && ( y - 1 >= 0 ) )
8,364,032✔
293
  {
294
    safe_getmapinfo( x - 1, y - 1, &z0, &mi );
8,350,852✔
295

296
    if ( is_cave_shadow( mi ) || is_cave_exit( mi ) )
8,350,852✔
297
      z0 = z;
×
298

299
    if ( is_no_draw( mi ) )
8,350,852✔
300
      cave_override = true;
×
301

302
    if ( z0 < lowest_z )
8,350,852✔
303
    {
304
      lowest_z = z0;
25,432✔
305
    }
306
  }
307

308
  if ( x - 1 >= 0 )
8,364,032✔
309
  {
310
    safe_getmapinfo( x - 1, y, &z0, &mi );
8,354,048✔
311

312
    if ( is_cave_shadow( mi ) || is_cave_exit( mi ) )
8,354,048✔
313
      z0 = z;
×
314

315
    if ( is_no_draw( mi ) )
8,354,048✔
316
      cave_override = true;
×
317

318
    if ( z0 < lowest_z )
8,354,048✔
319
    {
320
      lowest_z = z0;
8✔
321
    }
322
  }
323

324
  if ( ( x - 1 >= 0 ) && ( y + 1 < uo_map_height ) )
8,364,032✔
325
  {
326
    safe_getmapinfo( x - 1, y + 1, &z0, &mi );
8,350,852✔
327

328
    if ( is_cave_shadow( mi ) || is_cave_exit( mi ) )
8,350,852✔
329
      z0 = z;
×
330

331
    if ( is_no_draw( mi ) )
8,350,852✔
332
      cave_override = true;
×
333

334
    if ( z0 < lowest_z )
8,350,852✔
335
    {
336
      lowest_z = z0;
8,904✔
337
    }
338
  }
339

340
  if ( y - 1 >= 0 )
8,364,032✔
341
  {
342
    safe_getmapinfo( x, y - 1, &z0, &mi );
8,360,832✔
343

344
    if ( is_cave_shadow( mi ) || is_cave_exit( mi ) )
8,360,832✔
345
      z0 = z;
×
346

347
    if ( is_no_draw( mi ) )
8,360,832✔
348
      cave_override = true;
×
349

350
    if ( z0 < lowest_z )
8,360,832✔
351
    {
352
      lowest_z = z0;
8✔
353
    }
354
  }
355

356
  if ( ( y - 1 >= 0 ) && ( x + 1 < uo_map_width ) )
8,364,032✔
357
  {
358
    safe_getmapinfo( x + 1, y - 1, &z0, &mi );
8,350,852✔
359

360
    if ( is_cave_shadow( mi ) || is_cave_exit( mi ) )
8,350,852✔
361
      z0 = z;
×
362

363
    if ( is_no_draw( mi ) )
8,350,852✔
364
      cave_override = true;
×
365

366
    if ( z0 < lowest_z )
8,350,852✔
367
    {
368
      lowest_z = z0;
29,248✔
369
    }
370
  }
371

372
  if ( x + 1 < uo_map_width )
8,364,032✔
373
  {
374
    safe_getmapinfo( x + 1, y, &z0, &mi );
8,354,048✔
375

376
    if ( is_cave_shadow( mi ) || is_cave_exit( mi ) )
8,354,048✔
377
      z0 = z;
×
378

379
    if ( is_no_draw( mi ) )
8,354,048✔
380
      cave_override = true;
×
381

382
    if ( z0 < lowest_z )
8,354,048✔
383
    {
384
      lowest_z = z0;
×
385
    }
386
  }
387

388
  if ( ( x + 1 < uo_map_width ) && ( y + 1 < uo_map_height ) )
8,364,032✔
389
  {
390
    safe_getmapinfo( x + 1, y + 1, &z0, &mi );
8,350,852✔
391

392
    if ( is_cave_shadow( mi ) || is_cave_exit( mi ) )
8,350,852✔
393
      z0 = z;
×
394

395
    if ( is_no_draw( mi ) )
8,350,852✔
396
      cave_override = true;
×
397

398
    if ( z0 < lowest_z )
8,350,852✔
399
    {
400
      lowest_z = z0;
8✔
401
    }
402
  }
403

404
  if ( y + 1 < uo_map_height )
8,364,032✔
405
  {
406
    safe_getmapinfo( x, y + 1, &z0, &mi );
8,360,832✔
407

408
    if ( is_cave_shadow( mi ) || is_cave_exit( mi ) )
8,360,832✔
409
      z0 = z;
×
410

411
    if ( is_no_draw( mi ) )
8,360,832✔
412
      cave_override = true;
×
413

414
    if ( z0 < lowest_z )
8,360,832✔
415
    {
416
      lowest_z = z0;
×
417
    }
418
  }
419

420
  if ( cave_override )
8,364,032✔
421
    return z;
×
422
  else
423
    return lowest_z;
8,364,032✔
424
}
425

426
void UoConvertMain::ProcessSolidBlock( unsigned short x_base, unsigned short y_base,
130,688✔
427
                                       MapWriter& mapwriter )
428
{
429
  unsigned int idx2_offset = 0;
130,688✔
430
  SOLIDX2_ELEM idx2_elem;
431
  memset( &idx2_elem, 0, sizeof idx2_elem );
130,688✔
432
  idx2_elem.baseindex = mapwriter.NextSolidIndex();
130,688✔
433

434
  unsigned short x_add_max = SOLIDX_X_SIZE, y_add_max = SOLIDX_Y_SIZE;
130,688✔
435
  if ( x_base + x_add_max > uo_map_width )
130,688✔
436
    x_add_max = uo_map_width - x_base;
×
437
  if ( y_base + y_add_max > uo_map_height )
130,688✔
438
    y_add_max = uo_map_height - y_base;
×
439

440
  for ( unsigned short x_add = 0; x_add < x_add_max; ++x_add )
1,176,192✔
441
  {
442
    for ( unsigned short y_add = 0; y_add < y_add_max; ++y_add )
9,409,536✔
443
    {
444
      unsigned short x = x_base + x_add;
8,364,032✔
445
      unsigned short y = y_base + y_add;
8,364,032✔
446

447
      StaticList statics;
8,364,032✔
448

449
      // read the map, and treat it like a static.
450
      short z;
451
      USTRUCT_MAPINFO mi;
452

453
      safe_getmapinfo( x, y, &z, &mi );
8,364,032✔
454

455
      if ( mi.landtile > 0x3FFF )
8,364,032✔
456
        INFO_PRINTLN( "Tile {:#x} at ({},{},{}) is an invalid ID!", mi.landtile, x, y, z );
×
457

458
      // for water, don't average with surrounding tiles.
459
      if ( Plib::landtile_uoflags_read( mi.landtile ) & USTRUCT_TILE::FLAG_LIQUID )
8,364,032✔
460
        z = mi.z;
725,760✔
461
      short low_z = get_lowestadjacentz( x, y, z );
8,364,032✔
462

463
      short lt_height = z - low_z;
8,364,032✔
464
      z = low_z;
8,364,032✔
465

466
      if ( mi.landtile > 0x3FFF )
8,364,032✔
467
        INFO_PRINTLN( "Tile {:#x} at ({},{},{}) is an invalid ID!", mi.landtile, x, y, z );
×
468

469
      unsigned int lt_flags = Plib::landtile_uoflags_read( mi.landtile );
8,364,032✔
470
      if ( ~lt_flags & USTRUCT_TILE::FLAG_BLOCKING )
8,364,032✔
471
      {  // this seems to be the default.
472
        lt_flags |= USTRUCT_TILE::FLAG_PLATFORM;
7,638,272✔
473
      }
474
      lt_flags |=
8,364,032✔
475
          USTRUCT_TILE::FLAG_NO_SHOOT;  // added to make sure people using noshoot will have shapes
476
      // generated by this tile in future block LOS, shouldn't
477
      // affect people using old LOS method one way or another.
478
      lt_flags |= USTRUCT_TILE::FLAG_FLOOR;
8,364,032✔
479
      lt_flags |= USTRUCT_TILE::FLAG_HALF_HEIGHT;  // the entire map is this way
8,364,032✔
480

481
      if ( lt_flags & USTRUCT_TILE::FLAG_WALL )
8,364,032✔
482
        lt_height = 20;
×
483

484
      readstatics( statics, x, y,
8,364,032✔
485
                   USTRUCT_TILE::FLAG_BLOCKING | USTRUCT_TILE::FLAG_PLATFORM |
486
                       USTRUCT_TILE::FLAG_HALF_HEIGHT | USTRUCT_TILE::FLAG_LIQUID |
487
                       USTRUCT_TILE::FLAG_HOVEROVER
488
                   // USTRUCT_TILE::FLAG__WALK
489
      );
490

491
      for ( unsigned i = 0; i < statics.size(); ++i )
8,364,384✔
492
      {
493
        StaticRec srec = statics[i];
352✔
494

495
        unsigned int polflags = polflags_from_tileflags( srec.graphic, srec.flags, cfg_use_no_shoot,
704✔
496
                                                         cfg_LOS_through_windows );
352✔
497

498
        if ( ( ~polflags & FLAG::MOVELAND ) && ( ~polflags & FLAG::MOVESEA ) &&
352✔
499
             ( ~polflags & FLAG::BLOCKSIGHT ) && ( ~polflags & FLAG::BLOCKING ) &&
196✔
500
             ( ~polflags & FLAG::OVERFLIGHT ) )
×
501
        {
502
          // remove it.  we'll re-sort later.
503
          statics.erase( statics.begin() + i );
×
504
          --i;  // do-over
×
505
        }
506
        if ( ( ~srec.flags & USTRUCT_TILE::FLAG_BLOCKING ) &&
352✔
507
             ( ~srec.flags & USTRUCT_TILE::FLAG_PLATFORM ) &&
156✔
508
             ( ~srec.flags & USTRUCT_TILE::FLAG_HALF_HEIGHT ) &&
×
509
             ( ~srec.flags & USTRUCT_TILE::FLAG_LIQUID ) &&
×
510
             ( ~srec.flags & USTRUCT_TILE::FLAG_HOVEROVER ) )
×
511
        /*(~srec.flags & USTRUCT_TILE::FLAG__WALK)*/
512
        {
513
          // remove it.  we'll re-sort later.
514
          statics.erase( statics.begin() + i );
×
515
          --i;  // do-over
×
516
        }
517
      }
518

519
      bool addMap = true;
8,364,032✔
520

521
      for ( const auto& srec : statics )
8,364,384✔
522
      {
523
        // Look for water tiles. If there are any, discard the map (which is usually at -15 anyway)
524
        if ( z + lt_height <= srec.z &&
1,056✔
525
             // only where the map is below or same Z as the static
526
             ( ( srec.z - ( z + lt_height ) ) <= 10 ) && DiscardedWaterTypes.count( srec.graphic ) )
352✔
527
        {
528
          // arr, there be water here
529
          addMap = false;
×
530
        }
531

532
        // if there's a static on top of one of these "wall" landtiles, make it override.
533
        if ( ( lt_flags & USTRUCT_TILE::FLAG_WALL ) &&  // wall?
352✔
534
             z <= srec.z && srec.z - z <= lt_height )
×
535
        {
536
          lt_height = srec.z - z;
×
537
        }
538
      }
539
      // shadows above caves
540
      if ( is_cave_shadow( mi ) && !statics.empty() )
8,364,032✔
541
      {
542
        addMap = false;
×
543
      }
544

545

546
      // If the map is a NODRAW tile, and there are statics, discard the map tile
547
      if ( mi.landtile == 2 && !statics.empty() )
8,364,032✔
548
        addMap = false;
×
549

550
      if ( addMap )
8,364,032✔
551
        statics.push_back( StaticRec( 0, static_cast<signed char>( z ), lt_flags,
8,364,032✔
552
                                      static_cast<char>( lt_height ) ) );
553

554
      sort( statics.begin(), statics.end(), StaticsByZ() );
8,364,032✔
555
      reverse( statics.begin(), statics.end() );
8,364,032✔
556

557
      std::vector<MapShape> shapes;
8,364,032✔
558

559
      // try to consolidate like shapes, and discard ones we don't care about.
560
      while ( !statics.empty() )
16,728,416✔
561
      {
562
        StaticRec srec = statics.back();
8,364,384✔
563
        statics.pop_back();
8,364,384✔
564

565
        unsigned int polflags = polflags_from_tileflags( srec.graphic, srec.flags, cfg_use_no_shoot,
16,728,768✔
566
                                                         cfg_LOS_through_windows );
8,364,384✔
567
        if ( ( ~polflags & FLAG::MOVELAND ) && ( ~polflags & FLAG::MOVESEA ) &&
8,364,384✔
568
             ( ~polflags & FLAG::BLOCKSIGHT ) && ( ~polflags & FLAG::BLOCKING ) &&
196✔
569
             ( ~polflags & FLAG::OVERFLIGHT ) )
×
570
        {
571
          passert_always( 0 );
×
572
          continue;
8,364,376✔
573
        }
574
        if ( ( ~srec.flags & USTRUCT_TILE::FLAG_BLOCKING ) &&
8,364,384✔
575
             ( ~srec.flags & USTRUCT_TILE::FLAG_PLATFORM ) &&
7,638,428✔
576
             ( ~srec.flags & USTRUCT_TILE::FLAG_HALF_HEIGHT ) &&
×
577
             ( ~srec.flags & USTRUCT_TILE::FLAG_LIQUID ) &&
×
578
             ( ~srec.flags & USTRUCT_TILE::FLAG_HOVEROVER ) )
×
579
        /*(~srec.flags & USTRUCT_TILE::FLAG__WALK)*/
580
        {
581
          passert_always( 0 );
×
582
          continue;
583
        }
584

585
        if ( shapes.empty() )
8,364,384✔
586
        {
587
          // this, whatever it is, is the map base.
588
          // TODO: look for water statics and use THOSE as the map.
589
          MapShape shape;
590
          shape.z = srec.z;  // these will be converted below to
8,364,032✔
591
          shape.height = 0;  // make the map "solid"
8,364,032✔
592
          shape.flags = static_cast<unsigned char>( polflags );
8,364,032✔
593
          // no matter what, the lowest level is gradual
594
          shape.flags |= FLAG::GRADUAL;
8,364,032✔
595
          shapes.push_back( shape );
8,364,032✔
596

597
          // for wall flag - map tile always height 0, at bottom. if map tile has height, add it as
598
          // a static
599
          if ( srec.height != 0 )
8,364,032✔
600
          {
601
            MapShape _shape;
602
            _shape.z = srec.z;
63,576✔
603
            _shape.height = srec.height;
63,576✔
604
            _shape.flags = polflags;
63,576✔
605
            shapes.push_back( _shape );
63,576✔
606
          }
607
          continue;
8,364,032✔
608
        }
8,364,032✔
609

610
        MapShape& prev = shapes.back();
352✔
611
        // we're adding it.
612
        MapShape shape;
613
        shape.z = srec.z;
352✔
614
        shape.height = srec.height;
352✔
615
        shape.flags = polflags;
352✔
616

617
        // always add the map shape seperately
618
        if ( shapes.size() == 1 )
352✔
619
        {
620
          shapes.push_back( shape );
208✔
621
          continue;
208✔
622
        }
623

624
        if ( shape.z < prev.z + prev.height )
144✔
625
        {
626
          // things can't exist in the same place.
627
          // shrink the bottom part of this shape.
628
          // if that would give it negative height, then skip it.
629
          short height_remove = prev.z + prev.height - shape.z;
×
630
          if ( height_remove <= shape.height )
×
631
          {
632
            shape.z += height_remove;
×
633
            shape.height -= height_remove;
×
634
          }
635
          else
636
          {  // example: 5530, 14
637
            continue;
×
638
          }
639
        }
640

641
        // sometimes water has "sand" a couple z-coords above it.
642
        // We'll try to detect this (really, anything that is up to 4 dist from water)
643
        // and extend the thing above downward.
644
        if ( ( prev.flags & FLAG::MOVESEA ) && ( shape.z > prev.z + prev.height ) &&
144✔
645
             ( shape.z <= prev.z + prev.height + 4 ) )
×
646
        {
647
          short height_add = shape.z - prev.z - prev.height;
×
648
          shape.z -= height_add;
×
649
          shape.height += height_add;
×
650
        }
651
        if ( ( prev.flags & FLAG::MOVESEA ) && ( prev.z + prev.height == -5 ) &&
144✔
652
             ( shape.flags & FLAG::MOVESEA ) && ( shape.z == 25 ) )
×
653
        {
654
          // oddly, there are some water tiles at z=25 in some places...I don't get it
655
          continue;
×
656
        }
657

658
        // string prevflags_s = flagstr(prev.flags);
659
        // const char* prevflags = prevflags_s.c_str();
660
        // string shapeflags_s = flagstr(shape.flags);
661
        // const char* shapeflags = shapeflags_s.c_str();
662

663
        if ( shape.z > prev.z + prev.height )
144✔
664
        {
665
          //
666
          // elevated above what's below, must include separately
667
          //
668

669
          shapes.push_back( shape );
96✔
670
          continue;
96✔
671
        }
672

673
        passert_always( shape.z == prev.z + prev.height );
48✔
674

675
        if ( shape.z == prev.z + prev.height )
48✔
676
        {
677
          //
678
          // sitting right on top of the previous solid
679
          //
680

681
          // standable atop non-standable: standable
682
          // nonstandable atop standable: nonstandable
683
          // etc
684
          bool can_combine =
685
              flags_match( prev.flags, shape.flags, FLAG::BLOCKSIGHT | FLAG::BLOCKING );
48✔
686
          if ( prev.flags & FLAG::MOVELAND && ~shape.flags & FLAG::BLOCKING &&
48✔
687
               ~shape.flags & FLAG::MOVELAND )
×
688
          {
689
            can_combine = false;
×
690
          }
691

692
          if ( can_combine )
48✔
693
          {
694
            prev.flags = shape.flags;
8✔
695
            prev.height += shape.height;
8✔
696
          }
697
          else  // if one blocks LOS, but not the other, they can't be combined this way.
698
          {
699
            shapes.push_back( shape );
40✔
700
            continue;
40✔
701
          }
702
        }
703
      }
704

705
      // the first StaticShape is the map base.
706
      MapShape base = shapes[0];
8,364,032✔
707
      shapes.erase( shapes.begin() );
8,364,032✔
708
      MAPCELL cell;
709
      passert_always( base.height == 0 );
8,364,032✔
710
      cell.z = static_cast<signed char>(
8,364,032✔
711
          base.z );  // assume now map has height=1. a static was already added if it was >0
8,364,032✔
712
      cell.flags = static_cast<u8>( base.flags );
8,364,032✔
713
      if ( !shapes.empty() )
8,364,032✔
714
        cell.flags |= FLAG::MORE_SOLIDS;
63,784✔
715

716
      mapwriter.SetMapCell( x, y, cell );
8,364,032✔
717

718
      if ( !shapes.empty() )
8,364,032✔
719
      {
720
        ++with_more_solids;
63,784✔
721
        total_statics += static_cast<unsigned int>( shapes.size() );
63,784✔
722
        if ( idx2_offset == 0 )
63,784✔
723
          idx2_offset = mapwriter.NextSolidx2Offset();
3,200✔
724

725
        unsigned int addindex = mapwriter.NextSolidIndex() - idx2_elem.baseindex;
63,784✔
726
        if ( addindex > std::numeric_limits<unsigned short>::max() )
63,784✔
727
          throw std::runtime_error( "addoffset overflow" );
×
728
        idx2_elem.addindex[x_add][y_add] = static_cast<unsigned short>( addindex );
63,784✔
729
        int count = static_cast<int>( shapes.size() );
63,784✔
730
        for ( int j = 0; j < count; ++j )
127,704✔
731
        {
732
          MapShape shape = shapes[j];
63,920✔
733
          char _z, height, flags;
734
          _z = static_cast<char>( shapes[j].z );
63,920✔
735
          height = static_cast<char>( shape.height );
63,920✔
736
          flags = static_cast<u8>( shape.flags );
63,920✔
737
          if ( !height )  // make 0 height solid
63,920✔
738
          {
739
            --_z;
144✔
740
            ++height;
144✔
741
          }
742

743
          if ( j != count - 1 )
63,920✔
744
            flags |= FLAG::MORE_SOLIDS;
136✔
745
          SOLIDS_ELEM solid;
746
          solid.z = _z;
63,920✔
747
          solid.height = height;
63,920✔
748
          solid.flags = flags;
63,920✔
749
          mapwriter.AppendSolid( solid );
63,920✔
750
        }
751
      }
752
    }
8,364,032✔
753
  }
754
  if ( idx2_offset )
130,688✔
755
  {
756
    ++nonempty;
3,200✔
757
    mapwriter.AppendSolidx2Elem( idx2_elem );
3,200✔
758
  }
759
  else
760
  {
761
    ++empty;
127,488✔
762
  }
763
  mapwriter.SetSolidx2Offset( x_base, y_base, idx2_offset );
130,688✔
764
}
130,688✔
765

766
std::string UoConvertMain::resolve_type_from_id( unsigned id ) const
20✔
767
{
768
  if ( BoatTypes.count( id ) )
20✔
769
    return "Boat";
12✔
770
  else
771
    return "Multi";
8✔
772
}
773

774
void UoConvertMain::write_multi_element( FILE* multis_cfg, const USTRUCT_MULTI_ELEMENT& elem,
820✔
775
                                         const std::string& mytype, bool& first )
776
{
777
  if ( elem.graphic == GRAPHIC_NODRAW )
820✔
778
    return;
×
779

780
  std::string type = elem.flags ? "static" : "dynamic";
820✔
781

782
  if ( mytype == "Boat" && first && elem.graphic != 1 )
820✔
783
    type = "static";
12✔
784

785
  std::string comment;
820✔
786
  if ( cfg_use_new_hsa_format )
820✔
787
  {
788
    USTRUCT_TILE_HSA tile;
789
    readtile( elem.graphic, &tile );
×
790
    comment.assign( tile.name, sizeof( tile.name ) );
×
791
  }
792
  else
793
  {
794
    USTRUCT_TILE tile;
795
    readtile( elem.graphic, &tile );
820✔
796
    comment.assign( tile.name, sizeof( tile.name ) );
820✔
797
  }
798

799
  fprintf( multis_cfg, "    %-7s 0x%04x %4d %4d %4d   // %s\n", type.c_str(), elem.graphic, elem.x,
1,640✔
800
           elem.y, elem.z, comment.c_str() );
820✔
801

802
  first = false;
820✔
803
}
820✔
804

805
void UoConvertMain::write_multi( FILE* multis_cfg, unsigned id,
×
806
                                 std::vector<Plib::USTRUCT_MULTI_ELEMENT>& multi_elems )
807
{
808
  std::string mytype = resolve_type_from_id( id );
×
809

810
  fprintf( multis_cfg, "%s 0x%x\n", mytype.c_str(), id );
×
811
  fprintf( multis_cfg, "{\n" );
×
812

813
  bool first = true;
×
814
  for ( const auto& elem : multi_elems )
×
815
  {
816
    write_multi_element( multis_cfg, elem, mytype, first );
×
817
  }
818

819
  fprintf( multis_cfg, "}\n\n" );
×
820
}
×
821

822
void UoConvertMain::write_multi( FILE* multis_cfg, unsigned id, FILE* multi_mul,
20✔
823
                                 unsigned int offset, unsigned int length )
824
{
825
  USTRUCT_MULTI_ELEMENT elem;
826
  unsigned int count =
20✔
827
      cfg_use_new_hsa_format ? length / sizeof( USTRUCT_MULTI_ELEMENT_HSA ) : length / sizeof elem;
828

829
  std::string mytype = resolve_type_from_id( id );
20✔
830

831
  fprintf( multis_cfg, "%s 0x%x\n", mytype.c_str(), id );
20✔
832
  fprintf( multis_cfg, "{\n" );
20✔
833

834
  if ( fseek( multi_mul, offset, SEEK_SET ) != 0 )
20✔
835
  {
836
    throw std::runtime_error( "write_multi(): fseek() failed" );
×
837
  }
838

839

840
  bool first = true;
20✔
841
  while ( count-- )
840✔
842
  {
843
    if ( fread( &elem, sizeof elem, 1, multi_mul ) != 1 )
820✔
844
    {
845
      throw std::runtime_error( "write_multi(): fread() failed" );
×
846
    }
847

848
    if ( cfg_use_new_hsa_format )
820✔
849
    {
850
      if ( fseek( multi_mul, 4, SEEK_CUR ) != 0 )
×
851
        throw std::runtime_error( "write_multi(): fseek() failed" );
×
852
    }
853

854
    write_multi_element( multis_cfg, elem, mytype, first );
820✔
855
  }
856

857
  fprintf( multis_cfg, "}\n\n" );
20✔
858
}
20✔
859

860
void UoConvertMain::create_multis_cfg( FILE* multi_idx, FILE* multi_mul, FILE* multis_cfg )
1✔
861
{
862
  if ( fseek( multi_idx, 0, SEEK_SET ) != 0 )
1✔
863
    throw std::runtime_error( "create_multis_cfg: fseek failed" );
×
864
  unsigned count = 0;
1✔
865
  USTRUCT_IDX idxrec;
866
  for ( int i = 0; fread( &idxrec, sizeof idxrec, 1, multi_idx ) == 1; ++i )
16,384✔
867
  {
868
    const USTRUCT_VERSION* vrec = nullptr;
16,383✔
869

870
    if ( check_verdata( VERFILE_MULTI_MUL, i, vrec ) )
16,383✔
871
    {
872
      write_multi( multis_cfg, i, verfile, vrec->filepos, vrec->length );
×
873
      ++count;
×
874
    }
875
    else
876
    {
877
      if ( idxrec.offset == 0xFFffFFffLu )
16,383✔
878
        continue;
16,363✔
879

880
      write_multi( multis_cfg, i, multi_mul, idxrec.offset, idxrec.length );
20✔
881
      ++count;
20✔
882
    }
883
  }
884
  INFO_PRINTLN( "{} multi definitions written to multis.cfg", count );
1✔
885
}
1✔
886

887
void UoConvertMain::create_multis_cfg()
1✔
888
{
889
  std::map<unsigned int, std::vector<USTRUCT_MULTI_ELEMENT>> multi_map;
1✔
890

891
  std::string outdir = programArgsFindEquals( "outdir=", "." );
1✔
892
  FILE* multis_cfg = fopen( ( outdir + "/multis.cfg" ).c_str(), "wt" );
1✔
893

894
  if ( open_uopmulti_file( multi_map ) )
1✔
895
  {
896
    for ( auto& [id, elems] : multi_map )
×
897
    {
898
      write_multi( multis_cfg, id, elems );
×
899
    }
900

901
    INFO_PRINTLN( "{} multi definitions written to multis.cfg", multi_map.size() );
×
902

903
    return;
×
904
  }
905

906
  FILE* multi_idx = open_uo_file( "multi.idx" );
1✔
907
  FILE* multi_mul = open_uo_file( "multi.mul" );
1✔
908

909
  create_multis_cfg( multi_idx, multi_mul, multis_cfg );
1✔
910

911
  fclose( multis_cfg );
1✔
912

913
  fclose( multi_idx );
1✔
914
  fclose( multi_mul );
1✔
915
}
1✔
916
void UoConvertMain::write_flags( FILE* fp, unsigned int flags )
117✔
917
{
918
  if ( flags & FLAG::MOVELAND )
117✔
919
    fprintf( fp, "    MoveLand 1\n" );
27✔
920
  if ( flags & FLAG::MOVESEA )
117✔
921
    fprintf( fp, "    MoveSea 1\n" );
1✔
922
  if ( flags & FLAG::BLOCKSIGHT )
117✔
923
    fprintf( fp, "    BlockSight 1\n" );
92✔
924
  if ( ~flags & FLAG::OVERFLIGHT )
117✔
925
    fprintf( fp, "    OverFlight 0\n" );
117✔
926
  if ( flags & FLAG::ALLOWDROPON )
117✔
927
    fprintf( fp, "    AllowDropOn 1\n" );
27✔
928
  if ( flags & FLAG::GRADUAL )
117✔
929
    fprintf( fp, "    Gradual 1\n" );
4✔
930
  if ( flags & FLAG::STACKABLE )
117✔
931
    fprintf( fp, "    Stackable 1\n" );
2✔
932
  if ( flags & FLAG::BLOCKING )
117✔
933
    fprintf( fp, "    Blocking 1\n" );
67✔
934
  if ( flags & FLAG::MOVABLE )
117✔
935
    fprintf( fp, "    Movable 1\n" );
48✔
936
  if ( flags & FLAG::EQUIPPABLE )
117✔
937
    fprintf( fp, "    Equippable 1\n" );
4✔
938
  if ( flags & FLAG::DESC_PREPEND_A )
117✔
939
    fprintf( fp, "    DescPrependA 1\n" );
2✔
940
  if ( flags & FLAG::DESC_PREPEND_AN )
117✔
941
    fprintf( fp, "    DescPrependAn 1\n" );
×
942
}
117✔
943

944
void UoConvertMain::create_tiles_cfg()
1✔
945
{
946
  std::string outdir = programArgsFindEquals( "outdir=", "." );
1✔
947
  FILE* fp = fopen( ( outdir + "/tiles.cfg" ).c_str(), "wt" );
1✔
948
  int mountCount;
949
  char name[21];
950

951
  unsigned count = 0;
1✔
952
  for ( unsigned int graphic_i = 0; graphic_i <= Plib::systemstate.config.max_tile_id; ++graphic_i )
16,385✔
953
  {
954
    u16 graphic = static_cast<u16>( graphic_i );
16,384✔
955
    USTRUCT_TILE tile;
956
    if ( cfg_use_new_hsa_format )
16,384✔
957
    {
958
      USTRUCT_TILE_HSA newtile;
959
      read_objinfo( graphic, newtile );
×
960
      tile.anim = newtile.anim;
×
961
      tile.flags = newtile.flags;
×
962
      tile.height = newtile.height;
×
963
      tile.layer = newtile.layer;
×
964
      memcpy( tile.name, newtile.name, sizeof tile.name );
×
965
      tile.unk14 = newtile.unk14;
×
966
      tile.unk15 = newtile.unk15;
×
967
      tile.unk6 = newtile.unk6;
×
968
      tile.unk7 = newtile.unk7;
×
969
      tile.unk8 = newtile.unk8;
×
970
      tile.unk9 = newtile.unk9;
×
971
      tile.weight = newtile.weight;
×
972
    }
973
    else
974
      read_objinfo( graphic, tile );
16,384✔
975
    mountCount = static_cast<int>( MountTypes.count( graphic ) );
16,384✔
976

977
    if ( tile.name[0] == '\0' && tile.flags == 0 && tile.layer == 0 && tile.height == 0 &&
16,384✔
978
         mountCount == 0 )
979
    {
980
      continue;
16,269✔
981
    }
982
    unsigned int flags =
983
        polflags_from_tileflags( graphic, tile.flags, cfg_use_no_shoot, cfg_LOS_through_windows );
115✔
984
    if ( mountCount != 0 )
115✔
985
    {
986
      tile.layer = 25;
×
987
      flags |= FLAG::EQUIPPABLE;
×
988
    }
989

990
    memset( name, 0, sizeof name );
115✔
991
    memcpy( name, tile.name, sizeof tile.name );
115✔
992

993
    fprintf( fp, "tile 0x%x\n", graphic );
115✔
994
    fprintf( fp, "{\n" );
115✔
995
    fprintf( fp, "    Desc %s\n", name );
115✔
996
    fprintf( fp, "    UoFlags 0x%08lx\n", static_cast<unsigned long>( tile.flags ) );
115✔
997
    if ( tile.layer )
115✔
998
      fprintf( fp, "    Layer %u\n", tile.layer );
31✔
999
    if ( flags & FLAG::EQUIPPABLE )
115✔
1000
      fprintf( fp, "    AnimID %u\n", tile.anim );
4✔
1001
    if ( static_cast<unsigned long>( tile.flags ) & USTRUCT_TILE::FLAG_PARTIAL_HUE )
115✔
1002
      fprintf( fp, "    PartialHue 1\n" );
×
1003
    fprintf( fp, "    Height %u\n", tile.height );
115✔
1004
    fprintf( fp, "    Weight %u\n", tile.weight );
115✔
1005
    write_flags( fp, flags );
115✔
1006
    fprintf( fp, "}\n" );
115✔
1007
    fprintf( fp, "\n" );
115✔
1008
    ++count;
115✔
1009
  }
1010
  fclose( fp );
1✔
1011

1012
  INFO_PRINTLN( "{} tile definitions written to tiles.cfg", count );
1✔
1013
}
1✔
1014

1015
void UoConvertMain::create_landtiles_cfg()
1✔
1016
{
1017
  std::string outdir = programArgsFindEquals( "outdir=", "." );
1✔
1018
  FILE* fp = fopen( ( outdir + "/landtiles.cfg" ).c_str(), "wt" );
1✔
1019
  unsigned count = 0;
1✔
1020

1021
  for ( u16 i = 0; i <= 0x3FFF; ++i )
16,385✔
1022
  {
1023
    USTRUCT_LAND_TILE landtile;
1024
    if ( cfg_use_new_hsa_format )
16,384✔
1025
    {
1026
      USTRUCT_LAND_TILE_HSA newlandtile;
1027
      readlandtile( i, &newlandtile );
×
1028
      landtile.flags = newlandtile.flags;
×
1029
      landtile.unk = newlandtile.unk;
×
1030
      memcpy( landtile.name, newlandtile.name, sizeof landtile.name );
×
1031
    }
1032
    else
1033
      readlandtile( i, &landtile );
16,384✔
1034

1035
    if ( landtile.name[0] || landtile.flags )
16,384✔
1036
    {
1037
      fprintf( fp, "landtile 0x%x\n", i );
2✔
1038
      fprintf( fp, "{\n" );
2✔
1039
      fprintf( fp, "    Name %s\n", landtile.name );
2✔
1040
      fprintf( fp, "    UoFlags 0x%08lx\n", static_cast<unsigned long>( landtile.flags ) );
2✔
1041

1042
      unsigned int flags = polflags_from_landtileflags( i, landtile.flags );
2✔
1043
      flags &= ~FLAG::MOVABLE;  // movable makes no sense for landtiles
2✔
1044
      write_flags( fp, flags );
2✔
1045
      fprintf( fp, "}\n" );
2✔
1046
      fprintf( fp, "\n" );
2✔
1047
      ++count;
2✔
1048
    }
1049
  }
1050
  fclose( fp );
1✔
1051

1052
  INFO_PRINTLN( "{} landtile definitions written to landtiles.cfg", count );
1✔
1053
}
1✔
1054

1055
int UoConvertMain::main()
11✔
1056
{
1057
  const std::vector<std::string>& binArgs = programArgs();
11✔
1058

1059
  /**********************************************
1060
   * show help
1061
   **********************************************/
1062
  if ( binArgs.size() == 1 )
11✔
1063
  {
1064
    showHelp();
×
1065
    return 0;  // return "okay"
×
1066
  }
1067

1068
  // Setups uoconvert by finding the path of uo files and max tiles from pol.cfg or command
1069
  // line arguments. Also loads parameters from uoconvert.cfg.
1070
  setup_uoconvert();
11✔
1071

1072
  std::string command = binArgs[1];
11✔
1073
  if ( command == "uoptomul" )
11✔
1074
  {
1075
    if ( !convert_uop_to_mul() )
×
1076
      return 1;
×
1077
  }
1078
  else if ( command == "map" )
11✔
1079
  {
1080
    UoConvert::uo_mapid = programArgsFindEquals( "mapid=", 0, false );
4✔
1081
    UoConvert::uo_usedif = programArgsFindEquals( "usedif=", 1, false );
4✔
1082
    UoConvert::uo_readuop = (bool)programArgsFindEquals( "readuop=", 1, false );
4✔
1083

1084
    std::string realm = programArgsFindEquals( "realm=", "britannia" );
4✔
1085

1086
    UoConvert::open_uo_data_files();
4✔
1087
    UoConvert::read_uo_data();
4✔
1088

1089
    // Auto-detects defaults for mapid=0 or 1 based on the map size. All other sizes are fixed based
1090
    // on the mapid.
1091
    Pol::Plib::MUL::MapInfo mapinfo( UoConvert::uo_mapid, UoConvert::uo_map_size );
4✔
1092
    int default_width = mapinfo.width();
4✔
1093
    int default_height = mapinfo.height();
4✔
1094

1095
    if ( mapinfo.guessed() )
4✔
1096
      INFO_PRINTLN( "Auto-detected map dimensions: {}x{}", default_width, default_height );
2✔
1097

1098
    uo_map_width =
4✔
1099
        static_cast<unsigned short>( programArgsFindEquals( "width=", default_width, false ) );
4✔
1100
    uo_map_height =
4✔
1101
        static_cast<unsigned short>( programArgsFindEquals( "height=", default_height, false ) );
4✔
1102

1103
    check_for_errors_in_map_parameters();
4✔
1104

1105
    int x = programArgsFindEquals( "x=", -1, false );
4✔
1106
    int y = programArgsFindEquals( "y=", -1, false );
4✔
1107

1108
    // britannia: realm=main mapid=0 width=6144 height=4096
1109
    // ilshenar: realm=ilshenar mapid=2 width=2304 height=1600
1110
    // malas: realm=malas mapid=3 width=2560 height=2048
1111
    // tokuno: realm=tokuno mapid=4 width=1448 height=1448
1112
    // termur: realm=termur mapid=5 width=1280 height=4096
1113

1114
    if ( x >= 0 && y >= 0 )
4✔
1115
    {
1116
      UoConvertMain::update_map( realm, static_cast<u16>( x ), static_cast<u16>( y ) );
×
1117
    }
1118
    else
1119
    {
1120
      UoConvertMain::create_map( realm, static_cast<unsigned short>( uo_map_width ),
4✔
1121
                                 static_cast<unsigned short>( uo_map_height ) );
1122
    }
1123
  }
4✔
1124
  else if ( command == "statics" )
7✔
1125
  {
1126
    std::string realm = programArgsFindEquals( "realm=", "britannia" );
2✔
1127
    Plib::RealmDescriptor descriptor = Plib::RealmDescriptor::Load( realm );
2✔
1128

1129
    UoConvert::uo_mapid = descriptor.uomapid;
2✔
1130
    UoConvert::uo_usedif = descriptor.uodif;
2✔
1131
    UoConvert::uo_map_width = static_cast<unsigned short>( descriptor.width );
2✔
1132
    UoConvert::uo_map_height = static_cast<unsigned short>( descriptor.height );
2✔
1133

1134
    UoConvert::open_uo_data_files();
2✔
1135
    UoConvert::read_uo_data();
2✔
1136

1137
    write_pol_static_files( realm );
2✔
1138
  }
2✔
1139
  else if ( command == "multis" )
5✔
1140
  {
1141
    UoConvert::open_uo_data_files();
1✔
1142
    UoConvert::read_uo_data();
1✔
1143
    UoConvertMain::create_multis_cfg();
1✔
1144
  }
1145
  else if ( command == "tiles" )
4✔
1146
  {
1147
    UoConvert::open_uo_data_files();
1✔
1148
    UoConvert::read_uo_data();
1✔
1149
    UoConvertMain::create_tiles_cfg();
1✔
1150
  }
1151
  else if ( command == "landtiles" )
3✔
1152
  {
1153
    UoConvert::open_uo_data_files();
1✔
1154
    UoConvert::read_uo_data();
1✔
1155
    UoConvertMain::create_landtiles_cfg();
1✔
1156
  }
1157
  else if ( command == "maptile" )
2✔
1158
  {
1159
    std::string realm = programArgsFindEquals( "realm=", "britannia" );
2✔
1160
    Plib::RealmDescriptor descriptor = Plib::RealmDescriptor::Load( realm );
2✔
1161

1162
    UoConvert::uo_mapid = descriptor.uomapid;
2✔
1163
    UoConvert::uo_usedif = descriptor.uodif;
2✔
1164
    UoConvert::uo_map_width = static_cast<unsigned short>( descriptor.width );
2✔
1165
    UoConvert::uo_map_height = static_cast<unsigned short>( descriptor.height );
2✔
1166

1167
    UoConvert::open_uo_data_files();
2✔
1168
    UoConvert::read_uo_data();
2✔
1169

1170
    UoConvertMain::create_maptile( realm );
2✔
1171
  }
2✔
1172
  else if ( command == "flags" )
×
1173
  {
1174
    UoConvertMain::display_flags();
×
1175
  }
1176
  else  // unknown option
1177
  {
1178
    showHelp();
×
1179
    return 1;
×
1180
  }
1181
  UoConvert::clear_tiledata();
11✔
1182
  return 0;
11✔
1183
}
11✔
1184
void UoConvertMain::check_for_errors_in_map_parameters()
4✔
1185
{
1186
  if ( !MUL::Map::valid_size( UoConvert::uo_map_size, uo_map_width, uo_map_height ) )
4✔
1187
  {
1188
    size_t expected_size =
1189
        MUL::Map::blockSize * MUL::Map::expected_blocks( uo_map_width, uo_map_height );
×
1190

1191
    INFO_PRINTLN( "\nWarning: Width and height do not match the map size ({} bytes, expected {})",
×
1192
                  UoConvert::uo_map_size, expected_size );
1193

1194
    if ( uo_map_width == 0 || uo_map_height == 0 )
×
1195
      throw std::runtime_error(
×
1196
          "Width and height were not identified automatically. Please specify them manually." );
×
1197

1198

1199
    if ( ( uo_map_width % MUL::Map::blockWidth != 0 ) ||
×
1200
         ( uo_map_height % MUL::Map::blockHeight != 0 ) )
×
1201
      throw std::runtime_error( "Width and height must be divisible by 8" );
×
1202

1203
    if ( uo_map_size < expected_size )
×
1204
      throw std::runtime_error( "Map size is smaller than the given width and height" );
×
1205
  }
1206
}
4✔
1207
bool UoConvertMain::convert_uop_to_mul()
×
1208
{
1209
  // this is kludgy and doesn't take into account the UODataPath. Mostly a proof of concept now.
1210
  UoConvert::uo_mapid = programArgsFindEquals( "mapid=", 0, false );
×
1211

1212
  std::string mul_mapfile = "map" + to_string( uo_mapid ) + ".mul";
×
1213
  std::string uop_mapfile = "map" + to_string( uo_mapid ) + "LegacyMUL.uop";
×
1214

1215
  auto maphash = []( int mapid, size_t chunkidx )
×
1216
  { return HashLittle2( fmt::format( "build/map{}legacymul/{:08d}.dat", mapid, chunkidx ) ); };
×
1217

1218
  std::ifstream ifs( uop_mapfile, std::ifstream::binary );
×
1219
  if ( !ifs )
×
1220
  {
1221
    ERROR_PRINTLN( "Error when opening mapfile: {}", uop_mapfile );
×
1222
    return false;
×
1223
  }
1224

1225
  kaitai::kstream ks( &ifs );
×
1226
  uop_t uopfile( &ks );
×
1227

1228
  // TODO: read all blocks
1229
  std::map<uint64_t, uop_t::file_t*> filemap;
×
1230
  uop_t::block_addr_t* currentblock = uopfile.header()->firstblock();
×
1231
  for ( auto file : *currentblock->block_body()->files() )
×
1232
  {
1233
    if ( file == nullptr )
×
1234
      continue;
×
1235
    if ( file->decompressed_size() == 0 )
×
1236
      continue;
×
1237
    filemap[file->filehash()] = file;
×
1238
  }
1239

1240
  if ( uopfile.header()->nfiles() != filemap.size() )
×
1241
    INFO_PRINTLN( "Warning: not all chunks read ({}/{})", filemap.size(),
×
1242
                  uopfile.header()->nfiles() );
×
1243

1244
  std::ofstream ofs( mul_mapfile, std::ofstream::binary );
×
1245
  for ( size_t i = 0; i < filemap.size(); i++ )
×
1246
  {
1247
    auto fileitr = filemap.find( maphash( uo_mapid, i ) );
×
1248
    if ( fileitr == filemap.end() )
×
1249
    {
1250
      INFO_PRINTLN( "Couldn't find file hash: {}", maphash( uo_mapid, i ) );
×
1251
      continue;
×
1252
    }
1253

1254
    auto file = fileitr->second;
×
1255
    ofs << file->data()->filebytes();
×
1256
    INFO_PRINTLN( "Wrote: {}/{}", i + 1, filemap.size() );
×
1257
  }
1258
  INFO_PRINTLN( "Done converting." );
×
1259

1260
  return true;
×
1261
}
×
1262
void UoConvertMain::setup_uoconvert()
11✔
1263
{
1264
  std::string uodata_root = programArgsFindEquals( "uodata=", "" );
11✔
1265
  unsigned short max_tile =
1266
      static_cast<unsigned short>( programArgsFindEquals( "maxtileid=", 0x0, true ) );
11✔
1267

1268
  if ( max_tile )
11✔
1269
  {
1270
    INFO_PRINTLN( "Warning: maxtileid will be ignored and detected from tiledata.mul instead." );
×
1271
  }
1272

1273
  // read required parameters from pol.cfg
1274
  if ( uodata_root.empty() )
11✔
1275
  {
1276
    INFO_PRINTLN( "Reading pol.cfg." );
×
1277
    Clib::ConfigFile cf( "pol.cfg" );
×
1278
    Clib::ConfigElem elem;
×
1279

1280
    cf.readraw( elem );
×
1281

1282
    if ( uodata_root.empty() )
×
1283
      uodata_root = Plib::UOInstallFinder::remove_elem( elem );
×
1284
  }
×
1285

1286
  // Save the parameters into this ugly global state we have
1287
  Plib::systemstate.config.uo_datafile_root = Clib::normalized_dir_form( uodata_root );
11✔
1288

1289
  // Load parameters from uoconvert.cfg (multi types, mounts, etc)
1290
  load_uoconvert_cfg();
11✔
1291
}
11✔
1292

1293
void parse_graphics_properties( Clib::ConfigElem& elem, const std::string& prop_name,
16✔
1294
                                std::set<unsigned int>& dest )
1295
{
1296
  std::string prop_value;
16✔
1297
  std::string graphicnum;
16✔
1298

1299
  if ( !elem.has_prop( prop_name.c_str() ) )
16✔
1300
  {
1301
    elem.throw_prop_not_found( prop_name );
×
1302
  }
1303

1304
  while ( elem.remove_prop( prop_name.c_str(), &prop_value ) )
32✔
1305
  {
1306
    ISTRINGSTREAM is( prop_value );
16✔
1307
    while ( is >> graphicnum )
120✔
1308
    {
1309
      dest.insert( strtoul( graphicnum.c_str(), nullptr, 0 ) );
104✔
1310
    }
1311
  }
16✔
1312
}
16✔
1313

1314
void notice_deprecated( Clib::ConfigElem& elem, const std::string& prop_name )
16✔
1315
{
1316
  if ( elem.has_prop( prop_name.c_str() ) )
16✔
1317
  {
1318
    INFO_PRINTLN( "Note: specifying {} in MultiTypes is no longer needed.", prop_name );
16✔
1319
  }
1320
}
16✔
1321

1322
void UoConvertMain::load_uoconvert_cfg()
11✔
1323
{
1324
  std::string main_cfg = "uoconvert.cfg";
11✔
1325
  if ( Clib::FileExists( main_cfg ) )
11✔
1326
  {
1327
    Clib::ConfigElem elem;
8✔
1328
    INFO_PRINTLN( "Reading uoconvert.cfg." );
8✔
1329
    Clib::ConfigFile cf_main( main_cfg );
8✔
1330
    while ( cf_main.read( elem ) )
48✔
1331
    {
1332
      if ( elem.type_is( "MultiTypes" ) )
40✔
1333
      {
1334
        parse_graphics_properties( elem, "Boats", BoatTypes );
8✔
1335
        notice_deprecated( elem, "Houses" );
8✔
1336
        notice_deprecated( elem, "Stairs" );
8✔
1337
      }
1338
      else if ( elem.type_is( "LOSOptions" ) )
32✔
1339
      {
1340
        if ( elem.has_prop( "UseNoShoot" ) )
8✔
1341
          UoConvertMain::cfg_use_no_shoot = elem.remove_bool( "UseNoShoot" );
8✔
1342

1343
        if ( elem.has_prop( "LOSThroughWindows" ) )
8✔
1344
          UoConvertMain::cfg_LOS_through_windows = elem.remove_bool( "LOSThroughWindows" );
8✔
1345
      }
1346
      else if ( elem.type_is( "Mounts" ) )
24✔
1347
      {
1348
        parse_graphics_properties( elem, "Tiles", MountTypes );
8✔
1349
      }
1350
      else if ( elem.type_is( "StaticOptions" ) )
16✔
1351
      {
1352
        if ( elem.has_prop( "MaxStaticsPerBlock" ) )
8✔
1353
        {
1354
          UoConvert::cfg_max_statics_per_block = elem.remove_int( "MaxStaticsPerBlock" );
8✔
1355

1356
          if ( UoConvert::cfg_max_statics_per_block > MAX_STATICS_PER_BLOCK )
8✔
1357
          {
1358
            UoConvert::cfg_max_statics_per_block = MAX_STATICS_PER_BLOCK;
×
1359
            INFO_PRINTLN( "max. Statics per Block limited to {} Items",
×
1360
                          UoConvert::cfg_max_statics_per_block );
1361
          }
1362
          else if ( UoConvert::cfg_max_statics_per_block < 0 )
8✔
1363
            UoConvert::cfg_max_statics_per_block = 1000;
×
1364
        }
1365

1366
        if ( elem.has_prop( "WarningStaticsPerBlock" ) )
8✔
1367
        {
1368
          UoConvert::cfg_warning_statics_per_block = elem.remove_int( "WarningStaticsPerBlock" );
8✔
1369

1370
          if ( UoConvert::cfg_warning_statics_per_block > MAX_STATICS_PER_BLOCK )
8✔
1371
          {
1372
            UoConvert::cfg_warning_statics_per_block = MAX_STATICS_PER_BLOCK;
×
1373
            INFO_PRINTLN( "max. Statics per Block for Warning limited to {} Items",
×
1374
                          UoConvert::cfg_warning_statics_per_block );
1375
          }
1376
          else if ( UoConvert::cfg_warning_statics_per_block < 0 )
8✔
1377
            UoConvert::cfg_warning_statics_per_block = 1000;
×
1378
        }
1379

1380
        if ( elem.has_prop( "DiscardedWaterTiles" ) )
8✔
1381
        {
1382
          parse_graphics_properties( elem, "DiscardedWaterTiles", DiscardedWaterTypes );
×
1383
        }
1384
        else
1385
          for ( int i = 0x1796; i <= 0x17B2; ++i )
240✔
1386
            DiscardedWaterTypes.insert( i );
232✔
1387

1388
        if ( elem.has_prop( "ShowIllegalGraphicWarning" ) )
8✔
1389
          UoConvert::cfg_show_illegal_graphic_warning =
8✔
1390
              elem.remove_bool( "ShowIllegalGraphicWarning" );
8✔
1391
      }
1392
      else if ( elem.type_is( "TileOptions" ) )
8✔
1393
      {
1394
        if ( elem.has_prop( "ShowRoofAndPlatformWarning" ) )
8✔
1395
          cfg_show_roof_and_platform_warning = elem.remove_bool( "ShowRoofAndPlatformWarning" );
8✔
1396
      }
1397
      else if ( elem.type_is( "ClientOptions" ) )
×
1398
      {
1399
        if ( elem.has_prop( "UseNewHSAFormat" ) )
×
1400
          INFO_PRINTLN( "Warning: UseNewHSAFormat in uoconvert.cfg is no longer needed." );
×
1401
      }
1402
    }
1403
  }
8✔
1404
}
11✔
1405
}  // namespace UoConvert
1406
}  // namespace Pol
1407

1408
///////////////////////////////////////////////////////////////////////////////
1409
///////////////////////////////////////////////////////////////////////////////
1410
///////////////////////////////////////////////////////////////////////////////
1411

1412
int main( int argc, char* argv[] )
11✔
1413
{
1414
  Pol::UoConvert::UoConvertMain* UoConvertMain = new Pol::UoConvert::UoConvertMain();
11✔
1415
  UoConvertMain->start( argc, argv );
11✔
1416
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc