2012-03-02 04:41:29 +02:00
# include <iostream>
2012-09-30 23:34:53 +04:00
# include <vector>
# include <deque>
2012-04-08 11:51:52 +02:00
# include <list>
2021-05-06 22:41:20 +02:00
# include <unordered_set>
2012-04-08 17:52:55 +02:00
# include <map>
2015-02-22 14:14:13 +01:00
# include <fstream>
2016-04-26 15:42:11 +02:00
# include <cmath>
2022-01-29 21:09:27 +02:00
# include <memory>
2022-04-14 16:46:57 +02:00
# include <optional>
# include <iomanip>
2012-03-02 04:41:29 +02:00
# include <boost/program_options.hpp>
2022-01-22 15:58:41 +01:00
# include <components/esm3/esmreader.hpp>
# include <components/esm3/esmwriter.hpp>
2022-04-14 16:46:57 +02:00
# include <components/esm/format.hpp>
# include <components/files/openfile.hpp>
2022-08-03 00:00:54 +02:00
# include <components/misc/strings/algorithm.hpp>
2022-06-20 20:48:06 +02:00
# include <components/files/configurationmanager.hpp>
2022-07-03 00:02:29 +02:00
# include <components/files/conversion.hpp>
2010-02-19 09:12:49 +01:00
2012-09-30 23:34:53 +04:00
# include "record.hpp"
2022-04-17 09:00:58 +02:00
# include "labels.hpp"
2022-04-14 16:46:57 +02:00
# include "arguments.hpp"
# include "tes4.hpp"
namespace
{
2010-02-19 14:23:22 +01:00
2022-04-14 16:46:57 +02:00
using namespace EsmTool ;
constexpr unsigned majorVersion = 1 ;
constexpr unsigned minorVersion = 3 ;
2010-02-18 13:15:43 +01:00
2012-03-02 04:41:29 +02:00
// Create a local alias for brevity
namespace bpo = boost : : program_options ;
2012-04-08 11:51:52 +02:00
struct ESMData
{
2022-05-01 14:46:47 +02:00
ESM : : Header mHeader ;
2022-01-29 21:09:27 +02:00
std : : deque < std : : unique_ptr < EsmTool : : RecordBase > > mRecords ;
2015-07-21 20:47:02 +03:00
// Value: (Reference, Deleted flag)
std : : map < ESM : : Cell * , std : : deque < std : : pair < ESM : : CellRef , bool > > > mCellRefs ;
2012-09-30 23:34:53 +04:00
std : : map < int , int > mRecordStats ;
} ;
2012-04-08 11:51:52 +02:00
2012-03-02 04:41:29 +02:00
bool parseOptions ( int argc , char * * argv , Arguments & info )
{
2022-09-12 16:55:17 +02:00
bpo : : options_description desc ( R " (Inspect and extract from Morrowind ES files (ESM, ESP, ESS)
Syntax : esmtool [ options ] mode infile [ outfile ]
Allowed modes :
dump Dumps all readable data from the input file .
clone Clones the input file to the output file .
comp Compares the given files .
Allowed options ) " );
2022-09-12 16:48:15 +02:00
auto addOption = desc . add_options ( ) ;
addOption ( " help,h " , " print help message. " ) ;
addOption ( " version,v " , " print version information and quit. " ) ;
addOption ( " raw,r " , bpo : : value < std : : string > ( ) ,
2022-04-14 16:46:57 +02:00
" Show an unformatted list of all records and subrecords of given format: \n "
" \n \t TES3 "
2022-09-12 16:48:15 +02:00
" \n \t TES4 " ) ;
2012-10-09 19:41:45 -04:00
// The intention is that this option would interact better
2012-12-23 23:23:24 +04:00
// with other modes including clone, dump, and raw.
2022-09-12 16:48:15 +02:00
addOption ( " type,t " , bpo : : value < std : : vector < std : : string > > ( ) ,
2012-10-09 19:41:45 -04:00
" Show only records of this type (four character record code). May "
2022-09-12 16:48:15 +02:00
" be specified multiple times. Only affects dump mode. " ) ;
addOption ( " name,n " , bpo : : value < std : : string > ( ) ,
" Show only the record with this name. Only affects dump mode. " ) ;
addOption ( " plain,p " , " Print contents of dialogs, books and scripts. "
2013-05-22 20:09:11 +02:00
" (skipped by default) "
2022-09-12 16:48:15 +02:00
" Only affects dump mode. " ) ;
addOption ( " quiet,q " , " Suppress all record information. Useful for speed tests. " ) ;
addOption ( " loadcells,C " , " Browse through contents of all cells. " ) ;
2012-03-02 04:41:29 +02:00
2022-09-12 16:48:15 +02:00
addOption ( " encoding,e " , bpo : : value < std : : string > ( & ( info . encoding ) ) - >
2012-04-08 11:51:52 +02:00
default_value ( " win1252 " ) ,
" Character encoding used in ESMTool: \n "
" \n \t win1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages \n "
" \n \t win1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages \n "
" \n \t win1252 - Western European (Latin) alphabet, used by default " )
2012-03-02 04:41:29 +02:00
;
2010-02-19 09:12:49 +01:00
2012-09-30 23:34:53 +04:00
std : : string finalText = " \n If no option is given, the default action is to parse all records in the archive \n and display diagnostic information. " ;
2012-03-02 04:41:29 +02:00
// input-file is hidden and used as a positional argument
bpo : : options_description hidden ( " Hidden Options " ) ;
2022-09-12 16:48:15 +02:00
auto addHiddenOption = hidden . add_options ( ) ;
addHiddenOption ( " mode,m " , bpo : : value < std : : string > ( ) , " esmtool mode " ) ;
addHiddenOption ( " input-file,i " , bpo : : value < Files : : MaybeQuotedPathContainer > ( ) , " input file " ) ;
2012-03-02 04:41:29 +02:00
bpo : : positional_options_description p ;
2012-04-08 11:51:52 +02:00
p . add ( " mode " , 1 ) . add ( " input-file " , 2 ) ;
2012-03-02 04:41:29 +02:00
// there might be a better way to do this
bpo : : options_description all ;
all . add ( desc ) . add ( hidden ) ;
bpo : : variables_map variables ;
2013-06-26 18:29:09 +02:00
try
{
bpo : : parsed_options valid_opts = bpo : : command_line_parser ( argc , argv )
. options ( all ) . positional ( p ) . run ( ) ;
bpo : : store ( valid_opts , variables ) ;
}
2018-11-15 18:10:19 +04:00
catch ( std : : exception & e )
2013-06-26 18:29:09 +02:00
{
2018-11-15 18:10:19 +04:00
std : : cout < < " ERROR parsing arguments: " < < e . what ( ) < < std : : endl ;
2013-06-26 18:43:27 +02:00
return false ;
}
2013-06-26 18:29:09 +02:00
2012-03-02 04:41:29 +02:00
bpo : : notify ( variables ) ;
if ( variables . count ( " help " ) )
{
2012-09-30 23:34:53 +04:00
std : : cout < < desc < < finalText < < std : : endl ;
2012-03-02 04:41:29 +02:00
return false ;
}
if ( variables . count ( " version " ) )
2010-02-18 13:15:43 +01:00
{
2022-04-14 16:46:57 +02:00
std : : cout < < " ESMTool version " < < majorVersion < < ' . ' < < minorVersion < < std : : endl ;
2012-03-02 04:41:29 +02:00
return false ;
2010-02-18 13:15:43 +01:00
}
2012-04-08 11:51:52 +02:00
if ( ! variables . count ( " mode " ) )
{
2021-06-30 21:59:47 +02:00
std : : cout < < " No mode specified! \n \n "
2012-09-30 23:34:53 +04:00
< < desc < < finalText < < std : : endl ;
2012-03-02 04:41:29 +02:00
return false ;
2010-02-18 13:15:43 +01:00
}
2012-10-09 01:33:47 -04:00
if ( variables . count ( " type " ) > 0 )
2015-01-25 03:02:36 +01:00
info . types = variables [ " type " ] . as < std : : vector < std : : string > > ( ) ;
if ( variables . count ( " name " ) > 0 )
info . name = variables [ " name " ] . as < std : : string > ( ) ;
2012-10-09 01:33:47 -04:00
2012-09-30 23:34:53 +04:00
info . mode = variables [ " mode " ] . as < std : : string > ( ) ;
2012-04-12 14:00:58 +02:00
if ( ! ( info . mode = = " dump " | | info . mode = = " clone " | | info . mode = = " comp " ) )
2012-03-02 04:41:29 +02:00
{
2021-06-30 21:59:47 +02:00
std : : cout < < " \n ERROR: invalid mode \" " < < info . mode < < " \" \n \n "
2012-09-30 23:34:53 +04:00
< < desc < < finalText < < std : : endl ;
2012-03-02 04:41:29 +02:00
return false ;
}
if ( ! variables . count ( " input-file " ) )
{
2012-09-30 23:34:53 +04:00
std : : cout < < " \n ERROR: missing ES file \n \n " ;
std : : cout < < desc < < finalText < < std : : endl ;
2012-03-02 04:41:29 +02:00
return false ;
}
// handling gracefully the user adding multiple files
2012-09-30 23:34:53 +04:00
/* if (variables["input-file"].as< std::vector<std::string> >().size() > 1)
2012-04-08 11:51:52 +02:00
{
2012-09-30 23:34:53 +04:00
std : : cout < < " \n ERROR: more than one ES file specified \n \n " ;
std : : cout < < desc < < finalText < < std : : endl ;
2012-04-08 11:51:52 +02:00
return false ;
} */
2022-08-06 18:09:50 +02:00
const auto inputFiles = variables [ " input-file " ] . as < Files : : MaybeQuotedPathContainer > ( ) ;
2022-06-23 19:13:10 +02:00
info . filename = inputFiles [ 0 ] . u8string ( ) ; // This call to u8string is redundant, but required to build on MSVC 14.26 due to implementation bugs.
2022-06-20 20:48:06 +02:00
if ( inputFiles . size ( ) > 1 )
2022-06-23 19:13:10 +02:00
info . outname = inputFiles [ 1 ] . u8string ( ) ; // This call to u8string is redundant, but required to build on MSVC 14.26 due to implementation bugs.
2012-03-02 04:41:29 +02:00
2022-04-14 16:46:57 +02:00
if ( const auto it = variables . find ( " raw " ) ; it ! = variables . end ( ) )
info . mRawFormat = ESM : : parseFormat ( it - > second . as < std : : string > ( ) ) ;
2015-01-22 19:04:59 +01:00
info . quiet_given = variables . count ( " quiet " ) ! = 0 ;
info . loadcells_given = variables . count ( " loadcells " ) ! = 0 ;
info . plain_given = variables . count ( " plain " ) ! = 0 ;
2012-03-02 04:41:29 +02:00
// Font encoding settings
2012-09-30 23:34:53 +04:00
info . encoding = variables [ " encoding " ] . as < std : : string > ( ) ;
2013-01-06 01:37:58 +01:00
if ( info . encoding ! = " win1250 " & & info . encoding ! = " win1251 " & & info . encoding ! = " win1252 " )
2012-03-02 04:41:29 +02:00
{
2021-06-30 21:59:47 +02:00
std : : cout < < info . encoding < < " is not a valid encoding option. \n " ;
2013-01-06 01:37:58 +01:00
info . encoding = " win1252 " ;
2012-03-02 04:41:29 +02:00
}
2013-01-06 01:37:58 +01:00
std : : cout < < ToUTF8 : : encodingUsingMessage ( info . encoding ) < < std : : endl ;
2012-03-02 04:41:29 +02:00
return true ;
}
2022-04-14 15:48:32 +02:00
void loadCell ( const Arguments & info , ESM : : Cell & cell , ESM : : ESMReader & esm , ESMData * data ) ;
2012-04-08 17:52:55 +02:00
2022-04-14 15:48:32 +02:00
int load ( const Arguments & info , ESMData * data ) ;
int clone ( const Arguments & info ) ;
int comp ( const Arguments & info ) ;
2012-03-02 04:41:29 +02:00
2022-04-14 16:46:57 +02:00
}
2012-03-02 04:41:29 +02:00
int main ( int argc , char * * argv )
{
2014-12-18 03:24:10 +01:00
try
{
Arguments info ;
if ( ! parseOptions ( argc , argv , info ) )
return 1 ;
if ( info . mode = = " dump " )
2022-04-14 15:48:32 +02:00
return load ( info , nullptr ) ;
2014-12-18 03:24:10 +01:00
else if ( info . mode = = " clone " )
return clone ( info ) ;
else if ( info . mode = = " comp " )
return comp ( info ) ;
else
{
std : : cout < < " Invalid or no mode specified, dying horribly. Have a nice day. " < < std : : endl ;
return 1 ;
}
}
catch ( std : : exception & e )
2012-04-08 11:51:52 +02:00
{
2014-12-18 03:24:10 +01:00
std : : cerr < < " ERROR: " < < e . what ( ) < < std : : endl ;
2012-04-08 11:51:52 +02:00
return 1 ;
}
2012-03-02 04:41:29 +02:00
2012-04-08 11:51:52 +02:00
return 0 ;
}
2012-03-02 04:41:29 +02:00
2022-04-14 16:46:57 +02:00
namespace
{
2022-04-14 15:48:32 +02:00
void loadCell ( const Arguments & info , ESM : : Cell & cell , ESM : : ESMReader & esm , ESMData * data )
2012-04-08 11:51:52 +02:00
{
2012-04-08 17:52:55 +02:00
bool quiet = ( info . quiet_given | | info . mode = = " clone " ) ;
bool save = ( info . mode = = " clone " ) ;
2010-02-19 09:12:49 +01:00
2012-04-08 11:51:52 +02:00
// Skip back to the beginning of the reference list
2012-11-10 21:43:41 +01:00
// FIXME: Changes to the references backend required to support multiple plugins have
// almost certainly broken this following line. I'll leave it as is for now, so that
// the compiler does not complain.
cell . restore ( esm , 0 ) ;
2010-03-04 15:42:59 +01:00
2012-04-08 11:51:52 +02:00
// Loop through all the references
2012-09-30 23:34:53 +04:00
ESM : : CellRef ref ;
if ( ! quiet ) std : : cout < < " References: \n " ;
2014-01-06 13:53:20 +01:00
2015-07-21 20:47:02 +03:00
bool deleted = false ;
2021-07-12 18:24:51 +02:00
ESM : : MovedCellRef movedCellRef ;
bool moved = false ;
while ( cell . getNextRef ( esm , ref , deleted , movedCellRef , moved ) )
2010-02-19 09:12:49 +01:00
{
2022-04-14 15:48:32 +02:00
if ( data ! = nullptr & & save )
data - > mCellRefs [ & cell ] . push_back ( std : : make_pair ( ref , deleted ) ) ;
2010-02-19 09:12:49 +01:00
2012-04-08 11:51:52 +02:00
if ( quiet ) continue ;
2010-02-19 09:12:49 +01:00
2021-07-12 18:26:27 +02:00
std : : cout < < " - Refnum: " < < ref . mRefNum . mIndex < < ' \n ' ;
2021-06-30 21:59:47 +02:00
std : : cout < < " ID: " < < ref . mRefID < < ' \n ' ;
std : : cout < < " Position: ( " < < ref . mPos . pos [ 0 ] < < " , " < < ref . mPos . pos [ 1 ] < < " , " < < ref . mPos . pos [ 2 ] < < " ) \n " ;
2020-04-13 20:17:16 +03:00
if ( ref . mScale ! = 1.f )
2021-06-30 21:59:47 +02:00
std : : cout < < " Scale: " < < ref . mScale < < ' \n ' ;
2020-04-13 20:17:16 +03:00
if ( ! ref . mOwner . empty ( ) )
2021-06-30 21:59:47 +02:00
std : : cout < < " Owner: " < < ref . mOwner < < ' \n ' ;
2020-04-13 20:17:16 +03:00
if ( ! ref . mGlobalVariable . empty ( ) )
2021-06-30 21:59:47 +02:00
std : : cout < < " Global: " < < ref . mGlobalVariable < < ' \n ' ;
2020-04-13 20:17:16 +03:00
if ( ! ref . mFaction . empty ( ) )
2021-06-30 21:59:47 +02:00
std : : cout < < " Faction: " < < ref . mFaction < < ' \n ' ;
2020-04-13 20:17:16 +03:00
if ( ! ref . mFaction . empty ( ) | | ref . mFactionRank ! = - 2 )
2021-06-30 21:59:47 +02:00
std : : cout < < " Faction rank: " < < ref . mFactionRank < < ' \n ' ;
std : : cout < < " Enchantment charge: " < < ref . mEnchantmentCharge < < ' \n ' ;
std : : cout < < " Uses/health: " < < ref . mChargeInt < < ' \n ' ;
std : : cout < < " Gold value: " < < ref . mGoldValue < < ' \n ' ;
std : : cout < < " Blocked: " < < static_cast < int > ( ref . mReferenceBlocked ) < < ' \n ' ;
std : : cout < < " Deleted: " < < deleted < < ' \n ' ;
2015-01-25 03:02:36 +01:00
if ( ! ref . mKey . empty ( ) )
2021-06-30 21:59:47 +02:00
std : : cout < < " Key: " < < ref . mKey < < ' \n ' ;
std : : cout < < " Lock level: " < < ref . mLockLevel < < ' \n ' ;
2020-04-13 20:17:16 +03:00
if ( ! ref . mTrap . empty ( ) )
2021-06-30 21:59:47 +02:00
std : : cout < < " Trap: " < < ref . mTrap < < ' \n ' ;
2020-04-13 20:17:16 +03:00
if ( ! ref . mSoul . empty ( ) )
2021-06-30 21:59:47 +02:00
std : : cout < < " Soul: " < < ref . mSoul < < ' \n ' ;
2020-04-13 20:17:16 +03:00
if ( ref . mTeleport )
{
std : : cout < < " Destination position: ( " < < ref . mDoorDest . pos [ 0 ] < < " , "
2021-06-30 21:59:47 +02:00
< < ref . mDoorDest . pos [ 1 ] < < " , " < < ref . mDoorDest . pos [ 2 ] < < " ) \n " ;
2020-04-13 20:17:16 +03:00
if ( ! ref . mDestCell . empty ( ) )
2021-06-30 21:59:47 +02:00
std : : cout < < " Destination cell: " < < ref . mDestCell < < ' \n ' ;
2020-04-13 20:17:16 +03:00
}
2021-07-21 14:40:54 +02:00
std : : cout < < " Moved: " < < std : : boolalpha < < moved < < std : : noboolalpha < < ' \n ' ;
2021-07-12 18:24:51 +02:00
if ( moved )
{
std : : cout < < " Moved refnum: " < < movedCellRef . mRefNum . mIndex < < ' \n ' ;
std : : cout < < " Moved content file: " < < movedCellRef . mRefNum . mContentFile < < ' \n ' ;
std : : cout < < " Target: " < < movedCellRef . mTarget [ 0 ] < < " , " < < movedCellRef . mTarget [ 1 ] < < ' \n ' ;
}
2012-04-08 11:51:52 +02:00
}
}
2010-02-19 09:12:49 +01:00
2022-06-19 13:28:33 +02:00
void printRawTes3 ( const std : : filesystem : : path & path )
2012-04-08 11:51:52 +02:00
{
2022-08-20 00:26:23 +02:00
std : : cout < < " TES3 RAW file listing: " < < Files : : pathToUnicodeString ( path ) < < ' \n ' ;
2022-04-14 16:46:57 +02:00
ESM : : ESMReader esm ;
esm . openRaw ( path ) ;
2012-04-08 11:51:52 +02:00
while ( esm . hasMoreRecs ( ) )
2010-02-19 09:12:49 +01:00
{
2012-09-30 23:34:53 +04:00
ESM : : NAME n = esm . getRecName ( ) ;
2022-02-25 17:40:27 +01:00
std : : cout < < " Record: " < < n . toStringView ( ) < < ' \n ' ;
2012-04-08 11:51:52 +02:00
esm . getRecHeader ( ) ;
while ( esm . hasMoreSubs ( ) )
{
2015-02-22 14:14:13 +01:00
size_t offs = esm . getFileOffset ( ) ;
2012-04-08 11:51:52 +02:00
esm . getSubName ( ) ;
esm . skipHSub ( ) ;
n = esm . retSubName ( ) ;
2014-12-18 03:24:10 +01:00
std : : ios : : fmtflags f ( std : : cout . flags ( ) ) ;
2022-02-25 17:40:27 +01:00
std : : cout < < " " < < n . toStringView ( ) < < " - " < < esm . getSubSize ( )
2021-06-30 21:59:47 +02:00
< < " bytes @ 0x " < < std : : hex < < offs < < ' \n ' ;
2014-12-18 03:24:10 +01:00
std : : cout . flags ( f ) ;
2012-04-08 11:51:52 +02:00
}
2010-02-19 09:12:49 +01:00
}
2012-04-08 11:51:52 +02:00
}
2010-02-19 09:12:49 +01:00
2022-04-14 16:46:57 +02:00
int loadTes3 ( const Arguments & info , std : : unique_ptr < std : : ifstream > & & stream , ESMData * data )
2012-04-08 11:51:52 +02:00
{
2022-07-03 00:02:29 +02:00
std : : cout < < " Loading TES3 file: " < < info . filename < < ' \n ' ;
2022-04-14 16:46:57 +02:00
2022-04-14 15:48:32 +02:00
ESM : : ESMReader esm ;
2013-01-06 01:37:58 +01:00
ToUTF8 : : Utf8Encoder encoder ( ToUTF8 : : calculateEncoding ( info . encoding ) ) ;
esm . setEncoder ( & encoder ) ;
2010-02-28 09:18:48 +01:00
2021-05-06 22:41:20 +02:00
std : : unordered_set < uint32_t > skipped ;
2010-02-18 13:15:43 +01:00
2022-04-14 16:46:57 +02:00
try
{
2012-04-08 11:51:52 +02:00
bool quiet = ( info . quiet_given | | info . mode = = " clone " ) ;
2012-04-08 17:52:55 +02:00
bool loadCells = ( info . loadcells_given | | info . mode = = " clone " ) ;
2012-04-08 11:51:52 +02:00
bool save = ( info . mode = = " clone " ) ;
2022-04-14 16:46:57 +02:00
esm . open ( std : : move ( stream ) , info . filename ) ;
2012-04-08 11:51:52 +02:00
2022-04-14 15:48:32 +02:00
if ( data ! = nullptr )
2022-05-01 14:46:47 +02:00
data - > mHeader = esm . getHeader ( ) ;
2012-04-08 11:51:52 +02:00
2012-04-08 17:04:52 +02:00
if ( ! quiet )
{
2021-06-30 21:59:47 +02:00
std : : cout < < " Author: " < < esm . getAuthor ( ) < < ' \n '
< < " Description: " < < esm . getDesc ( ) < < ' \n '
< < " File format version: " < < esm . getFVer ( ) < < ' \n ' ;
2020-06-10 10:30:37 +04:00
std : : vector < ESM : : Header : : MasterData > masterData = esm . getGameFiles ( ) ;
if ( ! masterData . empty ( ) )
2012-04-08 17:04:52 +02:00
{
2021-06-30 21:59:47 +02:00
std : : cout < < " Masters: " < < ' \n ' ;
2020-06-10 10:30:37 +04:00
for ( const auto & master : masterData )
2021-06-30 21:59:47 +02:00
std : : cout < < " " < < master . name < < " , " < < master . size < < " bytes \n " ;
2012-04-08 17:04:52 +02:00
}
}
2012-04-08 11:51:52 +02:00
// Loop through all records
while ( esm . hasMoreRecs ( ) )
2010-02-18 13:15:43 +01:00
{
2021-05-06 22:41:20 +02:00
const ESM : : NAME n = esm . getRecName ( ) ;
2012-04-14 00:14:04 +02:00
uint32_t flags ;
esm . getRecHeader ( flags ) ;
2022-01-29 21:09:27 +02:00
auto record = EsmTool : : RecordBase : : create ( n ) ;
2020-06-10 10:30:37 +04:00
if ( record = = nullptr )
2015-07-19 14:55:54 +03:00
{
2022-02-25 17:40:27 +01:00
if ( ! quiet & & skipped . count ( n . toInt ( ) ) = = 0 )
2015-07-19 14:55:54 +03:00
{
2022-02-25 17:40:27 +01:00
std : : cout < < " Skipping " < < n . toStringView ( ) < < " records. \n " ;
2021-10-17 02:52:22 +02:00
skipped . emplace ( n . toInt ( ) ) ;
2015-07-19 14:55:54 +03:00
}
esm . skipRecord ( ) ;
if ( quiet ) break ;
std : : cout < < " Skipping \n " ;
continue ;
}
record - > setFlags ( static_cast < int > ( flags ) ) ;
record - > setPrintPlain ( info . plain_given ) ;
record - > load ( esm ) ;
2012-10-09 19:41:45 -04:00
// Is the user interested in this record type?
bool interested = true ;
2022-04-14 15:48:32 +02:00
if ( ! info . types . empty ( ) & & std : : find ( info . types . begin ( ) , info . types . end ( ) , n . toStringView ( ) ) = = info . types . end ( ) )
interested = false ;
2012-10-09 01:33:47 -04:00
2015-07-19 14:55:54 +03:00
if ( ! info . name . empty ( ) & & ! Misc : : StringUtils : : ciEqual ( info . name , record - > getId ( ) ) )
2015-01-25 03:02:36 +01:00
interested = false ;
2012-10-09 01:33:47 -04:00
if ( ! quiet & & interested )
2015-07-19 14:55:54 +03:00
{
2022-04-17 09:00:58 +02:00
std : : cout < < " \n Record: " < < n . toStringView ( ) < < " ' " < < record - > getId ( ) < < " ' \n "
< < " Record flags: " < < recordFlags ( record - > getFlags ( ) ) < < ' \n ' ;
2015-07-19 14:55:54 +03:00
record - > print ( ) ;
}
2012-04-08 11:51:52 +02:00
2021-10-17 02:52:22 +02:00
if ( record - > getType ( ) . toInt ( ) = = ESM : : REC_CELL & & loadCells & & interested )
2015-07-19 14:55:54 +03:00
{
2022-04-14 15:48:32 +02:00
loadCell ( info , record - > cast < ESM : : Cell > ( ) - > get ( ) , esm , data ) ;
2015-07-19 14:55:54 +03:00
}
2012-04-14 00:14:04 +02:00
2022-04-14 15:48:32 +02:00
if ( data ! = nullptr )
2015-07-19 14:55:54 +03:00
{
2022-04-14 15:48:32 +02:00
if ( save )
data - > mRecords . push_back ( std : : move ( record ) ) ;
+ + data - > mRecordStats [ n . toInt ( ) ] ;
2012-04-08 11:51:52 +02:00
}
2010-02-18 13:15:43 +01:00
}
2022-04-14 15:48:32 +02:00
}
catch ( const std : : exception & e )
{
2012-09-30 23:34:53 +04:00
std : : cout < < " \n ERROR: \n \n " < < e . what ( ) < < std : : endl ;
2022-04-14 15:48:32 +02:00
if ( data ! = nullptr )
data - > mRecords . clear ( ) ;
2012-04-08 11:51:52 +02:00
return 1 ;
2010-03-04 15:42:59 +01:00
}
2012-04-08 11:51:52 +02:00
return 0 ;
2010-02-18 13:15:43 +01:00
}
2010-02-19 09:12:49 +01:00
2022-04-14 16:46:57 +02:00
int load ( const Arguments & info , ESMData * data )
{
if ( info . mRawFormat . has_value ( ) & & info . mode = = " dump " )
{
switch ( * info . mRawFormat )
{
case ESM : : Format : : Tes3 :
printRawTes3 ( info . filename ) ;
break ;
case ESM : : Format : : Tes4 :
2022-07-03 00:02:29 +02:00
std : : cout < < " Printing raw TES4 file is not supported: " < < Files : : pathToUnicodeString ( info . filename ) < < " \n " ;
2022-04-14 16:46:57 +02:00
break ;
}
return 0 ;
}
auto stream = Files : : openBinaryInputFileStream ( info . filename ) ;
if ( ! stream - > is_open ( ) )
{
std : : cout < < " Failed to open file: " < < std : : strerror ( errno ) < < ' \n ' ;
return - 1 ;
}
const ESM : : Format format = ESM : : readFormat ( * stream ) ;
stream - > seekg ( 0 ) ;
switch ( format )
{
case ESM : : Format : : Tes3 :
return loadTes3 ( info , std : : move ( stream ) , data ) ;
case ESM : : Format : : Tes4 :
if ( data ! = nullptr )
{
std : : cout < < " Collecting data from esm file is not supported for TES4 \n " ;
return - 1 ;
}
return loadTes4 ( info , std : : move ( stream ) ) ;
}
std : : cout < < " Unsupported ESM format: " < < ESM : : NAME ( format ) . toStringView ( ) < < ' \n ' ;
return - 1 ;
}
2012-04-08 11:51:52 +02:00
2022-04-14 15:48:32 +02:00
int clone ( const Arguments & info )
2010-03-04 15:42:59 +01:00
{
2012-04-08 11:51:52 +02:00
if ( info . outname . empty ( ) )
{
2012-09-30 23:34:53 +04:00
std : : cout < < " You need to specify an output name " < < std : : endl ;
2012-04-08 11:51:52 +02:00
return 1 ;
}
2010-03-04 15:42:59 +01:00
2022-04-14 15:48:32 +02:00
ESMData data ;
if ( load ( info , & data ) ! = 0 )
2010-03-04 15:42:59 +01:00
{
2012-09-30 23:34:53 +04:00
std : : cout < < " Failed to load, aborting. " < < std : : endl ;
2012-04-08 11:51:52 +02:00
return 1 ;
}
2010-03-04 15:42:59 +01:00
2022-04-14 15:48:32 +02:00
size_t recordCount = data . mRecords . size ( ) ;
2010-03-04 15:42:59 +01:00
2012-04-08 17:04:52 +02:00
int digitCount = 1 ; // For a nicer output
2016-04-25 20:01:06 +02:00
if ( recordCount > 0 )
2016-04-26 15:42:11 +02:00
digitCount = ( int ) std : : log10 ( recordCount ) + 1 ;
2012-04-08 17:04:52 +02:00
2021-06-30 21:59:47 +02:00
std : : cout < < " Loaded " < < recordCount < < " records: \n \n " ;
2012-04-08 11:51:52 +02:00
2012-04-08 17:04:52 +02:00
int i = 0 ;
2022-04-14 15:48:32 +02:00
for ( std : : pair < int , int > stat : data . mRecordStats )
2010-02-19 09:12:49 +01:00
{
2016-05-07 20:32:51 +03:00
ESM : : NAME name ;
2021-10-17 02:52:22 +02:00
name = stat . first ;
2018-12-02 15:48:25 +03:00
int amount = stat . second ;
2022-02-25 17:40:27 +01:00
std : : cout < < std : : setw ( digitCount ) < < amount < < " " < < name . toStringView ( ) < < " " ;
2012-04-08 17:04:52 +02:00
if ( + + i % 3 = = 0 )
2021-06-30 21:59:47 +02:00
std : : cout < < ' \n ' ;
2010-02-19 09:12:49 +01:00
}
2012-12-23 23:23:24 +04:00
2012-04-08 17:04:52 +02:00
if ( i % 3 ! = 0 )
2021-06-30 21:59:47 +02:00
std : : cout < < ' \n ' ;
2012-04-08 17:04:52 +02:00
2022-07-03 00:02:29 +02:00
std : : cout < < " \n Saving records to: " < < Files : : pathToUnicodeString ( info . outname ) < < " ... \n " ;
2012-04-08 17:04:52 +02:00
2022-04-14 15:48:32 +02:00
ESM : : ESMWriter esm ;
2013-01-06 01:37:58 +01:00
ToUTF8 : : Utf8Encoder encoder ( ToUTF8 : : calculateEncoding ( info . encoding ) ) ;
esm . setEncoder ( & encoder ) ;
2022-05-01 14:46:47 +02:00
esm . setHeader ( data . mHeader ) ;
esm . setVersion ( ESM : : VER_13 ) ;
2013-03-12 14:33:35 +01:00
esm . setRecordCount ( recordCount ) ;
2012-04-08 17:04:52 +02:00
2022-06-19 13:28:33 +02:00
std : : fstream save ( info . outname , std : : fstream : : out | std : : fstream : : binary ) ;
2012-04-08 17:04:52 +02:00
esm . save ( save ) ;
int saved = 0 ;
2022-04-14 15:48:32 +02:00
for ( auto & record : data . mRecords )
2012-04-08 17:04:52 +02:00
{
2018-12-02 15:48:25 +03:00
if ( i < = 0 )
break ;
2022-01-28 18:40:17 +01:00
const ESM : : NAME typeName = record - > getType ( ) ;
2012-04-08 17:04:52 +02:00
2022-01-28 18:40:17 +01:00
esm . startRecord ( typeName , record - > getFlags ( ) ) ;
2012-04-12 14:00:58 +02:00
2012-09-30 23:34:53 +04:00
record - > save ( esm ) ;
2021-10-17 02:52:22 +02:00
if ( typeName . toInt ( ) = = ESM : : REC_CELL ) {
2012-09-30 23:34:53 +04:00
ESM : : Cell * ptr = & record - > cast < ESM : : Cell > ( ) - > get ( ) ;
2022-04-14 15:48:32 +02:00
if ( ! data . mCellRefs [ ptr ] . empty ( ) )
2018-12-02 15:48:25 +03:00
{
2022-04-14 15:48:32 +02:00
for ( std : : pair < ESM : : CellRef , bool > & ref : data . mCellRefs [ ptr ] )
2018-12-02 15:48:25 +03:00
ref . first . save ( esm , ref . second ) ;
2012-04-08 17:52:55 +02:00
}
}
2022-01-28 18:40:17 +01:00
esm . endRecord ( typeName ) ;
2012-04-08 17:04:52 +02:00
saved + + ;
2018-11-13 23:07:01 +04:00
int perc = recordCount = = 0 ? 100 : ( int ) ( ( saved / ( float ) recordCount ) * 100 ) ;
2012-04-08 17:04:52 +02:00
if ( perc % 10 = = 0 )
2010-02-19 09:12:49 +01:00
{
2012-09-30 23:34:53 +04:00
std : : cerr < < " \r " < < perc < < " % " ;
2010-02-19 09:12:49 +01:00
}
}
2012-12-23 23:23:24 +04:00
2012-09-30 23:34:53 +04:00
std : : cout < < " \r Done! " < < std : : endl ;
2012-04-08 17:04:52 +02:00
esm . close ( ) ;
save . close ( ) ;
2012-04-08 11:51:52 +02:00
2012-04-12 14:00:58 +02:00
return 0 ;
}
2022-04-14 15:48:32 +02:00
int comp ( const Arguments & info )
2012-04-12 14:00:58 +02:00
{
if ( info . filename . empty ( ) | | info . outname . empty ( ) )
{
2012-09-30 23:34:53 +04:00
std : : cout < < " You need to specify two input files " < < std : : endl ;
2012-04-12 14:00:58 +02:00
return 1 ;
}
Arguments fileOne ;
Arguments fileTwo ;
fileOne . mode = " clone " ;
fileTwo . mode = " clone " ;
fileOne . encoding = info . encoding ;
fileTwo . encoding = info . encoding ;
2012-12-23 23:23:24 +04:00
2012-04-12 14:00:58 +02:00
fileOne . filename = info . filename ;
fileTwo . filename = info . outname ;
2022-04-14 15:48:32 +02:00
ESMData dataOne ;
if ( load ( fileOne , & dataOne ) ! = 0 )
2012-04-12 14:00:58 +02:00
{
2022-07-03 00:02:29 +02:00
std : : cout < < " Failed to load " < < Files : : pathToUnicodeString ( info . filename ) < < " , aborting comparison. " < < std : : endl ;
2012-04-12 14:00:58 +02:00
return 1 ;
}
2022-04-14 15:48:32 +02:00
ESMData dataTwo ;
if ( load ( fileTwo , & dataTwo ) ! = 0 )
2012-04-12 14:00:58 +02:00
{
2022-07-03 00:02:29 +02:00
std : : cout < < " Failed to load " < < Files : : pathToUnicodeString ( info . outname ) < < " , aborting comparison. " < < std : : endl ;
2012-04-12 14:00:58 +02:00
return 1 ;
}
2022-04-14 15:48:32 +02:00
if ( dataOne . mRecords . size ( ) ! = dataTwo . mRecords . size ( ) )
2012-04-12 14:00:58 +02:00
{
2012-09-30 23:34:53 +04:00
std : : cout < < " Not equal, different amount of records. " < < std : : endl ;
2012-04-12 14:00:58 +02:00
return 1 ;
}
2012-12-23 23:23:24 +04:00
2012-04-08 11:51:52 +02:00
return 0 ;
2010-02-19 09:12:49 +01:00
}
2022-04-14 16:46:57 +02:00
}