polserver/pol-core/uoconvert/UoConvertMain.cpp

1364 lines
42 KiB
C++
Raw Permalink Normal View History

2015-11-14 00:25:39 +01:00
#include "UoConvertMain.h"
#include <stdio.h>
#include <string.h>
#include <string>
2015-11-14 00:25:39 +01:00
#include "../clib/Program/ProgramMain.h"
2015-11-14 00:25:39 +01:00
#include "../clib/cfgelem.h"
#include "../clib/cfgfile.h"
#include "../clib/fileutil.h"
#include "../clib/logfacility.h"
#include "../clib/passert.h"
#include "../clib/rawtypes.h"
#include "../clib/stlutil.h"
2015-11-14 00:25:39 +01:00
#include "../clib/timer.h"
#include "../plib/clidata.h"
#include "../plib/mapcell.h"
#include "../plib/mapfunc.h"
#include "../plib/mapshape.h"
#include "../plib/mapsolid.h"
#include "../plib/maptile.h"
#include "../plib/mapwriter.h"
#include "../plib/polfile.h"
#include "../plib/realmdescriptor.h"
#include "../plib/systemstate.h"
#include "../plib/udatfile.h"
#include "../plib/uofile.h"
#include "../plib/uofilei.h"
#include "../plib/uoinstallfinder.h"
#include "../plib/uopreader/uop.h"
#include "../plib/uopreader/uophash.h"
#include "../plib/ustruct.h"
2018-12-22 00:32:47 +01:00
#include "../pol/landtile.h"
#include "../pol/objtype.h"
2015-11-14 00:25:39 +01:00
namespace Pol
{
namespace UoConvert
{
2015-11-14 00:25:39 +01:00
using namespace std;
using namespace Pol::Core;
using namespace Pol::Plib;
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
2019-01-22 23:51:19 +01:00
UoConvertMain::UoConvertMain()
: Pol::Clib::ProgramMain(), cfg_use_no_shoot( false ), cfg_LOS_through_windows( false )
{
}
UoConvertMain::~UoConvertMain() {}
2015-11-14 00:25:39 +01:00
///////////////////////////////////////////////////////////////////////////////
void UoConvertMain::showHelp()
{
ERROR_PRINT << "Usage:\n"
<< " \n"
<< " UOCONVERT command [options ...]\n"
<< " \n"
<< " Commands: \n"
<< " map {uodata=Dir} {maxtileid=0x3FFF/0x7FFF} {realm=realmname} {width=Width}"
<< " {height=Height} {mapid=0} {readuop=1} {x=X} {y=Y}\n"
<< " statics {uodata=Dir} {maxtileid=0x3FFF/0x7FFF} {realm=realmname}\n"
<< " maptile {uodata=Dir} {maxtileid=0x3FFF/0x7FFF} {realm=realmname}\n"
<< " multis {uodata=Dir} {maxtileid=0x3FFF/0x7FFF} {outdir=dir}\n"
<< " tiles {uodata=Dir} {maxtileid=0x3FFF/0x7FFF} {outdir=dir}\n"
<< " landtiles {uodata=Dir} {maxtileid=0x3FFF/0x7FFF} {outdir=dir}\n";
2015-11-14 00:25:39 +01:00
}
using namespace Core;
using namespace Plib;
2019-01-22 23:51:19 +01:00
void UoConvertMain::display_flags()
2015-11-14 00:25:39 +01:00
{
for ( unsigned blocking = 0; blocking <= 1; ++blocking )
{
2015-11-14 11:39:26 +01:00
for ( unsigned platform = 0; platform <= 1; ++platform )
{
for ( unsigned walk = 0; walk <= 1; ++walk )
{
for ( unsigned wall = 0; wall <= 1; ++wall )
{
for ( unsigned half = 0; half <= 1; ++half )
{
for ( unsigned floor = 0; floor <= 1; ++floor )
{
unsigned flags = 0;
if ( blocking )
2018-12-22 00:32:47 +01:00
flags |= USTRUCT_TILE::FLAG_BLOCKING;
if ( platform )
2018-12-22 00:32:47 +01:00
flags |= USTRUCT_TILE::FLAG_PLATFORM;
if ( walk )
2018-12-22 00:32:47 +01:00
flags |= USTRUCT_TILE::FLAG__WALK;
if ( wall )
2018-12-22 00:32:47 +01:00
flags |= USTRUCT_TILE::FLAG_WALL;
if ( half )
2018-12-22 00:32:47 +01:00
flags |= USTRUCT_TILE::FLAG_HALF_HEIGHT;
if ( floor )
2018-12-22 00:32:47 +01:00
flags |= USTRUCT_TILE::FLAG_FLOOR;
unsigned int polflags = Plib::polflags_from_tileflags(
0x4000, flags, cfg_use_no_shoot, cfg_LOS_through_windows );
2015-11-14 11:39:26 +01:00
unsigned moveland = ( polflags & Plib::FLAG::MOVELAND ) ? 1 : 0;
INFO_PRINT.Format( "{} {} {} {} {} {}: {}\n" )
<< blocking << platform << walk << wall << half << floor << moveland;
2015-11-14 11:39:26 +01:00
}
}
}
}
}
2015-11-14 00:25:39 +01:00
}
}
2019-01-22 23:51:19 +01:00
void UoConvertMain::create_maptile( const std::string& realmname )
2015-11-14 00:25:39 +01:00
{
Plib::RealmDescriptor descriptor = Plib::RealmDescriptor::Load( realmname );
uo_map_height = static_cast<unsigned short>( descriptor.height );
uo_map_width = static_cast<unsigned short>( descriptor.width );
INFO_PRINT << "Creating maptile file.\n"
<< " Realm: " << realmname << "\n"
<< " Map ID: " << descriptor.uomapid << "\n"
<< " Use Dif files: " << ( descriptor.uodif ? "Yes" : "No" ) << "\n"
<< " Size: " << uo_map_width << "x" << uo_map_height << "\n";
2015-11-14 00:25:39 +01:00
auto writer = new Plib::MapWriter();
writer->OpenExistingFiles( realmname );
for ( unsigned short y_base = 0; y_base < uo_map_height; y_base += Plib::MAPTILE_CHUNK )
{
2015-11-14 11:39:26 +01:00
for ( unsigned short x_base = 0; x_base < uo_map_width; x_base += Plib::MAPTILE_CHUNK )
{
for ( unsigned short x_add = 0; x_add < Plib::MAPTILE_CHUNK; ++x_add )
{
for ( unsigned short y_add = 0; y_add < Plib::MAPTILE_CHUNK; ++y_add )
{
unsigned short x = x_base + x_add;
unsigned short y = y_base + y_add;
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
short z;
2018-12-22 00:32:47 +01:00
USTRUCT_MAPINFO mi;
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
safe_getmapinfo( x, y, &z, &mi );
2015-11-14 00:25:39 +01:00
if ( mi.landtile > 0x3FFF )
INFO_PRINT.Format( "Tile 0x{:X} at ({},{},{}) is an invalid ID!\n" )
<< mi.landtile << x << y << z;
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
// for water, don't average with surrounding tiles.
2018-12-22 00:32:47 +01:00
if ( Plib::landtile_uoflags_read( mi.landtile ) & Plib::USTRUCT_TILE::FLAG_LIQUID )
2015-11-14 11:39:26 +01:00
z = mi.z;
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
Plib::MAPTILE_CELL cell;
cell.landtile = mi.landtile;
cell.z = static_cast<signed char>( z );
writer->SetMapTile( x, y, cell );
}
}
}
2015-11-14 00:25:39 +01:00
INFO_PRINT << "\rConverting: " << y_base * 100 / uo_map_height << "%";
}
writer->Flush();
delete writer;
INFO_PRINT << "\rConversion complete.\n";
}
class StaticsByZ
{
public:
bool operator()( const StaticRec& a, const StaticRec& b )
{
return ( a.z < b.z ) || ( ( a.z == b.z && a.height < b.height ) );
2015-11-14 00:25:39 +01:00
}
};
2019-01-23 00:06:05 +01:00
2015-11-14 00:25:39 +01:00
bool flags_match( unsigned int f1, unsigned int f2, unsigned char bits_compare )
{
return ( f1 & bits_compare ) == ( f2 & bits_compare );
2015-11-14 00:25:39 +01:00
}
2019-01-23 00:06:05 +01:00
/*
2015-11-14 00:25:39 +01:00
bool otherflags_match( unsigned char f1, unsigned char f2, unsigned char bits_exclude )
{
return ( f1 & ~bits_exclude ) == ( f2 & ~bits_exclude );
2015-11-14 00:25:39 +01:00
}
bool differby_exactly( unsigned char f1, unsigned char f2, unsigned char bits )
{
return ( ( f1 ^ f2 ) == bits );
2019-01-23 00:06:05 +01:00
}*/
2015-11-14 00:25:39 +01:00
2019-01-22 23:51:19 +01:00
void UoConvertMain::update_map( const std::string& realm, unsigned short x, unsigned short y )
2015-11-14 00:25:39 +01:00
{
auto mapwriter = new MapWriter();
mapwriter->OpenExistingFiles( realm );
rawmapfullread();
rawstaticfullread();
unsigned short x_base = x / SOLIDX_X_SIZE * SOLIDX_X_SIZE;
unsigned short y_base = y / SOLIDX_Y_SIZE * SOLIDX_Y_SIZE;
ProcessSolidBlock( x_base, y_base, *mapwriter );
delete mapwriter;
INFO_PRINT << "empty=" << empty << ", nonempty=" << nonempty << "\n"
<< "with more_solids: " << with_more_solids << "\n"
<< "total statics=" << total_statics << "\n";
2015-11-14 00:25:39 +01:00
}
2019-01-22 23:51:19 +01:00
void UoConvertMain::create_map( const std::string& realm, unsigned short width,
unsigned short height )
2015-11-14 00:25:39 +01:00
{
auto mapwriter = new MapWriter();
INFO_PRINT << "Creating map base and solids files.\n"
<< " Realm: " << realm << "\n"
<< " Map ID: " << uo_mapid << "\n"
<< " Reading UOP file: " << ( uo_readuop ? "Yes" : "No" ) << "\n"
<< " Use Dif files: " << ( uo_usedif ? "Yes" : "No" ) << "\n"
<< " Size: " << uo_map_width << "x" << uo_map_height << "\n"
<< "Initializing files: ";
2015-11-14 00:25:39 +01:00
mapwriter->CreateNewFiles( realm, width, height );
INFO_PRINT << "Done.\n";
Tools::Timer<> timer;
rawmapfullread();
rawstaticfullread();
INFO_PRINT << " Reading mapfiles time: " << timer.ellapsed() << " ms.\n";
for ( unsigned short y_base = 0; y_base < height; y_base += SOLIDX_Y_SIZE )
{
2015-11-14 11:39:26 +01:00
for ( unsigned short x_base = 0; x_base < width; x_base += SOLIDX_X_SIZE )
{
ProcessSolidBlock( x_base, y_base, *mapwriter );
}
2015-11-14 00:25:39 +01:00
INFO_PRINT << "\rConverting: " << y_base * 100 / height << "%";
}
timer.stop();
mapwriter->WriteConfigFile();
delete mapwriter;
INFO_PRINT << "\rConversion complete. \n"
<< "Conversion details:\n"
<< " Total blocks: " << empty + nonempty << "\n"
<< " Blocks with solids: " << nonempty << " ("
<< ( nonempty * 100 / ( empty + nonempty ) ) << "%)"
<< "\n"
<< " Blocks without solids: " << empty << " ("
<< ( empty * 100 / ( empty + nonempty ) ) << "%)"
<< "\n"
<< " Locations with solids: " << with_more_solids << "\n"
<< " Total number of solids: " << total_statics << "\n"
<< " Elapsed time: " << timer.ellapsed() << " ms.\n";
2015-11-14 00:25:39 +01:00
}
2019-01-23 00:06:05 +01:00
static bool is_no_draw( USTRUCT_MAPINFO& mi )
2015-11-14 00:25:39 +01:00
{
return ( mi.landtile == 0x2 );
}
2019-01-23 00:06:05 +01:00
static bool is_cave_exit( USTRUCT_MAPINFO& mi )
2015-11-14 00:25:39 +01:00
{
return ( mi.landtile == 0x7ec || mi.landtile == 0x7ed || mi.landtile == 0x7ee ||
mi.landtile == 0x7ef || mi.landtile == 0x7f0 || mi.landtile == 0x7f1 ||
mi.landtile == 0x834 || mi.landtile == 0x835 || mi.landtile == 0x836 ||
mi.landtile == 0x837 || mi.landtile == 0x838 || mi.landtile == 0x839 ||
mi.landtile == 0x1d3 || mi.landtile == 0x1d4 || mi.landtile == 0x1d5 ||
mi.landtile == 0x1d6 || mi.landtile == 0x1d7 || mi.landtile == 0x1d8 ||
mi.landtile == 0x1d9 || mi.landtile == 0x1da );
2015-11-14 00:25:39 +01:00
}
2019-01-23 00:06:05 +01:00
static bool is_cave_shadow( USTRUCT_MAPINFO& mi )
2015-11-14 00:25:39 +01:00
{
return ( mi.landtile == 0x1db || // shadows above caves
mi.landtile == 0x1ae || // more shadows above caves
mi.landtile == 0x1af || mi.landtile == 0x1b0 || mi.landtile == 0x1b1 ||
mi.landtile == 0x1b2 || mi.landtile == 0x1b3 || mi.landtile == 0x1b4 ||
mi.landtile == 0x1b5 );
2015-11-14 00:25:39 +01:00
}
short get_lowestadjacentz( unsigned short x, unsigned short y, short z )
{
USTRUCT_MAPINFO mi;
short z0;
short lowest_z = z;
bool cave_override = false;
if ( ( x - 1 >= 0 ) && ( y - 1 >= 0 ) )
{
2015-11-14 11:39:26 +01:00
safe_getmapinfo( x - 1, y - 1, &z0, &mi );
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
if ( is_cave_shadow( mi ) || is_cave_exit( mi ) )
z0 = z;
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
if ( is_no_draw( mi ) )
cave_override = true;
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
if ( z0 < lowest_z )
{
lowest_z = z0;
}
2015-11-14 00:25:39 +01:00
}
if ( x - 1 >= 0 )
{
2015-11-14 11:39:26 +01:00
safe_getmapinfo( x - 1, y, &z0, &mi );
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
if ( is_cave_shadow( mi ) || is_cave_exit( mi ) )
z0 = z;
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
if ( is_no_draw( mi ) )
cave_override = true;
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
if ( z0 < lowest_z )
{
lowest_z = z0;
}
2015-11-14 00:25:39 +01:00
}
if ( ( x - 1 >= 0 ) && ( y + 1 < uo_map_height ) )
{
2015-11-14 11:39:26 +01:00
safe_getmapinfo( x - 1, y + 1, &z0, &mi );
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
if ( is_cave_shadow( mi ) || is_cave_exit( mi ) )
z0 = z;
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
if ( is_no_draw( mi ) )
cave_override = true;
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
if ( z0 < lowest_z )
{
lowest_z = z0;
}
2015-11-14 00:25:39 +01:00
}
if ( y - 1 >= 0 )
{
2015-11-14 11:39:26 +01:00
safe_getmapinfo( x, y - 1, &z0, &mi );
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
if ( is_cave_shadow( mi ) || is_cave_exit( mi ) )
z0 = z;
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
if ( is_no_draw( mi ) )
cave_override = true;
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
if ( z0 < lowest_z )
{
lowest_z = z0;
}
2015-11-14 00:25:39 +01:00
}
if ( ( y - 1 >= 0 ) && ( x + 1 < uo_map_width ) )
{
2015-11-14 11:39:26 +01:00
safe_getmapinfo( x + 1, y - 1, &z0, &mi );
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
if ( is_cave_shadow( mi ) || is_cave_exit( mi ) )
z0 = z;
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
if ( is_no_draw( mi ) )
cave_override = true;
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
if ( z0 < lowest_z )
{
lowest_z = z0;
}
2015-11-14 00:25:39 +01:00
}
if ( x + 1 < uo_map_width )
{
2015-11-14 11:39:26 +01:00
safe_getmapinfo( x + 1, y, &z0, &mi );
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
if ( is_cave_shadow( mi ) || is_cave_exit( mi ) )
z0 = z;
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
if ( is_no_draw( mi ) )
cave_override = true;
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
if ( z0 < lowest_z )
{
lowest_z = z0;
}
2015-11-14 00:25:39 +01:00
}
if ( ( x + 1 < uo_map_width ) && ( y + 1 < uo_map_height ) )
{
2015-11-14 11:39:26 +01:00
safe_getmapinfo( x + 1, y + 1, &z0, &mi );
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
if ( is_cave_shadow( mi ) || is_cave_exit( mi ) )
z0 = z;
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
if ( is_no_draw( mi ) )
cave_override = true;
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
if ( z0 < lowest_z )
{
lowest_z = z0;
}
2015-11-14 00:25:39 +01:00
}
if ( y + 1 < uo_map_height )
{
2015-11-14 11:39:26 +01:00
safe_getmapinfo( x, y + 1, &z0, &mi );
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
if ( is_cave_shadow( mi ) || is_cave_exit( mi ) )
z0 = z;
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
if ( is_no_draw( mi ) )
cave_override = true;
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
if ( z0 < lowest_z )
{
lowest_z = z0;
}
2015-11-14 00:25:39 +01:00
}
if ( cave_override )
2015-11-14 11:39:26 +01:00
return z;
2015-11-14 00:25:39 +01:00
else
2015-11-14 11:39:26 +01:00
return lowest_z;
2015-11-14 00:25:39 +01:00
}
2019-01-22 23:51:19 +01:00
void UoConvertMain::ProcessSolidBlock( unsigned short x_base, unsigned short y_base,
MapWriter& mapwriter )
2015-11-14 00:25:39 +01:00
{
unsigned int idx2_offset = 0;
SOLIDX2_ELEM idx2_elem;
memset( &idx2_elem, 0, sizeof idx2_elem );
idx2_elem.baseindex = mapwriter.NextSolidIndex();
unsigned short x_add_max = SOLIDX_X_SIZE, y_add_max = SOLIDX_Y_SIZE;
if ( x_base + x_add_max > uo_map_width )
2015-11-14 11:39:26 +01:00
x_add_max = uo_map_width - x_base;
2015-11-14 00:25:39 +01:00
if ( y_base + y_add_max > uo_map_height )
2015-11-14 11:39:26 +01:00
y_add_max = uo_map_height - y_base;
2015-11-14 00:25:39 +01:00
for ( unsigned short x_add = 0; x_add < x_add_max; ++x_add )
{
2015-11-14 11:39:26 +01:00
for ( unsigned short y_add = 0; y_add < y_add_max; ++y_add )
{
unsigned short x = x_base + x_add;
unsigned short y = y_base + y_add;
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
StaticList statics;
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
// read the map, and treat it like a static.
short z;
USTRUCT_MAPINFO mi;
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
safe_getmapinfo( x, y, &z, &mi );
2015-11-14 00:25:39 +01:00
if ( mi.landtile > 0x3FFF )
INFO_PRINT.Format( "Tile 0x{:X} at ({},{},{}) is an invalid ID!\n" )
<< mi.landtile << x << y << z;
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
// for water, don't average with surrounding tiles.
2018-12-22 00:32:47 +01:00
if ( Plib::landtile_uoflags_read( mi.landtile ) & USTRUCT_TILE::FLAG_LIQUID )
2015-11-14 11:39:26 +01:00
z = mi.z;
short low_z = get_lowestadjacentz( x, y, z );
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
short lt_height = z - low_z;
z = low_z;
2015-11-14 00:25:39 +01:00
if ( mi.landtile > 0x3FFF )
INFO_PRINT.Format( "Tile 0x{:X} at ({},{},{}) is an invalid ID!\n" )
<< mi.landtile << x << y << z;
2015-11-14 00:25:39 +01:00
2018-12-22 00:32:47 +01:00
unsigned int lt_flags = Plib::landtile_uoflags_read( mi.landtile );
2015-11-14 11:39:26 +01:00
if ( ~lt_flags & USTRUCT_TILE::FLAG_BLOCKING )
{ // this seems to be the default.
2015-11-14 11:39:26 +01:00
lt_flags |= USTRUCT_TILE::FLAG_PLATFORM;
}
lt_flags |=
USTRUCT_TILE::FLAG_NO_SHOOT; // added to make sure people using noshoot will have shapes
2015-11-14 11:39:26 +01:00
// generated by this tile in future block LOS, shouldn't
// affect people using old LOS method one way or another.
lt_flags |= USTRUCT_TILE::FLAG_FLOOR;
lt_flags |= USTRUCT_TILE::FLAG_HALF_HEIGHT; // the entire map is this way
2015-11-14 11:39:26 +01:00
if ( lt_flags & USTRUCT_TILE::FLAG_WALL )
lt_height = 20;
readstatics( statics, x, y,
USTRUCT_TILE::FLAG_BLOCKING | USTRUCT_TILE::FLAG_PLATFORM |
USTRUCT_TILE::FLAG_HALF_HEIGHT | USTRUCT_TILE::FLAG_LIQUID |
USTRUCT_TILE::FLAG_HOVEROVER
// USTRUCT_TILE::FLAG__WALK
);
2015-11-14 11:39:26 +01:00
for ( unsigned i = 0; i < statics.size(); ++i )
{
StaticRec srec = statics[i];
unsigned int polflags = polflags_from_tileflags( srec.graphic, srec.flags, cfg_use_no_shoot,
cfg_LOS_through_windows );
2015-11-14 11:39:26 +01:00
if ( ( ~polflags & FLAG::MOVELAND ) && ( ~polflags & FLAG::MOVESEA ) &&
( ~polflags & FLAG::BLOCKSIGHT ) && ( ~polflags & FLAG::BLOCKING ) &&
2015-11-14 11:39:26 +01:00
( ~polflags & FLAG::OVERFLIGHT ) )
{
// remove it. we'll re-sort later.
statics.erase( statics.begin() + i );
--i; // do-over
2015-11-14 11:39:26 +01:00
}
if ( ( ~srec.flags & USTRUCT_TILE::FLAG_BLOCKING ) &&
( ~srec.flags & USTRUCT_TILE::FLAG_PLATFORM ) &&
( ~srec.flags & USTRUCT_TILE::FLAG_HALF_HEIGHT ) &&
( ~srec.flags & USTRUCT_TILE::FLAG_LIQUID ) &&
( ~srec.flags & USTRUCT_TILE::FLAG_HOVEROVER ) )
/*(~srec.flags & USTRUCT_TILE::FLAG__WALK)*/
2015-11-14 11:39:26 +01:00
{
// remove it. we'll re-sort later.
statics.erase( statics.begin() + i );
--i; // do-over
2015-11-14 11:39:26 +01:00
}
}
bool addMap = true;
for ( const auto& srec : statics )
2015-11-14 11:39:26 +01:00
{
// Look for water tiles. If there are any, discard the map (which is usually at -15 anyway)
if ( z + lt_height <= srec.z &&
( ( srec.z - ( z + lt_height ) ) <=
10 ) && // only where the map is below or same Z as the static
srec.graphic >= 0x1796 &&
srec.graphic <= 0x17B2 ) // FIXME hardcoded
2015-11-14 11:39:26 +01:00
{
// arr, there be water here
addMap = false;
}
// if there's a static on top of one of these "wall" landtiles, make it override.
if ( ( lt_flags & USTRUCT_TILE::FLAG_WALL ) && // wall?
z <= srec.z && srec.z - z <= lt_height )
2015-11-14 11:39:26 +01:00
{
lt_height = srec.z - z;
}
}
// shadows above caves
if ( is_cave_shadow( mi ) && !statics.empty() )
{
addMap = false;
}
// If the map is a NODRAW tile, and there are statics, discard the map tile
if ( mi.landtile == 2 && !statics.empty() )
addMap = false;
if ( addMap )
statics.push_back( StaticRec( 0, static_cast<signed char>( z ), lt_flags,
static_cast<char>( lt_height ) ) );
2015-11-14 11:39:26 +01:00
sort( statics.begin(), statics.end(), StaticsByZ() );
reverse( statics.begin(), statics.end() );
2015-11-14 00:25:39 +01:00
std::vector<MapShape> shapes;
2015-11-14 11:39:26 +01:00
// try to consolidate like shapes, and discard ones we don't care about.
while ( !statics.empty() )
{
StaticRec srec = statics.back();
statics.pop_back();
unsigned int polflags = polflags_from_tileflags( srec.graphic, srec.flags, cfg_use_no_shoot,
cfg_LOS_through_windows );
if ( ( ~polflags & FLAG::MOVELAND ) && ( ~polflags & FLAG::MOVESEA ) &&
( ~polflags & FLAG::BLOCKSIGHT ) && ( ~polflags & FLAG::BLOCKING ) &&
2015-11-14 11:39:26 +01:00
( ~polflags & FLAG::OVERFLIGHT ) )
{
passert_always( 0 );
continue;
}
if ( ( ~srec.flags & USTRUCT_TILE::FLAG_BLOCKING ) &&
( ~srec.flags & USTRUCT_TILE::FLAG_PLATFORM ) &&
( ~srec.flags & USTRUCT_TILE::FLAG_HALF_HEIGHT ) &&
( ~srec.flags & USTRUCT_TILE::FLAG_LIQUID ) &&
( ~srec.flags & USTRUCT_TILE::FLAG_HOVEROVER ) )
/*(~srec.flags & USTRUCT_TILE::FLAG__WALK)*/
2015-11-14 11:39:26 +01:00
{
passert_always( 0 );
continue;
}
if ( shapes.empty() )
{
// this, whatever it is, is the map base.
// TODO: look for water statics and use THOSE as the map.
2015-11-14 11:39:26 +01:00
MapShape shape;
shape.z = srec.z; // these will be converted below to
shape.height = 0; // make the map "solid"
2015-11-14 11:39:26 +01:00
shape.flags = static_cast<unsigned char>( polflags );
// no matter what, the lowest level is gradual
shape.flags |= FLAG::GRADUAL;
shapes.push_back( shape );
// for wall flag - map tile always height 0, at bottom. if map tile has height, add it as
// a static
2015-11-14 11:39:26 +01:00
if ( srec.height != 0 )
{
MapShape _shape;
_shape.z = srec.z;
_shape.height = srec.height;
_shape.flags = polflags;
shapes.push_back( _shape );
}
continue;
}
MapShape& prev = shapes.back();
// we're adding it.
MapShape shape;
shape.z = srec.z;
shape.height = srec.height;
shape.flags = polflags;
// always add the map shape seperately
2015-11-14 11:39:26 +01:00
if ( shapes.size() == 1 )
{
shapes.push_back( shape );
continue;
}
if ( shape.z < prev.z + prev.height )
{
// things can't exist in the same place.
// shrink the bottom part of this shape.
// if that would give it negative height, then skip it.
short height_remove = prev.z + prev.height - shape.z;
if ( height_remove <= shape.height )
{
shape.z += height_remove;
shape.height -= height_remove;
}
else
{ // example: 5530, 14
2015-11-14 11:39:26 +01:00
continue;
}
}
// sometimes water has "sand" a couple z-coords above it.
// We'll try to detect this (really, anything that is up to 4 dist from water)
// and extend the thing above downward.
if ( ( prev.flags & FLAG::MOVESEA ) && ( shape.z > prev.z + prev.height ) &&
2015-11-14 11:39:26 +01:00
( shape.z <= prev.z + prev.height + 4 ) )
{
short height_add = shape.z - prev.z - prev.height;
shape.z -= height_add;
shape.height += height_add;
}
if ( ( prev.flags & FLAG::MOVESEA ) && ( prev.z + prev.height == -5 ) &&
( shape.flags & FLAG::MOVESEA ) && ( shape.z == 25 ) )
2015-11-14 11:39:26 +01:00
{
// oddly, there are some water tiles at z=25 in some places...I don't get it
continue;
}
// string prevflags_s = flagstr(prev.flags);
// const char* prevflags = prevflags_s.c_str();
// string shapeflags_s = flagstr(shape.flags);
// const char* shapeflags = shapeflags_s.c_str();
2015-11-14 11:39:26 +01:00
if ( shape.z > prev.z + prev.height )
{
//
// elevated above what's below, must include separately
//
shapes.push_back( shape );
continue;
}
passert_always( shape.z == prev.z + prev.height );
if ( shape.z == prev.z + prev.height )
{
//
// sitting right on top of the previous solid
//
// standable atop non-standable: standable
// nonstandable atop standable: nonstandable
// etc
bool can_combine =
flags_match( prev.flags, shape.flags, FLAG::BLOCKSIGHT | FLAG::BLOCKING );
if ( prev.flags & FLAG::MOVELAND && ~shape.flags & FLAG::BLOCKING &&
2015-11-14 11:39:26 +01:00
~shape.flags & FLAG::MOVELAND )
{
can_combine = false;
}
if ( can_combine )
{
prev.flags = shape.flags;
prev.height += shape.height;
}
else // if one blocks LOS, but not the other, they can't be combined this way.
2015-11-14 11:39:26 +01:00
{
shapes.push_back( shape );
continue;
}
}
}
// the first StaticShape is the map base.
MapShape base = shapes[0];
shapes.erase( shapes.begin() );
MAPCELL cell;
passert_always( base.height == 0 );
cell.z = static_cast<signed char>(
base.z ); // assume now map has height=1. a static was already added if it was >0
2015-11-14 11:39:26 +01:00
cell.flags = static_cast<u8>( base.flags );
if ( !shapes.empty() )
cell.flags |= FLAG::MORE_SOLIDS;
mapwriter.SetMapCell( x, y, cell );
if ( !shapes.empty() )
{
++with_more_solids;
total_statics += static_cast<unsigned int>( shapes.size() );
if ( idx2_offset == 0 )
idx2_offset = mapwriter.NextSolidx2Offset();
unsigned int addindex = mapwriter.NextSolidIndex() - idx2_elem.baseindex;
if ( addindex > std::numeric_limits<unsigned short>::max() )
throw std::runtime_error( "addoffset overflow" );
2015-11-14 11:39:26 +01:00
idx2_elem.addindex[x_add][y_add] = static_cast<unsigned short>( addindex );
int count = static_cast<int>( shapes.size() );
for ( int j = 0; j < count; ++j )
{
MapShape shape = shapes[j];
char _z, height, flags;
_z = static_cast<char>( shapes[j].z );
height = static_cast<char>( shape.height );
flags = static_cast<u8>( shape.flags );
if ( !height ) // make 0 height solid
2015-11-14 11:39:26 +01:00
{
--_z;
++height;
}
if ( j != count - 1 )
flags |= FLAG::MORE_SOLIDS;
SOLIDS_ELEM solid;
solid.z = _z;
solid.height = height;
solid.flags = flags;
mapwriter.AppendSolid( solid );
}
}
}
2015-11-14 00:25:39 +01:00
}
if ( idx2_offset )
{
2015-11-14 11:39:26 +01:00
++nonempty;
mapwriter.AppendSolidx2Elem( idx2_elem );
2015-11-14 00:25:39 +01:00
}
else
{
2015-11-14 11:39:26 +01:00
++empty;
2015-11-14 00:25:39 +01:00
}
mapwriter.SetSolidx2Offset( x_base, y_base, idx2_offset );
}
2019-01-22 23:51:19 +01:00
void UoConvertMain::write_multi( FILE* multis_cfg, unsigned id, FILE* multi_mul,
unsigned int offset, unsigned int length )
2015-11-14 00:25:39 +01:00
{
USTRUCT_MULTI_ELEMENT elem;
unsigned int count;
if ( cfg_use_new_hsa_format )
2015-11-14 11:39:26 +01:00
count = length / sizeof( USTRUCT_MULTI_ELEMENT_HSA );
2015-11-14 00:25:39 +01:00
else
2015-11-14 11:39:26 +01:00
count = length / sizeof elem;
2015-11-14 00:25:39 +01:00
std::string type, mytype;
if ( BoatTypes.count( id ) )
2015-11-14 11:39:26 +01:00
type = "Boat";
2015-11-14 00:25:39 +01:00
else if ( HouseTypes.count( id ) )
2015-11-14 11:39:26 +01:00
type = "House";
2015-11-14 00:25:39 +01:00
else if ( StairTypes.count( id ) )
2015-11-14 11:39:26 +01:00
type = "Stairs";
2015-11-14 00:25:39 +01:00
else
{
ERROR_PRINT << "Type 0x" << fmt::hexu( id )
<< " not found in uoconvert.cfg, assuming \"House\" type.\n";
2015-11-14 11:39:26 +01:00
type = "House";
2015-11-14 00:25:39 +01:00
}
mytype = type;
fprintf( multis_cfg, "%s 0x%x\n", type.c_str(), id );
fprintf( multis_cfg, "{\n" );
if ( fseek( multi_mul, offset, SEEK_SET ) != 0 )
2015-11-14 00:25:39 +01:00
{
throw std::runtime_error( "write_multi(): fseek() failed" );
2015-11-14 00:25:39 +01:00
}
bool first = true;
while ( count-- )
{
if ( fread( &elem, sizeof elem, 1, multi_mul ) != 1 )
{
throw std::runtime_error( "write_multi(): fread() failed" );
}
2015-11-14 00:25:39 +01:00
if ( cfg_use_new_hsa_format )
{
if ( fseek( multi_mul, 4, SEEK_CUR ) != 0 )
throw std::runtime_error( "write_multi(): fseek() failed" );
}
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
if ( elem.graphic == GRAPHIC_NODRAW )
continue;
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
if ( elem.flags )
type = "static";
else
type = "dynamic";
2015-11-14 00:25:39 +01:00
2015-11-14 11:39:26 +01:00
// boats typically have as their first element the "mast", but flagged as dynamic.
if ( mytype == "Boat" )
{
if ( first && elem.graphic != 1 )
type = "static";
}
2015-11-14 00:25:39 +01:00
std::string comment;
2015-11-14 11:39:26 +01:00
if ( cfg_use_new_hsa_format )
{
USTRUCT_TILE_HSA tile;
readtile( elem.graphic, &tile );
comment.assign( tile.name, sizeof( tile.name ) );
}
else
{
USTRUCT_TILE tile;
readtile( elem.graphic, &tile );
comment.assign( tile.name, sizeof( tile.name ) );
}
fprintf( multis_cfg, " %-7s 0x%04x %4d %4d %4d // %s\n", type.c_str(), elem.graphic,
elem.x, elem.y, elem.z, comment.c_str() );
2015-11-14 11:39:26 +01:00
first = false;
2015-11-14 00:25:39 +01:00
}
fprintf( multis_cfg, "}\n" );
fprintf( multis_cfg, "\n" );
}
2019-01-22 23:51:19 +01:00
void UoConvertMain::create_multis_cfg( FILE* multi_idx, FILE* multi_mul, FILE* multis_cfg )
2015-11-14 00:25:39 +01:00
{
if ( fseek( multi_idx, 0, SEEK_SET ) != 0 )
throw std::runtime_error( "create_multis_cfg: fseek failed" );
2015-11-14 00:25:39 +01:00
unsigned count = 0;
USTRUCT_IDX idxrec;
for ( int i = 0; fread( &idxrec, sizeof idxrec, 1, multi_idx ) == 1; ++i )
{
const USTRUCT_VERSION* vrec = nullptr;
2015-11-14 11:39:26 +01:00
if ( check_verdata( VERFILE_MULTI_MUL, i, vrec ) )
{
write_multi( multis_cfg, i, verfile, vrec->filepos, vrec->length );
++count;
}
else
{
if ( idxrec.offset == 0xFFffFFffLu )
continue;
write_multi( multis_cfg, i, multi_mul, idxrec.offset, idxrec.length );
++count;
}
2015-11-14 00:25:39 +01:00
}
INFO_PRINT << count << " multi definitions written to multis.cfg\n";
}
2019-01-22 23:51:19 +01:00
void UoConvertMain::create_multis_cfg()
2015-11-14 00:25:39 +01:00
{
FILE* multi_idx = open_uo_file( "multi.idx" );
FILE* multi_mul = open_uo_file( "multi.mul" );
2015-11-14 00:25:39 +01:00
std::string outdir = programArgsFindEquals( "outdir=", "." );
FILE* multis_cfg = fopen( ( outdir + "/multis.cfg" ).c_str(), "wt" );
2015-11-14 00:25:39 +01:00
create_multis_cfg( multi_idx, multi_mul, multis_cfg );
fclose( multis_cfg );
fclose( multi_idx );
fclose( multi_mul );
}
2019-01-23 00:06:05 +01:00
void UoConvertMain::write_flags( FILE* fp, unsigned int flags )
2015-11-14 00:25:39 +01:00
{
if ( flags & FLAG::MOVELAND )
fprintf( fp, " MoveLand 1\n" );
if ( flags & FLAG::MOVESEA )
fprintf( fp, " MoveSea 1\n" );
if ( flags & FLAG::BLOCKSIGHT )
fprintf( fp, " BlockSight 1\n" );
if ( ~flags & FLAG::OVERFLIGHT )
fprintf( fp, " OverFlight 0\n" );
if ( flags & FLAG::ALLOWDROPON )
fprintf( fp, " AllowDropOn 1\n" );
if ( flags & FLAG::GRADUAL )
fprintf( fp, " Gradual 1\n" );
if ( flags & FLAG::STACKABLE )
fprintf( fp, " Stackable 1\n" );
if ( flags & FLAG::BLOCKING )
fprintf( fp, " Blocking 1\n" );
if ( flags & FLAG::MOVABLE )
fprintf( fp, " Movable 1\n" );
if ( flags & FLAG::EQUIPPABLE )
fprintf( fp, " Equippable 1\n" );
if ( flags & FLAG::DESC_PREPEND_A )
fprintf( fp, " DescPrependA 1\n" );
if ( flags & FLAG::DESC_PREPEND_AN )
fprintf( fp, " DescPrependAn 1\n" );
2015-11-14 00:25:39 +01:00
}
2019-01-22 23:51:19 +01:00
void UoConvertMain::create_tiles_cfg()
2015-11-14 00:25:39 +01:00
{
std::string outdir = programArgsFindEquals( "outdir=", "." );
FILE* fp = fopen( ( outdir + "/tiles.cfg" ).c_str(), "wt" );
2015-11-14 00:25:39 +01:00
int mountCount;
char name[21];
unsigned count = 0;
for ( unsigned int graphic_i = 0; graphic_i <= Plib::systemstate.config.max_tile_id; ++graphic_i )
{
2015-11-14 11:39:26 +01:00
u16 graphic = static_cast<u16>( graphic_i );
USTRUCT_TILE tile;
if ( cfg_use_new_hsa_format )
{
USTRUCT_TILE_HSA newtile;
read_objinfo( graphic, newtile );
tile.anim = newtile.anim;
tile.flags = newtile.flags;
tile.height = newtile.height;
tile.layer = newtile.layer;
memcpy( tile.name, newtile.name, sizeof tile.name );
tile.unk14 = newtile.unk14;
tile.unk15 = newtile.unk15;
tile.unk6 = newtile.unk6;
tile.unk7 = newtile.unk7;
tile.unk8 = newtile.unk8;
tile.unk9 = newtile.unk9;
tile.weight = newtile.weight;
}
else
read_objinfo( graphic, tile );
mountCount = static_cast<int>( MountTypes.count( graphic ) );
if ( tile.name[0] == '\0' && tile.flags == 0 && tile.layer == 0 && tile.height == 0 &&
mountCount == 0 )
2015-11-14 11:39:26 +01:00
{
continue;
}
unsigned int flags =
polflags_from_tileflags( graphic, tile.flags, cfg_use_no_shoot, cfg_LOS_through_windows );
2015-11-14 11:39:26 +01:00
if ( mountCount != 0 )
{
tile.layer = 25;
flags |= FLAG::EQUIPPABLE;
}
memset( name, 0, sizeof name );
memcpy( name, tile.name, sizeof tile.name );
fprintf( fp, "tile 0x%x\n", graphic );
fprintf( fp, "{\n" );
fprintf( fp, " Desc %s\n", name );
fprintf( fp, " UoFlags 0x%08lx\n", static_cast<unsigned long>( tile.flags ) );
if ( tile.layer )
fprintf( fp, " Layer %u\n", tile.layer );
if ( flags & FLAG::EQUIPPABLE )
fprintf( fp, " AnimID %u\n", tile.anim );
2015-11-14 11:39:26 +01:00
fprintf( fp, " Height %u\n", tile.height );
fprintf( fp, " Weight %u\n", tile.weight );
write_flags( fp, flags );
fprintf( fp, "}\n" );
fprintf( fp, "\n" );
++count;
2015-11-14 00:25:39 +01:00
}
fclose( fp );
INFO_PRINT << count << " tile definitions written to tiles.cfg\n";
}
2019-01-22 23:51:19 +01:00
void UoConvertMain::create_landtiles_cfg()
2015-11-14 00:25:39 +01:00
{
std::string outdir = programArgsFindEquals( "outdir=", "." );
FILE* fp = fopen( ( outdir + "/landtiles.cfg" ).c_str(), "wt" );
2015-11-14 00:25:39 +01:00
unsigned count = 0;
for ( u16 i = 0; i <= 0x3FFF; ++i )
{
2015-11-14 11:39:26 +01:00
USTRUCT_LAND_TILE landtile;
if ( cfg_use_new_hsa_format )
{
USTRUCT_LAND_TILE_HSA newlandtile;
readlandtile( i, &newlandtile );
landtile.flags = newlandtile.flags;
landtile.unk = newlandtile.unk;
memcpy( landtile.name, newlandtile.name, sizeof landtile.name );
}
else
readlandtile( i, &landtile );
if ( landtile.name[0] || landtile.flags )
{
fprintf( fp, "landtile 0x%x\n", i );
fprintf( fp, "{\n" );
fprintf( fp, " Name %s\n", landtile.name );
fprintf( fp, " UoFlags 0x%08lx\n", static_cast<unsigned long>( landtile.flags ) );
unsigned int flags = polflags_from_landtileflags( i, landtile.flags );
flags &= ~FLAG::MOVABLE; // movable makes no sense for landtiles
2015-11-14 11:39:26 +01:00
write_flags( fp, flags );
fprintf( fp, "}\n" );
fprintf( fp, "\n" );
++count;
}
2015-11-14 00:25:39 +01:00
}
fclose( fp );
INFO_PRINT << count << " landtile definitions written to landtiles.cfg\n";
}
int UoConvertMain::main()
{
const std::vector<std::string>& binArgs = programArgs();
2015-11-14 00:25:39 +01:00
/**********************************************
* show help
**********************************************/
if ( binArgs.size() == 1 )
{
showHelp();
return 0; // return "okay"
}
2015-11-14 00:25:39 +01:00
// Setups uoconvert by finding the path of uo files and max tiles from pol.cfg or command
// line arguments. Also loads parameters from uoconvert.cfg.
setup_uoconvert();
2015-11-14 00:25:39 +01:00
std::string command = binArgs[1];
if ( command == "uoptomul" )
{
if ( !convert_uop_to_mul() )
return 1;
}
else if ( command == "map" )
{
UoConvert::uo_mapid = programArgsFindEquals( "mapid=", 0, false );
UoConvert::uo_usedif = programArgsFindEquals( "usedif=", 0, false );
UoConvert::uo_readuop = (bool)programArgsFindEquals( "readuop=", 1, false );
2015-11-14 00:25:39 +01:00
std::string realm = programArgsFindEquals( "realm=", "britannia" );
int default_width = 6144;
int default_height = 4096;
switch ( UoConvert::uo_mapid )
2015-11-14 00:25:39 +01:00
{
case 0:
case 1:
break;
case 2: // ilshenar:
default_width = 2304;
default_height = 1600;
break;
case 3: // malas
default_width = 2560;
default_height = 2048;
break;
case 4: // tokuno
default_width = 1448;
default_height = 1448;
break;
case 5: // termur
default_width = 1280;
default_height = 4096;
break;
2015-11-14 00:25:39 +01:00
}
int width = programArgsFindEquals( "width=", default_width, false );
int height = programArgsFindEquals( "height=", default_height, false );
UoConvert::uo_map_width = static_cast<unsigned short>( width );
UoConvert::uo_map_height = static_cast<unsigned short>( height );
2015-11-14 00:25:39 +01:00
UoConvert::open_uo_data_files();
UoConvert::read_uo_data();
2015-11-14 00:25:39 +01:00
int x = programArgsFindEquals( "x=", -1, false );
int y = programArgsFindEquals( "y=", -1, false );
2015-11-14 00:25:39 +01:00
// britannia: realm=main mapid=0 width=6144 height=4096
// ilshenar: realm=ilshenar mapid=2 width=2304 height=1600
// malas: realm=malas mapid=3 width=2560 height=2048
// tokuno: realm=tokuno mapid=4 width=1448 height=1448
// termur: realm=termur mapid=5 width=1280 height=4096
if ( x >= 0 && y >= 0 )
2015-11-14 00:25:39 +01:00
{
2019-01-22 23:51:19 +01:00
UoConvertMain::update_map( realm, static_cast<u16>( x ), static_cast<u16>( y ) );
2015-11-14 00:25:39 +01:00
}
else
2015-11-14 00:25:39 +01:00
{
2019-01-22 23:51:19 +01:00
UoConvertMain::create_map( realm, static_cast<unsigned short>( width ),
static_cast<unsigned short>( height ) );
2015-11-14 00:25:39 +01:00
}
}
else if ( command == "statics" )
{
std::string realm = programArgsFindEquals( "realm=", "britannia" );
Plib::RealmDescriptor descriptor = Plib::RealmDescriptor::Load( realm );
2015-11-14 00:25:39 +01:00
UoConvert::uo_mapid = descriptor.uomapid;
UoConvert::uo_usedif = descriptor.uodif;
UoConvert::uo_map_width = static_cast<unsigned short>( descriptor.width );
UoConvert::uo_map_height = static_cast<unsigned short>( descriptor.height );
UoConvert::open_uo_data_files();
UoConvert::read_uo_data();
2018-12-22 00:32:47 +01:00
write_pol_static_files( realm );
}
else if ( command == "multis" )
{
UoConvert::open_uo_data_files();
UoConvert::read_uo_data();
2019-01-22 23:51:19 +01:00
UoConvertMain::create_multis_cfg();
}
else if ( command == "tiles" )
{
UoConvert::open_uo_data_files();
UoConvert::read_uo_data();
2019-01-22 23:51:19 +01:00
UoConvertMain::create_tiles_cfg();
}
else if ( command == "landtiles" )
{
UoConvert::open_uo_data_files();
UoConvert::read_uo_data();
2019-01-22 23:51:19 +01:00
UoConvertMain::create_landtiles_cfg();
}
else if ( command == "maptile" )
{
std::string realm = programArgsFindEquals( "realm=", "britannia" );
Plib::RealmDescriptor descriptor = Plib::RealmDescriptor::Load( realm );
UoConvert::uo_mapid = descriptor.uomapid;
UoConvert::uo_usedif = descriptor.uodif;
UoConvert::uo_map_width = static_cast<unsigned short>( descriptor.width );
UoConvert::uo_map_height = static_cast<unsigned short>( descriptor.height );
UoConvert::open_uo_data_files();
UoConvert::read_uo_data();
2019-01-22 23:51:19 +01:00
UoConvertMain::create_maptile( realm );
}
else if ( command == "flags" )
{
2019-01-22 23:51:19 +01:00
UoConvertMain::display_flags();
}
else // unknown option
{
showHelp();
return 1;
}
UoConvert::clear_tiledata();
return 0;
}
bool UoConvertMain::convert_uop_to_mul()
{
// this is kludgy and doesn't take into account the UODataPath. Mostly a proof of concept now.
UoConvert::uo_mapid = programArgsFindEquals( "mapid=", 0, false );
std::string mul_mapfile = "map" + to_string( uo_mapid ) + ".mul";
std::string uop_mapfile = "map" + to_string( uo_mapid ) + "LegacyMUL.uop";
auto maphash = []( int mapid, size_t chunkidx ) {
fmt::Writer tmp;
tmp << "build/map" << mapid << "legacymul/" << fmt::pad( chunkidx, 8, '0' ) << ".dat";
return HashLittle2( tmp.str() );
};
std::ifstream ifs( uop_mapfile, std::ifstream::binary );
if ( !ifs )
{
ERROR_PRINT << "Error when opening mapfile: " << uop_mapfile << '\n';
return false;
}
kaitai::kstream ks( &ifs );
uop_t uopfile( &ks );
// TODO: read all blocks
std::map<uint64_t, uop_t::file_t*> filemap;
uop_t::block_addr_t* currentblock = uopfile.header()->firstblock();
for ( auto file : *currentblock->block_body()->files() )
{
if ( file == nullptr )
continue;
if ( file->decompressed_size() == 0 )
continue;
filemap[file->filehash()] = file;
}
if ( uopfile.header()->nfiles() != filemap.size() )
INFO_PRINT << "Warning: not all chunks read (" << filemap.size() << "/"
<< uopfile.header()->nfiles() << ")\n";
std::ofstream ofs( mul_mapfile, std::ofstream::binary );
for ( size_t i = 0; i < filemap.size(); i++ )
{
auto fileitr = filemap.find( maphash( uo_mapid, i ) );
if ( fileitr == filemap.end() )
{
INFO_PRINT << "Couldn't find file hash: " << maphash( uo_mapid, i );
continue;
}
auto file = fileitr->second;
ofs << file->data()->filebytes();
INFO_PRINT << "Wrote: " << i + 1 << "/" << filemap.size() << '\n';
}
INFO_PRINT << "Done converting.\n";
return true;
}
void UoConvertMain::setup_uoconvert()
{
std::string uodata_root = programArgsFindEquals( "uodata=", "" );
unsigned short max_tile =
static_cast<unsigned short>( programArgsFindEquals( "maxtileid=", 0x0, true ) );
// if any of the two is missing, read from pol.cfg
if ( uodata_root.empty() || !max_tile )
{
INFO_PRINT << "Reading pol.cfg.\n";
Clib::ConfigFile cf( "pol.cfg" );
Clib::ConfigElem elem;
cf.readraw( elem );
if ( uodata_root.empty() )
uodata_root = Plib::UOInstallFinder::remove_elem( elem );
if ( !max_tile )
max_tile = elem.remove_ushort( "MaxTileID", 0x0 );
}
if ( max_tile != UOBJ_DEFAULT_MAX && max_tile != UOBJ_SA_MAX && max_tile != UOBJ_HSA_MAX )
max_tile = UOBJ_DEFAULT_MAX;
// Save the parameters into this ugly global state we have
Plib::systemstate.config.max_tile_id = max_tile;
Plib::systemstate.config.uo_datafile_root = Clib::normalized_dir_form( uodata_root );
// Load parameters from uoconvert.cfg (multi types, mounts, etc)
load_uoconvert_cfg();
}
void UoConvertMain::load_uoconvert_cfg()
{
std::string main_cfg = "uoconvert.cfg";
if ( Clib::FileExists( main_cfg ) )
{
std::string temp;
Clib::ConfigElem elem;
INFO_PRINT << "Reading uoconvert.cfg.\n";
Clib::ConfigFile cf_main( main_cfg );
while ( cf_main.read( elem ) )
{
if ( elem.type_is( "MultiTypes" ) )
{
temp = elem.remove_string( "Boats" );
ISTRINGSTREAM is_boats( temp );
std::string graphicnum;
while ( is_boats >> graphicnum )
BoatTypes.insert( strtoul( graphicnum.c_str(), nullptr, 0 ) );
temp = elem.remove_string( "Houses" );
ISTRINGSTREAM is_houses( temp );
while ( is_houses >> graphicnum )
HouseTypes.insert( strtoul( graphicnum.c_str(), nullptr, 0 ) );
temp = elem.remove_string( "Stairs" );
ISTRINGSTREAM is_stairs( temp );
while ( is_stairs >> graphicnum )
StairTypes.insert( strtoul( graphicnum.c_str(), nullptr, 0 ) );
}
else if ( elem.type_is( "LOSOptions" ) )
{
if ( elem.has_prop( "UseNoShoot" ) )
UoConvertMain::cfg_use_no_shoot = elem.remove_bool( "UseNoShoot" );
if ( elem.has_prop( "LOSThroughWindows" ) )
UoConvertMain::cfg_LOS_through_windows = elem.remove_bool( "LOSThroughWindows" );
}
else if ( elem.type_is( "Mounts" ) )
{
std::string graphicnum;
temp = elem.remove_string( "Tiles" );
ISTRINGSTREAM is_mounts( temp );
while ( is_mounts >> graphicnum )
{
MountTypes.insert( strtoul( graphicnum.c_str(), nullptr, 0 ) );
}
}
else if ( elem.type_is( "StaticOptions" ) )
{
if ( elem.has_prop( "MaxStaticsPerBlock" ) )
{
UoConvert::cfg_max_statics_per_block = elem.remove_int( "MaxStaticsPerBlock" );
if ( UoConvert::cfg_max_statics_per_block > MAX_STATICS_PER_BLOCK )
{
UoConvert::cfg_max_statics_per_block = MAX_STATICS_PER_BLOCK;
INFO_PRINT << "max. Statics per Block limited to "
<< UoConvert::cfg_max_statics_per_block << " Items\n";
}
else if ( UoConvert::cfg_max_statics_per_block < 0 )
UoConvert::cfg_max_statics_per_block = 1000;
}
if ( elem.has_prop( "WarningStaticsPerBlock" ) )
{
UoConvert::cfg_warning_statics_per_block = elem.remove_int( "WarningStaticsPerBlock" );
if ( UoConvert::cfg_warning_statics_per_block > MAX_STATICS_PER_BLOCK )
{
UoConvert::cfg_warning_statics_per_block = MAX_STATICS_PER_BLOCK;
INFO_PRINT << "max. Statics per Block for Warning limited to "
<< UoConvert::cfg_warning_statics_per_block << " Items\n";
}
else if ( UoConvert::cfg_warning_statics_per_block < 0 )
UoConvert::cfg_warning_statics_per_block = 1000;
}
if ( elem.has_prop( "ShowIllegalGraphicWarning" ) )
UoConvert::cfg_show_illegal_graphic_warning =
elem.remove_bool( "ShowIllegalGraphicWarning" );
}
else if ( elem.type_is( "TileOptions" ) )
{
if ( elem.has_prop( "ShowRoofAndPlatformWarning" ) )
cfg_show_roof_and_platform_warning = elem.remove_bool( "ShowRoofAndPlatformWarning" );
}
else if ( elem.type_is( "ClientOptions" ) )
{
if ( elem.has_prop( "UseNewHSAFormat" ) )
UoConvert::cfg_use_new_hsa_format = elem.remove_bool( "UseNewHSAFormat" );
}
}
}
}
} // namespace UoConvert
} // namespace Pol
2015-11-14 00:25:39 +01:00
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
int main( int argc, char* argv[] )
2015-11-14 00:25:39 +01:00
{
Pol::UoConvert::UoConvertMain* UoConvertMain = new Pol::UoConvert::UoConvertMain();
UoConvertMain->start( argc, argv );
2015-11-14 00:25:39 +01:00
}