2012-01-27 08:47:14 -07:00
/*
2011-06-16 15:53:39 -06:00
https : //github.com/peterix/dfhack
2012-09-29 20:03:37 -06:00
Copyright ( c ) 2009 - 2012 Petr Mr á zek ( peterix @ gmail . com )
2011-06-16 15:53:39 -06:00
This software is provided ' as - is ' , without any express or implied
warranty . In no event will the authors be held liable for any
damages arising from the use of this software .
Permission is granted to anyone to use this software for any
purpose , including commercial applications , and to alter it and
redistribute it freely , subject to the following restrictions :
1. The origin of this software must not be misrepresented ; you must
not claim that you wrote the original software . If you use this
software in a product , an acknowledgment in the product documentation
would be appreciated but is not required .
2. Altered source versions must be plainly marked as such , and
must not be misrepresented as being the original software .
3. This notice may not be removed or altered from any source
distribution .
*/
2011-06-14 08:13:28 -06:00
# include "Internal.h"
# include <string>
# include <vector>
# include <map>
# include <set>
# include <cstdio>
# include <cstring>
2011-06-24 21:35:29 -06:00
# include <iterator>
# include <sstream>
2015-09-21 15:24:51 -06:00
# include <forward_list>
# include <type_traits>
# include <cstdarg>
2011-06-14 08:13:28 -06:00
using namespace std ;
2011-12-31 04:48:42 -07:00
# include "Error.h"
# include "MemAccess.h"
# include "Core.h"
# include "DataDefs.h"
2022-03-30 15:46:09 -06:00
# include "Debug.h"
2011-12-31 04:48:42 -07:00
# include "Console.h"
2018-07-08 17:10:01 -06:00
# include "MiscUtils.h"
2011-12-31 04:48:42 -07:00
# include "Module.h"
# include "VersionInfoFactory.h"
# include "VersionInfo.h"
# include "PluginManager.h"
2011-06-17 07:02:43 -06:00
# include "ModuleFactory.h"
2023-01-04 18:15:32 -07:00
# include "modules/DFSDL.h"
2012-12-14 19:05:38 -07:00
# include "modules/EventManager.h"
2015-01-28 17:15:58 -07:00
# include "modules/Filesystem.h"
2011-12-31 04:48:42 -07:00
# include "modules/Gui.h"
2023-01-03 02:01:30 -07:00
# include "modules/Textures.h"
2011-12-31 04:48:42 -07:00
# include "modules/World.h"
2018-08-26 17:26:09 -06:00
# include "modules/Persistence.h"
2012-03-14 09:57:29 -06:00
# include "RemoteServer.h"
2018-07-04 12:22:04 -06:00
# include "RemoteTools.h"
2012-04-15 09:09:25 -06:00
# include "LuaTools.h"
2015-02-19 08:31:58 -07:00
# include "DFHackVersion.h"
2012-04-15 09:09:25 -06:00
2012-05-04 09:47:18 -06:00
# include "MiscUtils.h"
2011-07-26 21:59:09 -06:00
using namespace DFHack ;
2011-07-09 03:33:58 -06:00
2023-01-05 18:11:01 -07:00
# include "df/plotinfost.h"
# include "df/gamest.h"
2011-12-31 04:48:42 -07:00
# include "df/world.h"
# include "df/world_data.h"
2012-03-15 12:00:47 -06:00
# include "df/interfacest.h"
2011-12-31 04:48:42 -07:00
# include "df/viewscreen_dwarfmodest.h"
2014-09-30 23:02:35 -06:00
# include "df/viewscreen_game_cleanerst.h"
2014-07-27 22:57:55 -06:00
# include "df/viewscreen_loadgamest.h"
2018-05-05 14:08:06 -06:00
# include "df/viewscreen_new_regionst.h"
2014-07-27 22:57:55 -06:00
# include "df/viewscreen_savegamest.h"
2012-02-28 04:59:02 -07:00
# include <df/graphic.h>
2011-12-30 07:12:15 -07:00
2011-06-16 18:09:03 -06:00
# include <stdio.h>
2011-06-26 20:49:56 -06:00
# include <iomanip>
2011-07-12 04:13:14 -06:00
# include <stdlib.h>
2011-08-01 18:21:25 -06:00
# include <fstream>
2018-06-12 08:33:12 -06:00
# include <thread>
# include <mutex>
# include <condition_variable>
2015-07-23 21:24:00 -06:00
# include "md5wrapper.h"
2011-06-14 08:13:28 -06:00
2012-06-13 18:15:43 -06:00
# include "SDL_events.h"
2018-02-03 21:59:01 -07:00
# ifdef LINUX_BUILD
# include <dlfcn.h>
# endif
2011-12-30 12:25:50 -07:00
using namespace df : : enums ;
2012-01-31 09:17:27 -07:00
using df : : global : : init ;
2012-05-17 10:38:27 -06:00
using df : : global : : world ;
2011-07-26 21:59:09 -06:00
2012-03-13 18:52:58 -06:00
// FIXME: A lot of code in one file, all doing different things... there's something fishy about it.
2012-05-19 11:31:42 -06:00
static bool parseKeySpec ( std : : string keyspec , int * psym , int * pmod , std : : string * pfocus = NULL ) ;
2015-09-15 05:27:42 -06:00
size_t loadScriptFiles ( Core * core , color_ostream & out , const vector < std : : string > & prefix , const std : : string & folder ) ;
2012-03-12 00:31:29 -06:00
2018-07-03 13:19:19 -06:00
namespace DFHack {
2022-03-30 15:46:09 -06:00
2022-11-07 13:17:47 -07:00
DBG_DECLARE ( core , keybinding , DebugCategory : : LINFO ) ;
2022-03-30 15:46:09 -06:00
DBG_DECLARE ( core , script , DebugCategory : : LINFO ) ;
2023-01-04 12:35:50 -07:00
static const std : : string CONFIG_PATH = " dfhack-config/ " ;
static const std : : string CONFIG_DEFAULTS_PATH = " hack/data/dfhack-config-defaults/ " ;
2018-07-10 09:23:59 -06:00
class MainThread {
public :
2018-07-03 13:19:19 -06:00
//! MainThread::suspend keeps the main DF thread suspended from Core::Init to
//! thread exit.
static CoreSuspenderBase & suspend ( ) {
static thread_local CoreSuspenderBase lock ( std : : defer_lock ) ;
return lock ;
}
} ;
2018-06-21 09:58:16 -06:00
}
2011-07-11 14:23:13 -06:00
2018-07-03 12:15:12 -06:00
CoreSuspendReleaseMain : : CoreSuspendReleaseMain ( )
{
MainThread : : suspend ( ) . unlock ( ) ;
}
CoreSuspendReleaseMain : : ~ CoreSuspendReleaseMain ( )
{
MainThread : : suspend ( ) . lock ( ) ;
}
CoreSuspendClaimMain : : CoreSuspendClaimMain ( )
{
MainThread : : suspend ( ) . lock ( ) ;
}
CoreSuspendClaimMain : : ~ CoreSuspendClaimMain ( )
{
MainThread : : suspend ( ) . unlock ( ) ;
2018-06-21 09:58:16 -06:00
}
2011-07-11 14:23:13 -06:00
2012-03-15 05:33:19 -06:00
struct Core : : Private
{
2018-06-12 08:53:43 -06:00
std : : thread iothread ;
2018-06-21 08:13:22 -06:00
std : : thread hotkeythread ;
2019-08-23 18:30:06 -06:00
bool last_autosave_request { false } ;
2019-08-23 18:45:35 -06:00
bool was_load_save { false } ;
2012-03-15 05:33:19 -06:00
} ;
2017-07-12 14:28:21 -06:00
struct CommandDepthCounter
{
static const int MAX_DEPTH = 20 ;
static thread_local int depth ;
CommandDepthCounter ( ) { depth + + ; }
~ CommandDepthCounter ( ) { depth - - ; }
bool ok ( ) { return depth < MAX_DEPTH ; }
} ;
thread_local int CommandDepthCounter : : depth = 0 ;
2012-04-15 08:40:19 -06:00
void Core : : cheap_tokenise ( string const & input , vector < string > & output )
2011-06-16 18:09:03 -06:00
{
2011-12-29 06:37:07 -07:00
string * cur = NULL ;
2012-09-24 09:13:33 -06:00
size_t i = 0 ;
2011-12-29 06:37:07 -07:00
2012-09-24 09:13:33 -06:00
// Check the first non-space character
while ( i < input . size ( ) & & isspace ( input [ i ] ) ) i + + ;
// Special verbatim argument mode?
if ( i < input . size ( ) & & input [ i ] = = ' : ' )
{
// Read the command
std : : string cmd ;
i + + ;
while ( i < input . size ( ) & & ! isspace ( input [ i ] ) )
cmd . push_back ( input [ i + + ] ) ;
if ( ! cmd . empty ( ) )
output . push_back ( cmd ) ;
// Find the argument
while ( i < input . size ( ) & & isspace ( input [ i ] ) ) i + + ;
if ( i < input . size ( ) )
output . push_back ( input . substr ( i ) ) ;
return ;
}
// Otherwise, parse in the regular quoted mode
for ( ; i < input . size ( ) ; i + + )
{
2012-02-29 17:29:55 -07:00
unsigned char c = input [ i ] ;
2011-12-29 06:37:07 -07:00
if ( isspace ( c ) ) {
cur = NULL ;
} else {
if ( ! cur ) {
output . push_back ( " " ) ;
cur = & output . back ( ) ;
}
if ( c = = ' " ' ) {
for ( i + + ; i < input . size ( ) ; i + + ) {
c = input [ i ] ;
if ( c = = ' " ' )
break ;
else if ( c = = ' \\ ' ) {
if ( + + i < input . size ( ) )
cur - > push_back ( input [ i ] ) ;
}
else
cur - > push_back ( c ) ;
}
} else {
cur - > push_back ( c ) ;
}
}
}
2011-06-19 20:29:38 -06:00
}
2011-06-16 15:53:39 -06:00
2011-06-24 21:35:29 -06:00
struct IODATA
{
Core * core ;
PluginManager * plug_mgr ;
} ;
2011-06-16 15:53:39 -06:00
2011-07-09 03:33:58 -06:00
// A thread function... for handling hotkeys. This is needed because
// all the plugin commands are expected to be run from foreign threads.
// Running them from one of the main DF threads will result in deadlock!
2011-07-26 21:59:09 -06:00
void fHKthread ( void * iodata )
2011-07-09 03:33:58 -06:00
{
Core * core = ( ( IODATA * ) iodata ) - > core ;
PluginManager * plug_mgr = ( ( IODATA * ) iodata ) - > plug_mgr ;
if ( plug_mgr = = 0 | | core = = 0 )
{
cerr < < " Hotkey thread has croaked. " < < endl ;
2011-07-26 21:59:09 -06:00
return ;
2011-07-09 03:33:58 -06:00
}
2018-06-21 08:13:22 -06:00
bool keep_going = true ;
while ( keep_going )
2011-07-09 03:33:58 -06:00
{
2018-06-21 08:13:22 -06:00
std : : string stuff = core - > getHotkeyCmd ( keep_going ) ; // waits on mutex!
2011-07-09 03:33:58 -06:00
if ( ! stuff . empty ( ) )
{
2012-03-10 04:55:42 -07:00
color_ostream_proxy out ( core - > getConsole ( ) ) ;
2012-05-04 09:47:18 -06:00
auto rv = core - > runCommand ( out , stuff ) ;
2011-12-30 07:27:55 -07:00
2012-05-04 09:47:18 -06:00
if ( rv = = CR_NOT_IMPLEMENTED )
out . printerr ( " Invalid hotkey command: '%s' \n " , stuff . c_str ( ) ) ;
2011-07-09 03:33:58 -06:00
}
}
}
2012-02-22 15:22:41 -07:00
struct sortable
{
bool recolor ;
string name ;
string description ;
//FIXME: Nuke when MSVC stops failing at being C++11 compliant
sortable ( bool recolor_ , const string & name_ , const string & description_ ) : recolor ( recolor_ ) , name ( name_ ) , description ( description_ ) { } ;
bool operator < ( const sortable & rhs ) const
{
if ( name < rhs . name )
return true ;
return false ;
} ;
} ;
2015-10-17 13:35:40 -06:00
static string dfhack_version_desc ( )
{
stringstream s ;
s < < Version : : dfhack_version ( ) < < " " ;
if ( Version : : is_release ( ) )
s < < " (release) " ;
else
s < < " (development build " < < Version : : git_description ( ) < < " ) " ;
2016-10-08 14:58:08 -06:00
s < < " on " < < ( sizeof ( void * ) = = 8 ? " x86_64 " : " x86 " ) ;
2018-07-11 09:47:55 -06:00
if ( strlen ( Version : : dfhack_build_id ( ) ) )
s < < " [build ID: " < < Version : : dfhack_build_id ( ) < < " ] " ;
2015-10-17 13:35:40 -06:00
return s . str ( ) ;
}
2012-05-04 09:47:18 -06:00
namespace {
struct ScriptArgs {
const string * pcmd ;
vector < string > * pargs ;
} ;
2015-03-10 16:29:36 -06:00
struct ScriptEnableState {
const string * pcmd ;
bool pstate ;
} ;
2012-05-04 09:47:18 -06:00
}
static bool init_run_script ( color_ostream & out , lua_State * state , void * info )
{
auto args = ( ScriptArgs * ) info ;
if ( ! lua_checkstack ( state , args - > pargs - > size ( ) + 10 ) )
return false ;
Lua : : PushDFHack ( state ) ;
lua_getfield ( state , - 1 , " run_script " ) ;
lua_remove ( state , - 2 ) ;
lua_pushstring ( state , args - > pcmd - > c_str ( ) ) ;
for ( size_t i = 0 ; i < args - > pargs - > size ( ) ; i + + )
lua_pushstring ( state , ( * args - > pargs ) [ i ] . c_str ( ) ) ;
return true ;
}
2012-06-14 02:46:12 -06:00
static command_result runLuaScript ( color_ostream & out , std : : string name , vector < string > & args )
2012-05-04 09:47:18 -06:00
{
ScriptArgs data ;
2012-06-14 02:46:12 -06:00
data . pcmd = & name ;
2012-05-04 09:47:18 -06:00
data . pargs = & args ;
bool ok = Lua : : RunCoreQueryLoop ( out , Lua : : Core : : State , init_run_script , & data ) ;
return ok ? CR_OK : CR_FAILURE ;
}
2015-03-10 16:29:36 -06:00
static bool init_enable_script ( color_ostream & out , lua_State * state , void * info )
{
auto args = ( ScriptEnableState * ) info ;
if ( ! lua_checkstack ( state , 4 ) )
return false ;
Lua : : PushDFHack ( state ) ;
lua_getfield ( state , - 1 , " enable_script " ) ;
lua_remove ( state , - 2 ) ;
lua_pushstring ( state , args - > pcmd - > c_str ( ) ) ;
lua_pushboolean ( state , args - > pstate ) ;
return true ;
}
static command_result enableLuaScript ( color_ostream & out , std : : string name , bool state )
{
ScriptEnableState data ;
data . pcmd = & name ;
data . pstate = state ;
bool ok = Lua : : RunCoreQueryLoop ( out , Lua : : Core : : State , init_enable_script , & data ) ;
return ok ? CR_OK : CR_FAILURE ;
}
2012-05-04 09:47:18 -06:00
command_result Core : : runCommand ( color_ostream & out , const std : : string & command )
2011-06-19 20:29:38 -06:00
{
2011-12-30 12:11:34 -07:00
if ( ! command . empty ( ) )
2011-06-25 00:05:17 -06:00
{
2011-07-18 08:22:49 -06:00
vector < string > parts ;
2012-04-15 08:40:19 -06:00
Core : : cheap_tokenise ( command , parts ) ;
2011-07-18 08:22:49 -06:00
if ( parts . size ( ) = = 0 )
2012-05-04 09:47:18 -06:00
return CR_NOT_IMPLEMENTED ;
2011-07-18 08:22:49 -06:00
string first = parts [ 0 ] ;
parts . erase ( parts . begin ( ) ) ;
2011-12-30 12:11:34 -07:00
2012-05-04 09:47:18 -06:00
if ( first [ 0 ] = = ' # ' )
return CR_OK ;
2011-12-30 12:11:34 -07:00
2011-11-04 02:08:29 -06:00
cerr < < " Invoking: " < < command < < endl ;
2012-05-04 09:47:18 -06:00
return runCommand ( out , first , parts ) ;
}
else
return CR_NOT_IMPLEMENTED ;
}
2022-07-14 14:19:30 -06:00
bool is_builtin ( color_ostream & con , const string & command ) {
CoreSuspender suspend ;
auto L = Lua : : Core : : State ;
Lua : : StackUnwinder top ( L ) ;
2017-11-27 14:45:58 -07:00
2022-07-14 14:19:30 -06:00
if ( ! lua_checkstack ( L , 1 ) | |
! Lua : : PushModulePublic ( con , L , " helpdb " , " is_builtin " ) ) {
con . printerr ( " Failed to load helpdb Lua code \n " ) ;
return false ;
}
2012-12-06 17:16:40 -07:00
2022-07-14 14:19:30 -06:00
Lua : : Push ( L , command ) ;
2017-11-27 14:10:09 -07:00
2022-07-14 14:19:30 -06:00
if ( ! Lua : : SafeCall ( con , L , 1 , 1 ) ) {
con . printerr ( " Failed Lua call to helpdb.is_builtin. \n " ) ;
return false ;
2012-12-06 17:16:40 -07:00
}
2022-07-14 14:19:30 -06:00
return lua_toboolean ( L , - 1 ) ;
}
void get_commands ( color_ostream & con , vector < string > & commands ) {
CoreSuspender suspend ;
auto L = Lua : : Core : : State ;
Lua : : StackUnwinder top ( L ) ;
if ( ! lua_checkstack ( L , 1 ) | |
! Lua : : PushModulePublic ( con , L , " helpdb " , " get_commands " ) ) {
con . printerr ( " Failed to load helpdb Lua code \n " ) ;
return ;
}
if ( ! Lua : : SafeCall ( con , L , 0 , 1 ) ) {
con . printerr ( " Failed Lua call to helpdb.get_commands. \n " ) ;
}
Lua : : GetVector ( L , commands ) ;
}
2012-12-06 17:16:40 -07:00
2022-07-14 14:19:30 -06:00
static bool try_autocomplete ( color_ostream & con , const std : : string & first , std : : string & completed )
{
std : : vector < std : : string > commands , possible ;
for ( auto & command : commands )
if ( command . substr ( 0 , first . size ( ) ) = = first )
possible . push_back ( command ) ;
2012-12-06 17:16:40 -07:00
if ( possible . size ( ) = = 1 )
{
completed = possible [ 0 ] ;
2013-03-13 23:43:38 -06:00
//fprintf(stderr, "Autocompleted %s to %s\n", , );
con . printerr ( " %s is not recognized. Did you mean %s? \n " , first . c_str ( ) , completed . c_str ( ) ) ;
2012-12-06 17:16:40 -07:00
return true ;
}
if ( possible . size ( ) > 1 & & possible . size ( ) < 8 )
{
std : : string out ;
for ( size_t i = 0 ; i < possible . size ( ) ; i + + )
out + = " " + possible [ i ] ;
2013-03-13 23:43:38 -06:00
con . printerr ( " %s is not recognized. Possible completions:%s \n " , first . c_str ( ) , out . c_str ( ) ) ;
return true ;
2012-12-06 17:16:40 -07:00
}
return false ;
}
2015-09-06 14:23:02 -06:00
bool Core : : addScriptPath ( string path , bool search_before )
{
2018-06-12 08:33:12 -06:00
lock_guard < mutex > lock ( script_path_mutex ) ;
2015-09-06 14:23:02 -06:00
vector < string > & vec = script_paths [ search_before ? 0 : 1 ] ;
if ( std : : find ( vec . begin ( ) , vec . end ( ) , path ) ! = vec . end ( ) )
return false ;
if ( ! Filesystem : : isdir ( path ) )
return false ;
vec . push_back ( path ) ;
return true ;
}
bool Core : : removeScriptPath ( string path )
{
2018-06-12 08:33:12 -06:00
lock_guard < mutex > lock ( script_path_mutex ) ;
2015-09-06 14:23:02 -06:00
bool found = false ;
for ( int i = 0 ; i < 2 ; i + + )
{
vector < string > & vec = script_paths [ i ] ;
while ( 1 )
{
auto it = std : : find ( vec . begin ( ) , vec . end ( ) , path ) ;
if ( it = = vec . end ( ) )
break ;
vec . erase ( it ) ;
found = true ;
2014-07-07 06:50:40 -06:00
}
}
2015-09-06 14:23:02 -06:00
return found ;
}
void Core : : getScriptPaths ( std : : vector < std : : string > * dest )
{
2018-06-12 08:33:12 -06:00
lock_guard < mutex > lock ( script_path_mutex ) ;
2015-09-06 14:23:02 -06:00
dest - > clear ( ) ;
2023-01-04 12:35:50 -07:00
string df_path = this - > p - > getPath ( ) + " / " ;
2015-09-06 14:23:02 -06:00
for ( auto it = script_paths [ 0 ] . begin ( ) ; it ! = script_paths [ 0 ] . end ( ) ; + + it )
dest - > push_back ( * it ) ;
2023-01-04 12:35:50 -07:00
dest - > push_back ( df_path + CONFIG_PATH + " scripts " ) ;
2015-09-26 12:26:13 -06:00
if ( df : : global : : world & & isWorldLoaded ( ) ) {
2015-09-06 14:23:02 -06:00
string save = World : : ReadWorldFolder ( ) ;
if ( save . size ( ) )
2023-01-16 00:13:58 -07:00
dest - > push_back ( df_path + " /save/ " + save + " /scripts " ) ;
2014-07-07 06:50:40 -06:00
}
2015-09-06 14:23:02 -06:00
dest - > push_back ( df_path + " /hack/scripts " ) ;
for ( auto it = script_paths [ 1 ] . begin ( ) ; it ! = script_paths [ 1 ] . end ( ) ; + + it )
dest - > push_back ( * it ) ;
}
string Core : : findScript ( string name )
{
vector < string > paths ;
getScriptPaths ( & paths ) ;
for ( auto it = paths . begin ( ) ; it ! = paths . end ( ) ; + + it )
{
string path = * it + " / " + name ;
if ( Filesystem : : isfile ( path ) )
return path ;
2014-07-07 06:50:40 -06:00
}
return " " ;
}
2016-03-27 13:56:23 -06:00
bool loadScriptPaths ( color_ostream & out , bool silent = false )
{
using namespace std ;
2023-01-04 12:35:50 -07:00
string filename ( CONFIG_PATH + " script-paths.txt " ) ;
2016-03-27 13:56:23 -06:00
ifstream file ( filename ) ;
if ( ! file )
{
if ( ! silent )
out . printerr ( " Could not load %s \n " , filename . c_str ( ) ) ;
return false ;
}
string raw ;
int line = 0 ;
while ( getline ( file , raw ) )
{
+ + line ;
istringstream ss ( raw ) ;
char ch ;
ss > > skipws ;
if ( ! ( ss > > ch ) | | ch = = ' # ' )
continue ;
ss > > ws ; // discard whitespace
string path ;
getline ( ss , path ) ;
if ( ch = = ' + ' | | ch = = ' - ' )
{
if ( ! Core : : getInstance ( ) . addScriptPath ( path , ch = = ' + ' ) & & ! silent )
out . printerr ( " %s:%i: Failed to add path: %s \n " , filename . c_str ( ) , line , path . c_str ( ) ) ;
}
else if ( ! silent )
out . printerr ( " %s:%i: Illegal character: %c \n " , filename . c_str ( ) , line , ch ) ;
}
return true ;
}
2015-02-07 17:33:12 -07:00
static std : : map < std : : string , state_change_event > state_change_event_map ;
static void sc_event_map_init ( ) {
if ( ! state_change_event_map . size ( ) )
{
# define insert(name) state_change_event_map.insert(std::pair<std::string, state_change_event>(#name, name))
insert ( SC_WORLD_LOADED ) ;
insert ( SC_WORLD_UNLOADED ) ;
insert ( SC_MAP_LOADED ) ;
insert ( SC_MAP_UNLOADED ) ;
insert ( SC_VIEWSCREEN_CHANGED ) ;
insert ( SC_PAUSED ) ;
insert ( SC_UNPAUSED ) ;
# undef insert
}
}
static state_change_event sc_event_id ( std : : string name ) {
sc_event_map_init ( ) ;
auto it = state_change_event_map . find ( name ) ;
if ( it ! = state_change_event_map . end ( ) )
return it - > second ;
if ( name . find ( " SC_ " ) ! = 0 )
return sc_event_id ( std : : string ( " SC_ " ) + name ) ;
return SC_UNKNOWN ;
}
static std : : string sc_event_name ( state_change_event id ) {
sc_event_map_init ( ) ;
for ( auto it = state_change_event_map . begin ( ) ; it ! = state_change_event_map . end ( ) ; + + it )
{
if ( it - > second = = id )
return it - > first ;
}
return " SC_UNKNOWN " ;
}
2022-07-08 16:53:51 -06:00
void help_helper ( color_ostream & con , const string & entry_name ) {
CoreSuspender suspend ;
auto L = Lua : : Core : : State ;
Lua : : StackUnwinder top ( L ) ;
if ( ! lua_checkstack ( L , 2 ) | |
! Lua : : PushModulePublic ( con , L , " helpdb " , " help " ) ) {
con . printerr ( " Failed to load helpdb Lua code \n " ) ;
return ;
}
Lua : : Push ( L , entry_name ) ;
if ( ! Lua : : SafeCall ( con , L , 1 , 0 ) ) {
con . printerr ( " Failed Lua call to helpdb.help. \n " ) ;
}
2016-06-14 19:24:27 -06:00
}
2022-09-13 23:23:04 -06:00
void tags_helper ( color_ostream & con , const string & tag ) {
2022-07-08 16:53:51 -06:00
CoreSuspender suspend ;
auto L = Lua : : Core : : State ;
Lua : : StackUnwinder top ( L ) ;
if ( ! lua_checkstack ( L , 1 ) | |
! Lua : : PushModulePublic ( con , L , " helpdb " , " tags " ) ) {
con . printerr ( " Failed to load helpdb Lua code \n " ) ;
return ;
}
2022-09-13 23:23:04 -06:00
Lua : : Push ( L , tag ) ;
if ( ! Lua : : SafeCall ( con , L , 1 , 0 ) ) {
2022-07-08 16:53:51 -06:00
con . printerr ( " Failed Lua call to helpdb.tags. \n " ) ;
}
}
void ls_helper ( color_ostream & con , const vector < string > & params ) {
vector < string > filter ;
bool skip_tags = false ;
bool show_dev_commands = false ;
2022-10-05 14:30:14 -06:00
string exclude_strs = " " ;
2022-07-08 16:53:51 -06:00
2022-10-05 14:30:14 -06:00
bool in_exclude = false ;
2022-07-08 16:53:51 -06:00
for ( auto str : params ) {
2022-10-05 14:30:14 -06:00
if ( in_exclude )
exclude_strs = str ;
else if ( str = = " --notags " )
2022-07-08 16:53:51 -06:00
skip_tags = true ;
else if ( str = = " --dev " )
show_dev_commands = true ;
2022-10-05 14:30:14 -06:00
else if ( str = = " --exclude " )
in_exclude = true ;
2022-07-08 16:53:51 -06:00
else
filter . push_back ( str ) ;
}
CoreSuspender suspend ;
auto L = Lua : : Core : : State ;
Lua : : StackUnwinder top ( L ) ;
2022-10-05 14:30:14 -06:00
if ( ! lua_checkstack ( L , 5 ) | |
2022-07-08 16:53:51 -06:00
! Lua : : PushModulePublic ( con , L , " helpdb " , " ls " ) ) {
con . printerr ( " Failed to load helpdb Lua code \n " ) ;
return ;
}
Lua : : PushVector ( L , filter ) ;
Lua : : Push ( L , skip_tags ) ;
Lua : : Push ( L , show_dev_commands ) ;
2022-10-05 14:30:14 -06:00
Lua : : Push ( L , exclude_strs ) ;
2022-07-08 16:53:51 -06:00
2022-10-05 14:30:14 -06:00
if ( ! Lua : : SafeCall ( con , L , 4 , 0 ) ) {
2022-07-08 16:53:51 -06:00
con . printerr ( " Failed Lua call to helpdb.ls. \n " ) ;
}
2016-06-14 19:24:27 -06:00
}
2015-03-06 14:53:57 -07:00
command_result Core : : runCommand ( color_ostream & con , const std : : string & first_ , vector < string > & parts )
2012-05-04 09:47:18 -06:00
{
2015-03-06 14:53:57 -07:00
std : : string first = first_ ;
2017-07-12 14:28:21 -06:00
CommandDepthCounter counter ;
if ( ! counter . ok ( ) )
{
con . printerr ( " Cannot invoke \" %s \" : maximum command depth exceeded (%i) \n " ,
first . c_str ( ) , CommandDepthCounter : : MAX_DEPTH ) ;
return CR_FAILURE ;
}
2022-07-14 14:19:30 -06:00
if ( first . empty ( ) )
return CR_NOT_IMPLEMENTED ;
if ( first . find ( ' \\ ' ) ! = std : : string : : npos )
2012-05-04 09:47:18 -06:00
{
2022-07-14 14:19:30 -06:00
con . printerr ( " Replacing backslashes with forward slashes in \" %s \" \n " , first . c_str ( ) ) ;
for ( size_t i = 0 ; i < first . size ( ) ; i + + )
2015-02-27 16:46:29 -07:00
{
2022-07-14 14:19:30 -06:00
if ( first [ i ] = = ' \\ ' )
first [ i ] = ' / ' ;
2015-02-27 16:46:29 -07:00
}
2022-07-14 14:19:30 -06:00
}
2015-08-14 14:11:23 -06:00
2022-07-14 14:19:30 -06:00
// let's see what we actually got
command_result res ;
if ( first = = " help " | | first = = " man " | | first = = " ? " )
{
if ( ! parts . size ( ) )
2011-07-18 08:22:49 -06:00
{
2022-07-14 14:19:30 -06:00
if ( con . is_console ( ) )
2011-07-18 08:22:49 -06:00
{
2022-07-14 14:19:30 -06:00
con . print ( " This is the DFHack console. You can type commands in and manage DFHack plugins from it. \n "
" Some basic editing capabilities are included (single-line text editing). \n "
" The console also has a command history - you can navigate it with Up and Down keys. \n "
" On Windows, you may have to resize your console window. The appropriate menu is accessible \n "
" by clicking on the program icon in the top bar of the window. \n \n " ) ;
2011-07-18 08:22:49 -06:00
}
2022-07-14 14:19:30 -06:00
con . print ( " Here are some basic commands to get you started: \n "
" help|?|man - This text. \n "
" help <tool> - Usage help for the given plugin, command, or script. \n "
" tags - List the tags that the DFHack tools are grouped by. \n "
" ls|dir [<filter>] - List commands, optionally filtered by a tag or substring. \n "
" Optional parameters: \n "
" --notags: skip printing tags for each command. \n "
" --dev: include commands intended for developers and modders. \n "
" cls|clear - Clear the console. \n "
" fpause - Force DF to pause. \n "
" die - Force DF to close immediately, without saving. \n "
" keybinding - Modify bindings of commands to in-game key shortcuts. \n "
" \n "
" See more commands by running 'ls'. \n \n "
) ;
con . print ( " DFHack version %s \n " , dfhack_version_desc ( ) . c_str ( ) ) ;
2011-07-18 08:22:49 -06:00
}
2022-07-14 14:19:30 -06:00
else
2022-07-08 16:53:51 -06:00
{
2022-07-14 14:19:30 -06:00
help_helper ( con , parts [ 0 ] ) ;
2022-07-08 16:53:51 -06:00
}
2022-07-14 14:19:30 -06:00
}
else if ( first = = " tags " )
{
2022-09-13 23:23:04 -06:00
tags_helper ( con , parts . size ( ) ? parts [ 0 ] : " " ) ;
2022-07-14 14:19:30 -06:00
}
else if ( first = = " load " | | first = = " unload " | | first = = " reload " )
{
bool all = false ;
bool load = ( first = = " load " ) ;
bool unload = ( first = = " unload " ) ;
if ( parts . size ( ) )
2011-07-18 08:22:49 -06:00
{
2022-07-14 14:19:30 -06:00
for ( auto p = parts . begin ( ) ; p ! = parts . end ( ) ; p + + )
2011-07-18 08:22:49 -06:00
{
2022-07-14 14:19:30 -06:00
if ( p - > size ( ) & & ( * p ) [ 0 ] = = ' - ' )
2011-07-18 08:22:49 -06:00
{
2022-07-14 14:19:30 -06:00
if ( p - > find ( ' a ' ) ! = string : : npos )
all = true ;
2011-07-18 08:22:49 -06:00
}
}
2022-07-14 14:19:30 -06:00
if ( all )
{
if ( load )
plug_mgr - > loadAll ( ) ;
else if ( unload )
plug_mgr - > unloadAll ( ) ;
else
plug_mgr - > reloadAll ( ) ;
return CR_OK ;
}
for ( auto p = parts . begin ( ) ; p ! = parts . end ( ) ; p + + )
{
if ( ! p - > size ( ) | | ( * p ) [ 0 ] = = ' - ' )
continue ;
if ( load )
plug_mgr - > load ( * p ) ;
else if ( unload )
plug_mgr - > unload ( * p ) ;
else
plug_mgr - > reload ( * p ) ;
}
2011-07-18 08:22:49 -06:00
}
2022-07-14 14:19:30 -06:00
else
con . printerr ( " %s: no arguments \n " , first . c_str ( ) ) ;
}
else if ( first = = " enable " | | first = = " disable " )
{
CoreSuspender suspend ;
bool enable = ( first = = " enable " ) ;
2013-09-30 03:19:51 -06:00
2022-07-14 14:19:30 -06:00
if ( parts . size ( ) )
{
for ( size_t i = 0 ; i < parts . size ( ) ; i + + )
2013-09-30 03:19:51 -06:00
{
2022-07-14 14:19:30 -06:00
std : : string part = parts [ i ] ;
if ( part . find ( ' \\ ' ) ! = std : : string : : npos )
2013-09-30 03:19:51 -06:00
{
2022-07-14 14:19:30 -06:00
con . printerr ( " Replacing backslashes with forward slashes in \" %s \" \n " , part . c_str ( ) ) ;
for ( size_t j = 0 ; j < part . size ( ) ; j + + )
2015-03-15 14:54:18 -06:00
{
2022-07-14 14:19:30 -06:00
if ( part [ j ] = = ' \\ ' )
part [ j ] = ' / ' ;
2015-03-15 14:54:18 -06:00
}
2022-07-14 14:19:30 -06:00
}
2015-03-15 14:54:18 -06:00
2022-07-14 14:19:30 -06:00
Plugin * plug = ( * plug_mgr ) [ part ] ;
2013-09-30 03:19:51 -06:00
2022-07-14 14:19:30 -06:00
if ( ! plug )
{
std : : string lua = findScript ( part + " .lua " ) ;
if ( lua . size ( ) )
2013-09-30 03:19:51 -06:00
{
2022-07-14 14:19:30 -06:00
res = enableLuaScript ( con , part , enable ) ;
2013-09-30 03:19:51 -06:00
}
else
{
2022-07-14 14:19:30 -06:00
res = CR_NOT_FOUND ;
con . printerr ( " No such plugin or Lua script: %s \n " , part . c_str ( ) ) ;
2013-09-30 03:19:51 -06:00
}
}
2022-07-14 14:19:30 -06:00
else if ( ! plug - > can_set_enabled ( ) )
{
res = CR_NOT_IMPLEMENTED ;
con . printerr ( " Cannot %s plugin: %s \n " , first . c_str ( ) , part . c_str ( ) ) ;
}
else
2013-09-30 03:19:51 -06:00
{
2022-07-14 14:19:30 -06:00
res = plug - > set_enabled ( con , enable ) ;
if ( res ! = CR_OK | | plug - > is_enabled ( ) ! = enable )
con . printerr ( " Could not %s plugin: %s \n " , first . c_str ( ) , part . c_str ( ) ) ;
2013-09-30 03:19:51 -06:00
}
}
2022-07-14 14:19:30 -06:00
return res ;
2013-09-30 03:19:51 -06:00
}
2022-07-14 14:19:30 -06:00
else
2011-06-16 15:53:39 -06:00
{
2015-08-14 17:32:41 -06:00
for ( auto it = plug_mgr - > begin ( ) ; it ! = plug_mgr - > end ( ) ; + + it )
2011-06-26 20:49:56 -06:00
{
2015-10-03 11:42:20 -06:00
Plugin * plug = it - > second ;
2022-07-14 14:19:30 -06:00
if ( ! plug - > can_be_enabled ( ) ) continue ;
con . print (
2023-01-24 00:50:08 -07:00
" %21s %-3s%s \n " ,
2022-07-14 14:19:30 -06:00
( plug - > getName ( ) + " : " ) . c_str ( ) ,
plug - > is_enabled ( ) ? " on " : " off " ,
2022-12-05 18:08:50 -07:00
plug - > can_set_enabled ( ) ? " " : " (controlled internally) "
2015-08-14 14:11:23 -06:00
) ;
}
2022-12-05 18:08:50 -07:00
auto L = Lua : : Core : : State ;
Lua : : StackUnwinder top ( L ) ;
2022-12-07 14:26:35 -07:00
Lua : : CallLuaModuleFunction ( con , L , " script-manager " , " list " ) ;
2015-08-14 14:11:23 -06:00
}
2022-07-14 14:19:30 -06:00
}
else if ( first = = " ls " | | first = = " dir " )
{
ls_helper ( con , parts ) ;
}
else if ( first = = " plug " )
{
const char * header_format = " %30s %10s %4s %8s \n " ;
const char * row_format = " %30s %10s %4i %8s \n " ;
con . print ( header_format , " Name " , " State " , " Cmds " , " Enabled " ) ;
plug_mgr - > refresh ( ) ;
for ( auto it = plug_mgr - > begin ( ) ; it ! = plug_mgr - > end ( ) ; + + it )
2015-08-14 14:11:23 -06:00
{
2022-07-14 14:19:30 -06:00
Plugin * plug = it - > second ;
if ( ! plug )
continue ;
if ( parts . size ( ) & & std : : find ( parts . begin ( ) , parts . end ( ) , plug - > getName ( ) ) = = parts . end ( ) )
continue ;
color_value color ;
switch ( plug - > getState ( ) )
2015-08-14 14:11:23 -06:00
{
2022-07-14 14:19:30 -06:00
case Plugin : : PS_LOADED :
color = COLOR_RESET ;
break ;
case Plugin : : PS_UNLOADED :
case Plugin : : PS_UNLOADING :
color = COLOR_YELLOW ;
break ;
case Plugin : : PS_LOADING :
color = COLOR_LIGHTBLUE ;
break ;
case Plugin : : PS_BROKEN :
color = COLOR_LIGHTRED ;
break ;
default :
color = COLOR_LIGHTMAGENTA ;
break ;
2011-06-26 20:49:56 -06:00
}
2022-07-14 14:19:30 -06:00
con . color ( color ) ;
con . print ( row_format ,
plug - > getName ( ) . c_str ( ) ,
Plugin : : getStateDescription ( plug - > getState ( ) ) ,
plug - > size ( ) ,
( plug - > can_be_enabled ( )
? ( plug - > is_enabled ( ) ? " enabled " : " disabled " )
: " n/a " )
) ;
con . color ( COLOR_RESET ) ;
2011-06-16 15:53:39 -06:00
}
2022-07-14 14:19:30 -06:00
}
else if ( first = = " type " )
{
if ( ! parts . size ( ) )
2011-12-30 12:25:50 -07:00
{
2022-07-14 14:19:30 -06:00
con . printerr ( " type: no argument \n " ) ;
return CR_WRONG_USAGE ;
2011-12-30 12:25:50 -07:00
}
2022-07-14 14:19:30 -06:00
con < < parts [ 0 ] ;
bool builtin = is_builtin ( con , parts [ 0 ] ) ;
string lua_path = findScript ( parts [ 0 ] + " .lua " ) ;
Plugin * plug = plug_mgr - > getPluginByCommand ( parts [ 0 ] ) ;
if ( builtin )
2017-06-18 15:39:54 -06:00
{
2022-07-14 14:19:30 -06:00
con < < " is a built-in command " ;
con < < std : : endl ;
}
else if ( IsAlias ( parts [ 0 ] ) )
{
con < < " is an alias: " < < GetAliasCommand ( parts [ 0 ] ) < < std : : endl ;
}
else if ( plug )
{
con < < " is a command implemented by the plugin " < < plug - > getName ( ) < < std : : endl ;
}
else if ( lua_path . size ( ) )
{
con < < " is a Lua script: " < < lua_path < < std : : endl ;
}
else
{
con < < " is not a recognized command. " < < std : : endl ;
plug = plug_mgr - > getPluginByName ( parts [ 0 ] ) ;
if ( plug )
con < < " Plugin " < < parts [ 0 ] < < " exists and implements " < < plug - > size ( ) < < " commands. " < < std : : endl ;
return CR_FAILURE ;
}
}
else if ( first = = " keybinding " )
{
if ( parts . size ( ) > = 3 & & ( parts [ 0 ] = = " set " | | parts [ 0 ] = = " add " ) )
{
std : : string keystr = parts [ 1 ] ;
if ( parts [ 0 ] = = " set " )
ClearKeyBindings ( keystr ) ;
for ( int i = parts . size ( ) - 1 ; i > = 2 ; i - - )
2017-06-18 15:39:54 -06:00
{
2022-07-14 14:19:30 -06:00
if ( ! AddKeyBinding ( keystr , parts [ i ] ) ) {
con . printerr ( " Invalid key spec: %s \n " , keystr . c_str ( ) ) ;
break ;
2017-06-18 15:39:54 -06:00
}
}
2022-07-14 14:19:30 -06:00
}
else if ( parts . size ( ) > = 2 & & parts [ 0 ] = = " clear " )
{
for ( size_t i = 1 ; i < parts . size ( ) ; i + + )
2017-06-18 15:39:54 -06:00
{
2022-07-14 14:19:30 -06:00
if ( ! ClearKeyBindings ( parts [ i ] ) ) {
con . printerr ( " Invalid key spec: %s \n " , parts [ i ] . c_str ( ) ) ;
break ;
2017-06-18 15:39:54 -06:00
}
}
2022-07-14 14:19:30 -06:00
}
else if ( parts . size ( ) = = 2 & & parts [ 0 ] = = " list " )
{
std : : vector < std : : string > list = ListKeyBindings ( parts [ 1 ] ) ;
if ( list . empty ( ) )
con < < " No bindings. " < < endl ;
for ( size_t i = 0 ; i < list . size ( ) ; i + + )
con < < " " < < list [ i ] < < endl ;
}
else
{
con < < " Usage: " < < endl
< < " keybinding list <key> " < < endl
< < " keybinding clear <key>[@context]... " < < endl
< < " keybinding set <key>[@context] \" cmdline \" \" cmdline \" ... " < < endl
< < " keybinding add <key>[@context] \" cmdline \" \" cmdline \" ... " < < endl
< < " Later adds, and earlier items within one command have priority. " < < endl
2022-10-19 17:24:21 -06:00
< < " Supported keys: [Ctrl-][Alt-][Shift-](A-Z, 0-9, F1-F12, `, or Enter). " < < endl
2022-07-14 14:19:30 -06:00
< < " Context may be used to limit the scope of the binding, by " < < endl
< < " requiring the current context to have a certain prefix. " < < endl
2023-01-27 11:22:52 -07:00
< < " Current UI context is: " < < endl
2023-02-01 15:22:56 -07:00
< < join_strings ( " \n " , Gui : : getFocusStrings ( Gui : : getCurViewscreen ( true ) ) ) < < endl ;
2022-07-14 14:19:30 -06:00
}
}
else if ( first = = " alias " )
{
if ( parts . size ( ) > = 3 & & ( parts [ 0 ] = = " add " | | parts [ 0 ] = = " replace " ) )
{
const string & name = parts [ 1 ] ;
vector < string > cmd ( parts . begin ( ) + 2 , parts . end ( ) ) ;
if ( ! AddAlias ( name , cmd , parts [ 0 ] = = " replace " ) )
2017-06-18 15:39:54 -06:00
{
2022-07-14 14:19:30 -06:00
con . printerr ( " Could not add alias %s - already exists \n " , name . c_str ( ) ) ;
return CR_FAILURE ;
2017-06-18 15:39:54 -06:00
}
}
2022-07-14 14:19:30 -06:00
else if ( parts . size ( ) > = 2 & & ( parts [ 0 ] = = " delete " | | parts [ 0 ] = = " clear " ) )
2011-07-18 08:22:49 -06:00
{
2022-07-14 14:19:30 -06:00
if ( ! RemoveAlias ( parts [ 1 ] ) )
2018-05-05 14:08:06 -06:00
{
2022-07-14 14:19:30 -06:00
con . printerr ( " Could not remove alias %s \n " , parts [ 1 ] . c_str ( ) ) ;
return CR_FAILURE ;
2018-05-05 14:08:06 -06:00
}
2011-07-18 08:22:49 -06:00
}
2022-07-14 14:19:30 -06:00
else if ( parts . size ( ) > = 1 & & ( parts [ 0 ] = = " list " ) )
2011-06-22 00:14:21 -06:00
{
2022-07-14 14:19:30 -06:00
auto aliases = ListAliases ( ) ;
for ( auto p : aliases )
2012-05-04 09:47:18 -06:00
{
2022-07-14 14:19:30 -06:00
con < < p . first < < " : " < < join_strings ( " " , p . second ) < < endl ;
2012-05-04 09:47:18 -06:00
}
2011-06-22 00:14:21 -06:00
}
2022-07-14 14:19:30 -06:00
else
2011-07-20 12:58:19 -06:00
{
2022-07-14 14:19:30 -06:00
con < < " Usage: " < < endl
< < " alias add|replace <name> <command...> " < < endl
< < " alias delete|clear <name> <command...> " < < endl
< < " alias list " < < endl ;
2011-07-20 12:58:19 -06:00
}
2022-07-14 14:19:30 -06:00
}
else if ( first = = " fpause " )
{
World : : SetPauseState ( true ) ;
2022-12-20 20:04:30 -07:00
/* TODO: understand how this changes for v50
2022-07-14 14:19:30 -06:00
if ( auto scr = Gui : : getViewscreenByType < df : : viewscreen_new_regionst > ( ) )
2015-05-24 17:06:01 -06:00
{
2022-07-14 14:19:30 -06:00
scr - > worldgen_paused = true ;
2015-05-24 17:06:01 -06:00
}
2022-12-20 20:04:30 -07:00
*/
2022-07-14 14:19:30 -06:00
con . print ( " The game was forced to pause! \n " ) ;
}
else if ( first = = " cls " | | first = = " clear " )
{
if ( con . is_console ( ) )
( ( Console & ) con ) . clear ( ) ;
else
2012-03-12 00:31:29 -06:00
{
2022-07-14 14:19:30 -06:00
con . printerr ( " No console to clear. \n " ) ;
return CR_NEEDS_CONSOLE ;
}
}
else if ( first = = " die " )
{
std : : _Exit ( 666 ) ;
}
else if ( first = = " kill-lua " )
{
bool force = false ;
for ( auto it = parts . begin ( ) ; it ! = parts . end ( ) ; + + it )
{
if ( * it = = " force " )
force = true ;
}
if ( ! Lua : : Interrupt ( force ) )
{
con . printerr (
" Failed to register hook. This can happen if you have "
" lua profiling or coverage monitoring enabled. Use "
" 'kill-lua force' to force, but this may disable "
" profiling and coverage monitoring. \n " ) ;
}
}
else if ( first = = " script " )
{
if ( parts . size ( ) = = 1 )
{
loadScriptFile ( con , parts [ 0 ] , false ) ;
}
else
{
con < < " Usage: " < < endl
< < " script <filename> " < < endl ;
return CR_WRONG_USAGE ;
2012-03-12 00:31:29 -06:00
}
2022-07-14 14:19:30 -06:00
}
else if ( first = = " hide " )
{
if ( ! getConsole ( ) . hide ( ) )
2014-05-26 09:29:27 -06:00
{
2022-07-14 14:19:30 -06:00
con . printerr ( " Could not hide console \n " ) ;
return CR_FAILURE ;
}
return CR_OK ;
}
else if ( first = = " show " )
{
if ( ! getConsole ( ) . show ( ) )
{
con . printerr ( " Could not show console \n " ) ;
return CR_FAILURE ;
}
return CR_OK ;
}
else if ( first = = " sc-script " )
{
if ( parts . empty ( ) | | parts [ 0 ] = = " help " | | parts [ 0 ] = = " ? " )
{
con < < " Usage: sc-script add|remove|list|help SC_EVENT [path-to-script] [...] " < < endl ;
con < < " Valid event names (SC_ prefix is optional): " < < endl ;
for ( int i = SC_WORLD_LOADED ; i < = SC_UNPAUSED ; i + + )
2015-02-08 07:30:40 -07:00
{
2022-07-14 14:19:30 -06:00
std : : string name = sc_event_name ( ( state_change_event ) i ) ;
if ( name ! = " SC_UNKNOWN " )
con < < " " < < name < < endl ;
2015-02-08 07:30:40 -07:00
}
return CR_OK ;
2014-05-26 09:29:27 -06:00
}
2022-07-14 14:19:30 -06:00
else if ( parts [ 0 ] = = " list " )
2014-05-26 09:29:27 -06:00
{
2022-07-14 14:19:30 -06:00
if ( parts . size ( ) < 2 )
parts . push_back ( " " ) ;
if ( parts [ 1 ] . size ( ) & & sc_event_id ( parts [ 1 ] ) = = SC_UNKNOWN )
2015-02-08 07:30:40 -07:00
{
2022-07-14 14:19:30 -06:00
con < < " Unrecognized event name: " < < parts [ 1 ] < < endl ;
return CR_WRONG_USAGE ;
}
for ( auto it = state_change_scripts . begin ( ) ; it ! = state_change_scripts . end ( ) ; + + it )
{
if ( ! parts [ 1 ] . size ( ) | | ( it - > event = = sc_event_id ( parts [ 1 ] ) ) )
{
con . print ( " %s (%s): %s%s \n " , sc_event_name ( it - > event ) . c_str ( ) ,
it - > save_specific ? " save-specific " : " global " ,
it - > save_specific ? " <save folder>/raw/ " : " <DF folder>/ " ,
it - > path . c_str ( ) ) ;
}
2015-02-08 07:30:40 -07:00
}
return CR_OK ;
2014-05-26 09:29:27 -06:00
}
2022-07-14 14:19:30 -06:00
else if ( parts [ 0 ] = = " add " )
2015-02-07 17:33:12 -07:00
{
2022-07-14 14:19:30 -06:00
if ( parts . size ( ) < 3 | | ( parts . size ( ) > = 4 & & parts [ 3 ] ! = " -save " ) )
2015-02-07 17:33:12 -07:00
{
2022-07-14 14:19:30 -06:00
con < < " Usage: sc-script add EVENT path-to-script [-save] " < < endl ;
return CR_WRONG_USAGE ;
2015-02-07 17:33:12 -07:00
}
2022-07-14 14:19:30 -06:00
state_change_event evt = sc_event_id ( parts [ 1 ] ) ;
if ( evt = = SC_UNKNOWN )
2015-02-07 17:33:12 -07:00
{
2022-07-14 14:19:30 -06:00
con < < " Unrecognized event: " < < parts [ 1 ] < < endl ;
return CR_FAILURE ;
2015-02-07 17:33:12 -07:00
}
2022-07-14 14:19:30 -06:00
bool save_specific = ( parts . size ( ) > = 4 & & parts [ 3 ] = = " -save " ) ;
StateChangeScript script ( evt , parts [ 2 ] , save_specific ) ;
for ( auto it = state_change_scripts . begin ( ) ; it ! = state_change_scripts . end ( ) ; + + it )
2015-02-07 17:33:12 -07:00
{
2022-07-14 14:19:30 -06:00
if ( script = = * it )
2015-02-07 17:33:12 -07:00
{
2022-07-14 14:19:30 -06:00
con < < " Script already registered " < < endl ;
2015-02-07 17:33:12 -07:00
return CR_FAILURE ;
}
}
2022-07-14 14:19:30 -06:00
state_change_scripts . push_back ( script ) ;
return CR_OK ;
}
else if ( parts [ 0 ] = = " remove " )
{
if ( parts . size ( ) < 3 | | ( parts . size ( ) > = 4 & & parts [ 3 ] ! = " -save " ) )
2015-02-07 17:33:12 -07:00
{
2022-07-14 14:19:30 -06:00
con < < " Usage: sc-script remove EVENT path-to-script [-save] " < < endl ;
return CR_WRONG_USAGE ;
}
state_change_event evt = sc_event_id ( parts [ 1 ] ) ;
if ( evt = = SC_UNKNOWN )
{
con < < " Unrecognized event: " < < parts [ 1 ] < < endl ;
return CR_FAILURE ;
}
bool save_specific = ( parts . size ( ) > = 4 & & parts [ 3 ] = = " -save " ) ;
StateChangeScript tmp ( evt , parts [ 2 ] , save_specific ) ;
auto it = std : : find ( state_change_scripts . begin ( ) , state_change_scripts . end ( ) , tmp ) ;
if ( it ! = state_change_scripts . end ( ) )
{
state_change_scripts . erase ( it ) ;
return CR_OK ;
2015-02-07 17:33:12 -07:00
}
else
{
2022-07-14 14:19:30 -06:00
con < < " Unrecognized script " < < endl ;
return CR_FAILURE ;
2015-02-07 17:33:12 -07:00
}
}
2022-07-14 14:19:30 -06:00
else
2018-07-04 12:22:04 -06:00
{
2022-07-14 14:19:30 -06:00
con < < " Usage: sc-script add|remove|list|help SC_EVENT [path-to-script] [...] " < < endl ;
return CR_WRONG_USAGE ;
}
}
else if ( first = = " devel/dump-rpc " )
{
if ( parts . size ( ) = = 1 )
{
std : : ofstream file ( parts [ 0 ] ) ;
CoreService core ;
core . dumpMethods ( file ) ;
2018-07-04 12:22:04 -06:00
2022-07-14 14:19:30 -06:00
for ( auto & it : * plug_mgr )
{
Plugin * plug = it . second ;
if ( ! plug )
continue ;
2018-07-04 12:22:04 -06:00
2022-07-14 14:19:30 -06:00
std : : unique_ptr < RPCService > svc ( plug - > rpc_connect ( con ) ) ;
if ( ! svc )
continue ;
2018-07-04 12:22:04 -06:00
2022-07-14 14:19:30 -06:00
file < < " // Plugin: " < < plug - > getName ( ) < < endl ;
svc - > dumpMethods ( file ) ;
2018-07-04 12:22:04 -06:00
}
}
2022-07-14 14:19:30 -06:00
else
2017-06-18 15:39:54 -06:00
{
2022-07-14 14:19:30 -06:00
con < < " Usage: devel/dump-rpc \" filename \" " < < endl ;
return CR_WRONG_USAGE ;
2017-06-18 15:39:54 -06:00
}
2022-07-14 14:19:30 -06:00
}
else if ( RunAlias ( con , first , parts , res ) )
{
return res ;
}
else
{
res = plug_mgr - > InvokeCommand ( con , first , parts ) ;
2022-07-22 18:45:48 -06:00
if ( res = = CR_WRONG_USAGE )
{
help_helper ( con , first ) ;
}
else if ( res = = CR_NOT_IMPLEMENTED )
2011-06-16 15:53:39 -06:00
{
2022-07-14 14:19:30 -06:00
string completed ;
string filename = findScript ( first + " .lua " ) ;
bool lua = filename ! = " " ;
if ( ! lua ) {
filename = findScript ( first + " .rb " ) ;
}
if ( lua )
res = runLuaScript ( con , first , parts ) ;
else if ( try_autocomplete ( con , first , completed ) )
res = CR_NOT_IMPLEMENTED ;
else
con . printerr ( " %s is not a recognized command. \n " , first . c_str ( ) ) ;
if ( res = = CR_NOT_IMPLEMENTED )
2011-06-19 20:29:38 -06:00
{
2022-07-14 14:19:30 -06:00
Plugin * p = plug_mgr - > getPluginByName ( first ) ;
if ( p )
2015-11-25 18:22:14 -07:00
{
2022-07-14 14:19:30 -06:00
con . printerr ( " %s is a plugin " , first . c_str ( ) ) ;
if ( p - > getState ( ) = = Plugin : : PS_UNLOADED )
con . printerr ( " that is not loaded - try \" load %s \" or check stderr.log \n " ,
first . c_str ( ) ) ;
else if ( p - > size ( ) )
2022-07-22 21:57:19 -06:00
con . printerr ( " that implements %zi commands - see \" help %s \" for details \n " ,
2022-07-14 14:19:30 -06:00
p - > size ( ) , first . c_str ( ) ) ;
else
con . printerr ( " but does not implement any commands \n " ) ;
2015-11-25 18:22:14 -07:00
}
2011-06-19 20:29:38 -06:00
}
2011-06-16 15:53:39 -06:00
}
2022-07-14 14:19:30 -06:00
else if ( res = = CR_NEEDS_CONSOLE )
2022-07-27 17:30:14 -06:00
con . printerr ( " %s needs an interactive console to work. \n "
" Please run this command from the DFHack terminal. \n " , first . c_str ( ) ) ;
2022-07-14 14:19:30 -06:00
return res ;
2011-12-30 12:11:34 -07:00
}
2012-05-04 09:47:18 -06:00
2022-07-14 14:19:30 -06:00
return CR_OK ;
2011-12-30 12:11:34 -07:00
}
2012-05-04 09:47:18 -06:00
bool Core : : loadScriptFile ( color_ostream & out , string fname , bool silent )
2011-12-30 12:11:34 -07:00
{
2021-09-22 14:30:10 -06:00
if ( ! silent ) {
2022-03-30 15:46:09 -06:00
INFO ( script , out ) < < " Loading script: " < < fname < < std : : endl ;
2021-09-22 14:30:10 -06:00
cerr < < " Loading script: " < < fname < < std : : endl ;
}
2012-05-24 09:31:20 -06:00
ifstream script ( fname . c_str ( ) ) ;
2015-01-26 15:18:30 -07:00
if ( ! script . good ( ) )
2011-12-30 12:11:34 -07:00
{
2012-03-28 18:49:44 -06:00
if ( ! silent )
2021-09-22 14:30:10 -06:00
out . printerr ( " Error loading script: %s \n " , fname . c_str ( ) ) ;
2012-05-04 09:47:18 -06:00
return false ;
2011-12-30 12:11:34 -07:00
}
2015-01-26 15:18:30 -07:00
string command ;
while ( script . good ( ) ) {
string temp ;
getline ( script , temp ) ;
bool doMore = false ;
if ( temp . length ( ) > 0 ) {
if ( temp [ 0 ] = = ' # ' )
continue ;
if ( temp [ temp . length ( ) - 1 ] = = ' \r ' )
temp = temp . substr ( 0 , temp . length ( ) - 1 ) ;
if ( temp . length ( ) > 0 ) {
if ( temp [ temp . length ( ) - 1 ] = = ' \\ ' ) {
2015-01-26 17:24:31 -07:00
temp = temp . substr ( 0 , temp . length ( ) - 1 ) ;
2015-01-26 15:18:30 -07:00
doMore = true ;
}
}
}
command = command + temp ;
if ( ( ! doMore | | ! script . good ( ) ) & & ! command . empty ( ) ) {
runCommand ( out , command ) ;
command = " " ;
}
}
return true ;
2011-12-30 12:11:34 -07:00
}
2014-06-28 07:08:14 -06:00
static void run_dfhack_init ( color_ostream & out , Core * core )
{
2018-07-05 12:18:49 -06:00
CoreSuspender lock ;
2023-01-05 18:11:01 -07:00
if ( ! df : : global : : world | | ! df : : global : : plotinfo | | ! df : : global : : gview )
2014-07-24 13:10:37 -06:00
{
out . printerr ( " Key globals are missing, skipping loading dfhack.init. \n " ) ;
return ;
}
2015-09-21 16:57:58 -06:00
2022-07-10 09:54:55 -06:00
// load baseline defaults
2023-01-04 12:35:50 -07:00
core - > loadScriptFile ( out , CONFIG_PATH + " init/default.dfhack.init " , false ) ;
2022-07-10 09:54:55 -06:00
// load user overrides
2015-09-21 15:24:51 -06:00
std : : vector < std : : string > prefixes ( 1 , " dfhack " ) ;
2023-01-04 12:35:50 -07:00
loadScriptFiles ( core , out , prefixes , CONFIG_PATH + " init " ) ;
2014-06-28 07:08:14 -06:00
}
2014-01-27 06:13:31 -07:00
// Load dfhack.init in a dedicated thread (non-interactive console mode)
void fInitthread ( void * iodata )
{
IODATA * iod = ( ( IODATA * ) iodata ) ;
Core * core = iod - > core ;
2014-01-27 07:10:12 -07:00
color_ostream_proxy out ( core - > getConsole ( ) ) ;
2014-06-28 07:08:14 -06:00
run_dfhack_init ( out , core ) ;
2014-01-27 06:13:31 -07:00
}
2011-12-30 12:11:34 -07:00
// A thread function... for the interactive console.
void fIOthread ( void * iodata )
{
2023-01-04 12:35:50 -07:00
static const std : : string HISTORY_FILE = CONFIG_PATH + " dfhack.history " ;
2022-07-23 23:41:40 -06:00
2011-12-30 12:11:34 -07:00
IODATA * iod = ( ( IODATA * ) iodata ) ;
Core * core = iod - > core ;
PluginManager * plug_mgr = ( ( IODATA * ) iodata ) - > plug_mgr ;
CommandHistory main_history ;
2023-01-04 12:35:50 -07:00
main_history . load ( HISTORY_FILE . c_str ( ) ) ;
2011-12-30 12:11:34 -07:00
2012-03-10 04:55:42 -07:00
Console & con = core - > getConsole ( ) ;
2014-01-27 06:13:31 -07:00
if ( plug_mgr = = 0 )
2011-12-30 12:11:34 -07:00
{
con . printerr ( " Something horrible happened in Core's constructor... \n " ) ;
return ;
}
2014-06-28 07:08:14 -06:00
run_dfhack_init ( con , core ) ;
2011-12-30 12:11:34 -07:00
con . print ( " DFHack is ready. Have a nice day! \n "
2015-04-03 12:02:14 -06:00
" DFHack version %s \n "
" Type in '?' or 'help' for general help, 'ls' to see all commands. \n " ,
2015-10-17 13:35:40 -06:00
dfhack_version_desc ( ) . c_str ( ) ) ;
2011-12-30 12:11:34 -07:00
int clueless_counter = 0 ;
2018-07-18 12:29:13 -06:00
if ( getenv ( " DFHACK_DISABLE_CONSOLE " ) )
return ;
2011-12-30 12:11:34 -07:00
while ( true )
{
string command = " " ;
2018-07-04 06:21:25 -06:00
int ret ;
while ( ( ret = con . lineedit ( " [DFHack]# " , command , main_history ) )
= = Console : : RETRY ) ;
if ( ret = = Console : : SHUTDOWN )
2011-12-30 12:11:34 -07:00
{
cerr < < " Console is shutting down properly. " < < endl ;
return ;
}
2018-07-04 06:21:25 -06:00
else if ( ret = = Console : : FAILURE )
2011-12-30 12:11:34 -07:00
{
cerr < < " Console caught an unspecified error. " < < endl ;
continue ;
}
else if ( ret )
{
// a proper, non-empty command was entered
main_history . add ( command ) ;
2023-01-04 12:35:50 -07:00
main_history . save ( HISTORY_FILE . c_str ( ) ) ;
2011-12-30 12:11:34 -07:00
}
2015-01-11 10:48:52 -07:00
2012-05-04 09:47:18 -06:00
auto rv = core - > runCommand ( con , command ) ;
if ( rv = = CR_NOT_IMPLEMENTED )
clueless_counter + + ;
2011-12-30 12:11:34 -07:00
2011-06-22 00:14:21 -06:00
if ( clueless_counter = = 3 )
{
2015-11-25 18:22:14 -07:00
con . print ( " Run 'help' or '?' for the list of available commands. \n " ) ;
2011-06-22 00:14:21 -06:00
clueless_counter = 0 ;
}
2011-06-16 15:53:39 -06:00
}
}
2011-06-14 08:13:28 -06:00
2018-06-12 08:53:43 -06:00
Core : : ~ Core ( )
{
2018-07-10 09:23:22 -06:00
// we leak the memory in case ~Core is called after _exit
2018-06-12 08:53:43 -06:00
}
2012-03-15 05:33:19 -06:00
2018-06-12 08:33:12 -06:00
Core : : Core ( ) :
2018-07-08 17:10:01 -06:00
d ( dts : : make_unique < Private > ( ) ) ,
2018-06-12 08:33:12 -06:00
script_path_mutex { } ,
HotkeyMutex { } ,
HotkeyCond { } ,
alias_mutex { } ,
2018-07-07 04:13:55 -06:00
started { false } ,
2018-06-21 09:58:16 -06:00
misc_data_mutex { } ,
CoreSuspendMutex { } ,
CoreWakeup { } ,
ownerThread { } ,
toolCount { 0 }
2018-06-12 08:33:12 -06:00
{
2011-06-22 00:14:21 -06:00
// init the console. This must be always the first step!
2011-06-24 21:35:29 -06:00
plug_mgr = 0 ;
2011-07-09 03:33:58 -06:00
errorstate = false ;
2018-07-07 04:13:55 -06:00
vinfo = 0 ;
2011-07-09 03:33:58 -06:00
memset ( & ( s_mods ) , 0 , sizeof ( s_mods ) ) ;
// set up hotkey capture
2018-06-21 08:13:22 -06:00
hotkey_set = NO ;
2011-12-30 07:12:15 -07:00
last_world_data_ptr = NULL ;
2012-03-31 18:56:54 -06:00
last_local_map_ptr = NULL ;
2012-07-05 10:03:02 -06:00
last_pause_state = false ;
2011-12-30 12:25:50 -07:00
top_viewscreen = NULL ;
2012-03-15 01:07:43 -06:00
color_ostream : : log_errors_to_stderr = true ;
2015-09-06 14:23:02 -06:00
2011-07-09 03:33:58 -06:00
} ;
2015-10-17 19:18:04 -06:00
void Core : : fatal ( std : : string output )
2011-11-04 02:08:29 -06:00
{
2015-10-17 19:18:04 -06:00
errorstate = true ;
2011-11-04 02:08:29 -06:00
stringstream out ;
out < < output ;
2015-10-17 19:18:04 -06:00
if ( output [ output . size ( ) - 1 ] ! = ' \n ' )
out < < ' \n ' ;
out < < " DFHack will now deactivate. \n " ;
2011-11-04 02:08:29 -06:00
if ( con . isInited ( ) )
{
con . printerr ( " %s " , out . str ( ) . c_str ( ) ) ;
2015-10-17 19:18:04 -06:00
con . reset_color ( ) ;
con . print ( " \n " ) ;
2011-11-04 02:08:29 -06:00
}
fprintf ( stderr , " %s \n " , out . str ( ) . c_str ( ) ) ;
# ifndef LINUX_BUILD
out < < " Check file stderr.log for details \n " ;
MessageBox ( 0 , out . str ( ) . c_str ( ) , " DFHack error! " , MB_OK | MB_ICONERROR ) ;
2014-11-17 13:11:46 -07:00
# else
cout < < " DFHack fatal error: " < < out . str ( ) < < std : : endl ;
2011-11-04 02:08:29 -06:00
# endif
2020-02-09 17:05:09 -07:00
bool is_headless = bool ( getenv ( " DFHACK_HEADLESS " ) ) ;
if ( is_headless )
{
exit ( ' f ' ) ;
}
2011-11-04 02:08:29 -06:00
}
2012-05-04 09:47:18 -06:00
std : : string Core : : getHackPath ( )
{
# ifdef LINUX_BUILD
return p - > getPath ( ) + " /hack/ " ;
# else
return p - > getPath ( ) + " \\ hack \\ " ;
# endif
}
2011-07-09 03:33:58 -06:00
bool Core : : Init ( )
{
2011-07-27 06:22:37 -06:00
if ( started )
return true ;
2011-08-17 05:26:03 -06:00
if ( errorstate )
return false ;
2011-07-31 19:31:52 -06:00
2018-06-21 09:58:16 -06:00
// Lock the CoreSuspendMutex until the thread exits or call Core::Shutdown
// Core::Update will temporary unlock when there is any commands queued
2018-07-03 13:19:19 -06:00
MainThread : : suspend ( ) . lock ( ) ;
2018-06-21 09:58:16 -06:00
2017-06-30 11:29:05 -06:00
// Re-route stdout and stderr again - DF seems to set up stdout and
// stderr.txt on Windows as of 0.43.05. Also, log before switching files to
// make it obvious what's going on if someone checks the *.txt files.
# ifndef LINUX_BUILD
// Don't do this on Linux because it will break PRINT_MODE:TEXT
2022-12-23 20:05:00 -07:00
// this is handled as appropriate in Console-posix.cpp
2017-06-30 11:29:05 -06:00
fprintf ( stdout , " dfhack: redirecting stdout to stdout.log (again) \n " ) ;
2022-12-26 11:26:22 -07:00
if ( ! freopen ( " stdout.log " , " w " , stdout ) )
cerr < < " Could not redirect stdout to stdout.log " < < endl ;
2017-06-30 11:29:05 -06:00
# endif
2022-12-23 20:05:00 -07:00
fprintf ( stderr , " dfhack: redirecting stderr to stderr.log \n " ) ;
2022-12-26 11:26:22 -07:00
if ( ! freopen ( " stderr.log " , " w " , stderr ) )
cerr < < " Could not redirect stderr to stderr.log " < < endl ;
2017-06-30 11:29:05 -06:00
2020-10-14 19:22:53 -06:00
Filesystem : : init ( ) ;
cerr < < " DFHack build: " < < Version : : git_description ( ) < < " \n "
< < " Starting with working directory: " < < Filesystem : : getcwd ( ) < < endl ;
2015-06-24 17:32:45 -06:00
2011-06-17 07:02:43 -06:00
// find out what we are...
2011-08-14 00:42:21 -06:00
# ifdef LINUX_BUILD
2012-02-11 11:24:44 -07:00
const char * path = " hack/symbols.xml " ;
2011-08-14 00:42:21 -06:00
# else
2012-02-11 11:24:44 -07:00
const char * path = " hack \\ symbols.xml " ;
2011-08-14 00:42:21 -06:00
# endif
2018-07-08 16:50:14 -06:00
auto local_vif = dts : : make_unique < DFHack : : VersionInfoFactory > ( ) ;
2011-11-04 02:08:29 -06:00
cerr < < " Identifying DF version. \n " ;
try
{
2018-07-08 16:50:14 -06:00
local_vif - > loadFile ( path ) ;
2011-11-04 02:08:29 -06:00
}
catch ( Error : : All & err )
{
std : : stringstream out ;
2012-02-11 11:24:44 -07:00
out < < " Error while reading symbols.xml: \n " ;
2011-11-04 02:08:29 -06:00
out < < err . what ( ) < < std : : endl ;
errorstate = true ;
2015-10-17 19:18:04 -06:00
fatal ( out . str ( ) ) ;
2011-11-04 02:08:29 -06:00
return false ;
}
2018-07-08 16:50:14 -06:00
vif = std : : move ( local_vif ) ;
auto local_p = dts : : make_unique < DFHack : : Process > ( * vif ) ;
local_p - > ValidateDescriptionOS ( ) ;
2018-07-08 13:04:53 -06:00
vinfo = local_p - > getDescriptor ( ) ;
2011-08-16 15:39:18 -06:00
2018-07-08 13:04:53 -06:00
if ( ! vinfo | | ! local_p - > isIdentified ( ) )
2011-08-01 18:21:25 -06:00
{
2016-06-30 18:42:27 -06:00
if ( ! Version : : git_xml_match ( ) )
{
const char * msg = (
" ******************************************************* \n "
" * BIG, UGLY ERROR MESSAGE * \n "
" ******************************************************* \n "
" \n "
" This DF version is missing from hack/symbols.xml, and \n "
" you have compiled DFHack with a df-structures (xml) \n "
" version that does *not* match the version tracked in git. \n "
" \n "
" If you are not actively working on df-structures and you \n "
" expected DFHack to work, you probably forgot to run \n "
" \n "
" git submodule update \n "
" \n "
" If this does not sound familiar, read Compile.rst and \n "
" recompile. \n "
" More details can be found in stderr.log in this folder. \n "
) ;
cout < < msg < < endl ;
cerr < < msg < < endl ;
fatal ( " Not a known DF version - XML version mismatch (see console or stderr.log) " ) ;
}
else
{
fatal ( " Not a known DF version. \n " ) ;
}
2011-08-16 15:39:18 -06:00
errorstate = true ;
return false ;
}
2011-11-04 02:08:29 -06:00
cerr < < " Version: " < < vinfo - > getVersion ( ) < < endl ;
2018-07-08 13:04:53 -06:00
p = std : : move ( local_p ) ;
2018-01-14 20:56:35 -07:00
2012-06-14 03:08:39 -06:00
// Init global object pointers
df : : global : : InitGlobals ( ) ;
2011-11-04 02:08:29 -06:00
cerr < < " Initializing Console. \n " ;
2011-07-31 19:31:52 -06:00
// init the console.
2014-10-06 14:46:43 -06:00
bool is_text_mode = ( init & & init - > display . flag . is_set ( init_display_flags : : TEXT ) ) ;
2018-02-03 21:59:01 -07:00
bool is_headless = bool ( getenv ( " DFHACK_HEADLESS " ) ) ;
if ( is_headless )
{
# ifdef LINUX_BUILD
2020-04-28 19:38:47 -06:00
if ( is_text_mode )
2018-02-03 21:59:01 -07:00
{
2020-04-28 19:38:47 -06:00
auto endwin = ( int ( * ) ( void ) ) dlsym ( RTLD_DEFAULT , " endwin " ) ;
if ( endwin )
{
endwin ( ) ;
}
else
{
cerr < < " endwin(): bind failed " < < endl ;
}
2018-02-03 21:59:01 -07:00
}
else
{
2020-04-28 19:38:47 -06:00
cerr < < " Headless mode requires PRINT_MODE:TEXT " < < endl ;
2018-02-03 21:59:01 -07:00
}
# else
cerr < < " Headless mode not supported on Windows " < < endl ;
# endif
}
2018-07-18 12:29:13 -06:00
if ( is_text_mode & & ! is_headless )
2011-07-31 19:31:52 -06:00
{
2012-06-14 03:08:39 -06:00
cerr < < " Console is not available. Use dfhack-run to send commands. \n " ;
2014-10-06 14:46:43 -06:00
if ( ! is_text_mode )
{
cout < < " Console disabled. \n " ;
}
2011-07-31 19:31:52 -06:00
}
2012-06-14 03:08:39 -06:00
else if ( con . init ( false ) )
2011-11-04 02:08:29 -06:00
cerr < < " Console is running. \n " ;
else
2015-10-17 19:18:04 -06:00
cerr < < " Console has failed to initialize! \n " ;
2012-02-08 19:07:26 -07:00
/*
2011-08-16 15:39:18 -06:00
// dump offsets to a file
std : : ofstream dump ( " offsets.log " ) ;
if ( ! dump . fail ( ) )
2011-06-14 08:13:28 -06:00
{
2012-02-08 19:07:26 -07:00
//dump << vinfo->PrintOffsets();
2011-08-16 15:39:18 -06:00
dump . close ( ) ;
2011-06-14 08:13:28 -06:00
}
2012-02-08 19:07:26 -07:00
*/
2011-12-24 03:51:58 -07:00
// initialize data defs
2011-12-29 05:30:55 -07:00
virtual_identity : : Init ( this ) ;
2011-07-31 19:31:52 -06:00
2023-01-04 12:35:50 -07:00
// create config directory if it doesn't already exist
if ( ! Filesystem : : mkdir_recursive ( CONFIG_PATH ) )
con . printerr ( " Failed to create config directory: '%s' \n " , CONFIG_PATH . c_str ( ) ) ;
2015-06-18 06:59:01 -06:00
// copy over default config files if necessary
2020-07-18 23:22:37 -06:00
std : : map < std : : string , bool > config_files ;
std : : map < std : : string , bool > default_config_files ;
2023-01-04 12:35:50 -07:00
if ( Filesystem : : listdir_recursive ( CONFIG_PATH , config_files , 10 , false ) ! = 0 )
con . printerr ( " Failed to list directory: '%s' \n " , CONFIG_PATH . c_str ( ) ) ;
else if ( Filesystem : : listdir_recursive ( CONFIG_DEFAULTS_PATH , default_config_files , 10 , false ) ! = 0 )
con . printerr ( " Failed to list directory: '%s' \n " , CONFIG_DEFAULTS_PATH . c_str ( ) ) ;
2015-06-18 06:59:01 -06:00
else
{
2020-07-18 23:22:37 -06:00
// ensure all config file directories exist before we start copying files
2023-01-04 12:35:50 -07:00
for ( auto & entry : default_config_files ) {
2020-07-18 23:22:37 -06:00
// skip over files
2023-01-04 12:35:50 -07:00
if ( ! entry . second )
2020-07-18 23:22:37 -06:00
continue ;
2023-01-04 12:35:50 -07:00
std : : string dirname = CONFIG_PATH + entry . first ;
2020-07-18 23:22:37 -06:00
if ( ! Filesystem : : mkdir_recursive ( dirname ) )
con . printerr ( " Failed to create config directory: '%s' \n " , dirname . c_str ( ) ) ;
}
// copy files from the default tree that don't already exist in the config tree
2023-01-04 12:35:50 -07:00
for ( auto & entry : default_config_files ) {
2020-07-18 23:22:37 -06:00
// skip over directories
2023-01-04 12:35:50 -07:00
if ( entry . second )
2020-07-18 23:22:37 -06:00
continue ;
2023-01-04 12:35:50 -07:00
std : : string filename = entry . first ;
if ( ! config_files . count ( filename ) ) {
std : : string src_file = CONFIG_DEFAULTS_PATH + filename ;
2015-09-03 13:02:51 -06:00
if ( ! Filesystem : : isfile ( src_file ) )
continue ;
2023-01-04 12:35:50 -07:00
std : : string dest_file = CONFIG_PATH + filename ;
2015-06-18 06:59:01 -06:00
std : : ifstream src ( src_file , std : : ios : : binary ) ;
std : : ofstream dest ( dest_file , std : : ios : : binary ) ;
2023-01-04 12:35:50 -07:00
if ( ! src . good ( ) | | ! dest . good ( ) ) {
con . printerr ( " Copy failed: '%s' \n " , filename . c_str ( ) ) ;
2015-06-18 06:59:01 -06:00
continue ;
}
dest < < src . rdbuf ( ) ;
src . close ( ) ;
dest . close ( ) ;
}
}
}
2016-03-27 13:56:23 -06:00
loadScriptPaths ( con ) ;
2012-04-15 09:09:25 -06:00
// initialize common lua context
2015-10-17 19:18:04 -06:00
if ( ! Lua : : Core : : Init ( con ) )
{
fatal ( " Lua failed to initialize " ) ;
return false ;
}
2012-04-15 09:09:25 -06:00
2023-01-04 18:15:32 -07:00
cerr < < " Binding to SDL. \n " ;
if ( ! DFSDL : : init ( con ) ) {
fatal ( " cannot bind SDL libraries " ) ;
return false ;
}
2023-01-03 02:01:30 -07:00
cerr < < " Initializing textures. \n " ;
Textures : : init ( con ) ;
2011-06-17 07:02:43 -06:00
// create mutex for syncing with interactive tasks
2023-01-03 02:01:30 -07:00
cerr < < " Initializing plugins. \n " ;
2011-07-09 03:33:58 -06:00
// create plugin manager
2011-06-24 21:35:29 -06:00
plug_mgr = new PluginManager ( this ) ;
2015-08-14 14:11:23 -06:00
plug_mgr - > init ( ) ;
2018-07-07 04:13:55 -06:00
cerr < < " Starting the TCP listener. \n " ;
auto listen = ServerMain : : listen ( RemoteClient : : GetDefaultPort ( ) ) ;
2011-06-25 00:05:17 -06:00
IODATA * temp = new IODATA ;
temp - > core = this ;
temp - > plug_mgr = plug_mgr ;
2014-01-27 06:13:31 -07:00
2018-02-03 21:59:01 -07:00
if ( ! is_text_mode | | is_headless )
2012-06-14 03:08:39 -06:00
{
cerr < < " Starting IO thread. \n " ;
// create IO thread
2018-06-12 08:53:43 -06:00
d - > iothread = std : : thread { fIOthread , ( void * ) temp } ;
2012-06-14 03:08:39 -06:00
}
2014-01-27 06:13:31 -07:00
else
{
2018-06-12 08:53:43 -06:00
std : : cerr < < " Starting dfhack.init thread. \n " ;
d - > iothread = std : : thread { fInitthread , ( void * ) temp } ;
2014-01-27 06:13:31 -07:00
}
2011-11-04 02:08:29 -06:00
cerr < < " Starting DF input capture thread. \n " ;
2011-07-09 03:33:58 -06:00
// set up hotkey capture
2018-06-21 08:13:22 -06:00
d - > hotkeythread = std : : thread ( fHKthread , ( void * ) temp ) ;
2011-07-09 03:33:58 -06:00
started = true ;
2015-01-11 10:48:52 -07:00
modstate = 0 ;
2012-03-14 09:57:29 -06:00
2018-07-07 04:13:55 -06:00
if ( ! listen . get ( ) )
2012-03-14 09:57:29 -06:00
cerr < < " TCP listen failed. \n " ;
2023-01-05 18:11:01 -07:00
if ( df : : global : : game )
2015-12-13 13:03:25 -07:00
{
2015-12-13 13:03:25 -07:00
vector < string > args ;
2023-01-05 18:11:01 -07:00
const string & raw = df : : global : : game - > command_line . original ;
2015-12-13 13:03:25 -07:00
size_t offset = 0 ;
while ( offset < raw . size ( ) )
{
if ( raw [ offset ] = = ' " ' )
{
offset + + ;
size_t next = raw . find ( " \" " , offset ) ;
args . push_back ( raw . substr ( offset , next - offset ) ) ;
offset = next + 2 ;
}
else
{
size_t next = raw . find ( " " , offset ) ;
if ( next = = string : : npos )
{
args . push_back ( raw . substr ( offset ) ) ;
offset = raw . size ( ) ;
}
else
{
args . push_back ( raw . substr ( offset , next - offset ) ) ;
offset = next + 1 ;
}
}
}
2015-12-17 09:54:19 -07:00
for ( auto it = args . begin ( ) ; it ! = args . end ( ) ; )
2015-12-13 13:03:25 -07:00
{
2015-12-13 13:03:25 -07:00
const string & first = * it ;
2015-12-17 09:54:19 -07:00
if ( first . length ( ) > 0 & & first [ 0 ] = = ' + ' )
{
vector < string > cmd ;
for ( it + + ; it ! = args . end ( ) ; it + + ) {
2015-12-13 13:03:25 -07:00
const string & arg = * it ;
2015-12-17 09:54:19 -07:00
if ( arg . length ( ) > 0 & & arg [ 0 ] = = ' + ' )
{
break ;
}
cmd . push_back ( arg ) ;
2015-12-13 13:03:25 -07:00
}
2015-12-26 11:47:58 -07:00
if ( runCommand ( con , first . substr ( 1 ) , cmd ) ! = CR_OK )
2015-12-13 13:03:25 -07:00
{
2015-12-17 09:54:19 -07:00
cerr < < " Error running command: " < < first . substr ( 1 ) ;
for ( auto it2 = cmd . begin ( ) ; it2 ! = cmd . end ( ) ; it2 + + )
{
2015-12-13 13:03:25 -07:00
cerr < < " \" " < < * it2 < < " \" " ;
2015-12-17 09:54:19 -07:00
}
cerr < < " \n " ;
2015-12-13 13:03:25 -07:00
}
}
2015-12-17 09:54:19 -07:00
else
{
it + + ;
}
2015-12-13 13:03:25 -07:00
}
}
2011-11-04 02:08:29 -06:00
cerr < < " DFHack is running. \n " ;
2022-12-23 20:05:00 -07:00
2023-01-03 02:01:30 -07:00
onStateChange ( con , SC_CORE_INITIALIZED ) ;
2022-12-23 20:05:00 -07:00
2011-07-09 03:33:58 -06:00
return true ;
}
/// sets the current hotkey command
bool Core : : setHotkeyCmd ( std : : string cmd )
{
// access command
2018-06-12 08:33:12 -06:00
std : : lock_guard < std : : mutex > lock ( HotkeyMutex ) ;
2018-06-21 08:13:22 -06:00
hotkey_set = SET ;
2018-06-12 08:33:12 -06:00
hotkey_cmd = cmd ;
HotkeyCond . notify_all ( ) ;
2011-07-09 03:33:58 -06:00
return true ;
}
/// removes the hotkey command and gives it to the caller thread
2018-06-21 08:13:22 -06:00
std : : string Core : : getHotkeyCmd ( bool & keep_going )
2011-07-09 03:33:58 -06:00
{
string returner ;
2018-06-12 08:33:12 -06:00
std : : unique_lock < std : : mutex > lock ( HotkeyMutex ) ;
HotkeyCond . wait ( lock , [ this ] ( ) - > bool { return this - > hotkey_set ; } ) ;
2018-06-21 08:13:22 -06:00
if ( hotkey_set = = SHUTDOWN ) {
keep_going = false ;
return returner ;
}
hotkey_set = NO ;
2011-07-09 03:33:58 -06:00
returner = hotkey_cmd ;
hotkey_cmd . clear ( ) ;
return returner ;
}
2012-03-31 05:40:54 -06:00
void Core : : print ( const char * format , . . . )
{
color_ostream_proxy proxy ( getInstance ( ) . con ) ;
va_list args ;
va_start ( args , format ) ;
proxy . vprint ( format , args ) ;
va_end ( args ) ;
}
2012-03-10 04:55:42 -07:00
void Core : : printerr ( const char * format , . . . )
{
color_ostream_proxy proxy ( getInstance ( ) . con ) ;
va_list args ;
va_start ( args , format ) ;
proxy . vprinterr ( format , args ) ;
va_end ( args ) ;
}
2011-08-07 00:41:46 -06:00
void Core : : RegisterData ( void * p , std : : string key )
2011-08-04 13:00:21 -06:00
{
2018-06-12 08:33:12 -06:00
std : : lock_guard < std : : mutex > lock ( misc_data_mutex ) ;
2011-08-07 00:41:46 -06:00
misc_data_map [ key ] = p ;
2011-08-04 13:00:21 -06:00
}
2011-08-07 00:41:46 -06:00
void * Core : : GetData ( std : : string key )
2011-08-04 13:00:21 -06:00
{
2018-06-12 08:33:12 -06:00
std : : lock_guard < std : : mutex > lock ( misc_data_mutex ) ;
2011-08-07 00:41:46 -06:00
std : : map < std : : string , void * > : : iterator it = misc_data_map . find ( key ) ;
if ( it ! = misc_data_map . end ( ) )
{
void * p = it - > second ;
return p ;
}
else
{
return 0 ; // or throw an error.
}
2011-08-04 13:00:21 -06:00
}
2011-06-14 08:13:28 -06:00
2012-04-15 09:09:25 -06:00
bool Core : : isSuspended ( void )
{
2018-06-21 09:58:16 -06:00
return ownerThread . load ( ) = = std : : this_thread : : get_id ( ) ;
2011-06-14 08:13:28 -06:00
}
2022-12-23 20:05:00 -07:00
void Core : : doUpdate ( color_ostream & out )
2012-08-18 01:52:38 -06:00
{
2012-04-15 09:09:25 -06:00
Lua : : Core : : Reset ( out , " DF code execution " ) ;
2014-07-27 22:57:55 -06:00
// find the current viewscreen
df : : viewscreen * screen = NULL ;
if ( df : : global : : gview )
{
screen = & df : : global : : gview - > view ;
while ( screen - > child )
screen = screen - > child ;
}
2015-11-14 19:48:51 -07:00
// detect if the viewscreen changed, and trigger events later
bool vs_changed = false ;
if ( screen ! = top_viewscreen )
{
top_viewscreen = screen ;
vs_changed = true ;
}
2019-08-23 18:45:35 -06:00
bool is_load_save =
strict_virtual_cast < df : : viewscreen_game_cleanerst > ( screen ) | |
strict_virtual_cast < df : : viewscreen_loadgamest > ( screen ) | |
strict_virtual_cast < df : : viewscreen_savegamest > ( screen ) ;
2019-08-23 18:30:06 -06:00
// save data (do this before updating last_world_data_ptr and triggering unload events)
2023-01-05 18:11:01 -07:00
if ( ( df : : global : : plotinfo - > main . autosave_request & & ! d - > last_autosave_request ) | |
2019-08-23 18:45:35 -06:00
( is_load_save & & ! d - > was_load_save & & strict_virtual_cast < df : : viewscreen_savegamest > ( screen ) ) )
2019-08-23 18:30:06 -06:00
{
doSaveData ( out ) ;
}
2014-07-27 22:57:55 -06:00
2011-12-30 07:12:15 -07:00
// detect if the game was loaded or unloaded in the meantime
void * new_wdata = NULL ;
2012-03-31 18:56:54 -06:00
void * new_mapdata = NULL ;
2014-07-27 22:57:55 -06:00
if ( df : : global : : world & & ! is_load_save )
2012-03-31 18:56:54 -06:00
{
2011-12-30 07:12:15 -07:00
df : : world_data * wdata = df : : global : : world - > world_data ;
// when the game is unloaded, world_data isn't deleted, but its contents are
2013-07-07 15:34:55 -06:00
// regions work to detect arena too
if ( wdata & & ! wdata - > regions . empty ( ) )
2011-12-30 07:12:15 -07:00
new_wdata = wdata ;
2012-03-31 18:56:54 -06:00
new_mapdata = df : : global : : world - > map . block_index ;
2011-12-30 07:12:15 -07:00
}
2012-04-01 06:43:40 -06:00
2012-03-31 18:56:54 -06:00
// if the world changes
if ( new_wdata ! = last_world_data_ptr )
{
// we check for map change too
2012-04-10 02:11:00 -06:00
bool had_map = isMapLoaded ( ) ;
2012-04-01 06:43:40 -06:00
last_world_data_ptr = new_wdata ;
last_local_map_ptr = new_mapdata ;
2012-03-31 18:56:54 -06:00
// and if the world is going away, we report the map change first
2012-04-10 02:11:00 -06:00
if ( had_map )
2012-05-17 10:04:09 -06:00
onStateChange ( out , SC_MAP_UNLOADED ) ;
2012-03-31 18:56:54 -06:00
// and if the world is appearing, we report map change after that
2012-05-17 10:04:09 -06:00
onStateChange ( out , new_wdata ? SC_WORLD_LOADED : SC_WORLD_UNLOADED ) ;
2012-04-10 02:11:00 -06:00
if ( isMapLoaded ( ) )
2012-05-17 10:04:09 -06:00
onStateChange ( out , SC_MAP_LOADED ) ;
2012-03-31 18:56:54 -06:00
}
// otherwise just check for map change...
else if ( new_mapdata ! = last_local_map_ptr )
{
2012-04-10 02:11:00 -06:00
bool had_map = isMapLoaded ( ) ;
2012-03-31 18:56:54 -06:00
last_local_map_ptr = new_mapdata ;
2012-04-10 02:11:00 -06:00
if ( isMapLoaded ( ) ! = had_map )
{
2012-05-17 10:04:09 -06:00
onStateChange ( out , new_mapdata ? SC_MAP_LOADED : SC_MAP_UNLOADED ) ;
2012-04-10 02:11:00 -06:00
}
2011-12-30 07:12:15 -07:00
}
2015-11-14 19:48:51 -07:00
if ( vs_changed )
2014-07-27 22:57:55 -06:00
onStateChange ( out , SC_VIEWSCREEN_CHANGED ) ;
2011-12-30 12:25:50 -07:00
2012-07-05 10:03:02 -06:00
if ( df : : global : : pause_state )
{
if ( * df : : global : : pause_state ! = last_pause_state )
{
onStateChange ( out , last_pause_state ? SC_UNPAUSED : SC_PAUSED ) ;
last_pause_state = * df : : global : : pause_state ;
}
}
2012-05-17 10:04:09 -06:00
// Execute per-frame handlers
onUpdate ( out ) ;
2012-05-04 10:59:06 -06:00
2023-01-05 18:11:01 -07:00
d - > last_autosave_request = df : : global : : plotinfo - > main . autosave_request ;
2019-08-23 18:45:35 -06:00
d - > was_load_save = is_load_save ;
2012-08-18 01:52:38 -06:00
out < < std : : flush ;
}
// should always be from simulation thread!
int Core : : Update ( )
{
if ( errorstate )
return - 1 ;
color_ostream_proxy out ( con ) ;
// Pretend this thread has suspended the core in the usual way,
// and run various processing hooks.
2012-04-03 03:29:59 -06:00
{
2012-08-18 01:52:38 -06:00
if ( ! started )
{
2022-12-23 20:05:00 -07:00
// Initialize the core
2012-08-18 01:52:38 -06:00
Init ( ) ;
if ( errorstate )
return - 1 ;
}
2022-12-23 20:05:00 -07:00
doUpdate ( out ) ;
2012-08-18 01:52:38 -06:00
}
2011-12-30 07:12:15 -07:00
2018-06-21 09:58:16 -06:00
// Let all commands run that require CoreSuspender
2018-07-03 13:19:19 -06:00
CoreWakeup . wait ( MainThread : : suspend ( ) ,
2018-06-21 09:58:16 -06:00
[ this ] ( ) - > bool { return this - > toolCount . load ( ) = = 0 ; } ) ;
2012-03-15 05:33:19 -06:00
2011-06-14 08:13:28 -06:00
return 0 ;
} ;
2012-05-17 10:38:27 -06:00
extern bool buildings_do_onupdate ;
void buildings_onStateChange ( color_ostream & out , state_change_event event ) ;
void buildings_onUpdate ( color_ostream & out ) ;
static int buildings_timer = 0 ;
2012-05-17 10:04:09 -06:00
void Core : : onUpdate ( color_ostream & out )
{
2012-12-14 19:05:38 -07:00
EventManager : : manageEvents ( out ) ;
2012-05-17 10:38:27 -06:00
// convert building reagents
if ( buildings_do_onupdate & & ( + + buildings_timer & 1 ) )
buildings_onUpdate ( out ) ;
2012-05-17 10:04:09 -06:00
// notify all the plugins that a game tick is finished
plug_mgr - > OnUpdate ( out ) ;
// process timers in lua
Lua : : Core : : onUpdate ( out ) ;
}
2015-09-15 05:27:42 -06:00
void getFilesWithPrefixAndSuffix ( const std : : string & folder , const std : : string & prefix , const std : : string & suffix , std : : vector < std : : string > & result ) {
//DFHACK_EXPORT int listdir (std::string dir, std::vector<std::string> &files);
std : : vector < std : : string > files ;
DFHack : : Filesystem : : listdir ( folder , files ) ;
for ( size_t a = 0 ; a < files . size ( ) ; a + + ) {
if ( prefix . length ( ) > files [ a ] . length ( ) )
continue ;
if ( suffix . length ( ) > files [ a ] . length ( ) )
continue ;
if ( files [ a ] . compare ( 0 , prefix . length ( ) , prefix ) ! = 0 )
continue ;
if ( files [ a ] . compare ( files [ a ] . length ( ) - suffix . length ( ) , suffix . length ( ) , suffix ) ! = 0 )
continue ;
result . push_back ( files [ a ] ) ;
}
return ;
}
size_t loadScriptFiles ( Core * core , color_ostream & out , const vector < std : : string > & prefix , const std : : string & folder ) {
2022-11-20 18:27:14 -07:00
static const string suffix = " .init " ;
vector < string > scriptFiles ;
2015-09-15 05:27:42 -06:00
for ( size_t a = 0 ; a < prefix . size ( ) ; a + + ) {
getFilesWithPrefixAndSuffix ( folder , prefix [ a ] , " .init " , scriptFiles ) ;
}
2022-11-20 18:27:14 -07:00
std : : sort ( scriptFiles . begin ( ) , scriptFiles . end ( ) ,
[ & ] ( const string & a , const string & b ) {
string a_base = a . substr ( 0 , a . size ( ) - suffix . size ( ) ) ;
string b_base = b . substr ( 0 , b . size ( ) - suffix . size ( ) ) ;
return a_base < b_base ;
} ) ;
2015-09-21 15:24:51 -06:00
size_t result = 0 ;
2015-09-15 05:27:42 -06:00
for ( size_t a = 0 ; a < scriptFiles . size ( ) ; a + + ) {
2015-09-21 15:24:51 -06:00
result + + ;
2022-11-20 18:27:14 -07:00
string path = " " ;
2021-09-22 14:30:10 -06:00
if ( folder ! = " . " )
path = folder + " / " ;
core - > loadScriptFile ( out , path + scriptFiles [ a ] , false ) ;
2015-09-15 05:27:42 -06:00
}
2015-09-21 15:24:51 -06:00
return result ;
2015-09-15 05:27:42 -06:00
}
2015-09-21 15:24:51 -06:00
namespace DFHack {
namespace X {
typedef state_change_event Key ;
typedef vector < string > Val ;
typedef pair < Key , Val > Entry ;
typedef vector < Entry > EntryVector ;
typedef map < Key , Val > InitVariationTable ;
EntryVector computeInitVariationTable ( void * none , . . . ) {
va_list list ;
va_start ( list , none ) ;
EntryVector result ;
while ( true ) {
2015-09-21 16:57:58 -06:00
Key key = ( Key ) va_arg ( list , int ) ;
if ( key = = SC_UNKNOWN )
2015-09-21 15:24:51 -06:00
break ;
Val val ;
2015-09-21 16:57:58 -06:00
while ( true ) {
const char * v = va_arg ( list , const char * ) ;
if ( ! v | | ! v [ 0 ] )
2015-09-21 15:24:51 -06:00
break ;
2015-09-21 16:57:58 -06:00
val . push_back ( string ( v ) ) ;
2015-09-21 15:24:51 -06:00
}
result . push_back ( Entry ( key , val ) ) ;
}
va_end ( list ) ;
return result ;
}
2015-09-21 16:57:58 -06:00
2015-09-21 15:24:51 -06:00
InitVariationTable getTable ( const EntryVector & vec ) {
return InitVariationTable ( vec . begin ( ) , vec . end ( ) ) ;
}
2015-09-15 05:27:42 -06:00
}
2015-09-21 15:24:51 -06:00
}
void Core : : handleLoadAndUnloadScripts ( color_ostream & out , state_change_event event ) {
static const X : : InitVariationTable table = X : : getTable ( X : : computeInitVariationTable ( 0 ,
2015-09-21 16:57:58 -06:00
( int ) SC_WORLD_LOADED , " onLoad " , " onLoadWorld " , " onWorldLoaded " , " " ,
( int ) SC_WORLD_UNLOADED , " onUnload " , " onUnloadWorld " , " onWorldUnloaded " , " " ,
( int ) SC_MAP_LOADED , " onMapLoad " , " onLoadMap " , " " ,
( int ) SC_MAP_UNLOADED , " onMapUnload " , " onUnloadMap " , " " ,
( int ) SC_UNKNOWN
2015-09-21 15:24:51 -06:00
) ) ;
2015-09-21 16:57:58 -06:00
2014-07-21 18:14:43 -06:00
if ( ! df : : global : : world )
2015-02-14 20:53:06 -07:00
return ;
2023-01-16 00:13:58 -07:00
std : : string rawFolder = " save/ " + ( df : : global : : world - > cur_savegame . save_dir ) + " /init " ;
2015-09-21 16:57:58 -06:00
2015-09-21 15:24:51 -06:00
auto i = table . find ( event ) ;
if ( i ! = table . end ( ) ) {
const std : : vector < std : : string > & set = i - > second ;
2022-07-10 09:54:55 -06:00
// load baseline defaults
2023-01-04 12:35:50 -07:00
this - > loadScriptFile ( out , CONFIG_PATH + " init/default. " + set [ 0 ] + " .init " , false ) ;
2022-07-10 09:54:55 -06:00
2023-01-04 12:35:50 -07:00
loadScriptFiles ( this , out , set , CONFIG_PATH + " init " ) ;
2015-09-21 15:24:51 -06:00
loadScriptFiles ( this , out , set , rawFolder ) ;
}
2015-02-07 17:33:12 -07:00
for ( auto it = state_change_scripts . begin ( ) ; it ! = state_change_scripts . end ( ) ; + + it )
{
if ( it - > event = = event )
{
if ( ! it - > save_specific )
{
2021-09-22 14:30:10 -06:00
loadScriptFile ( out , it - > path , false ) ;
2015-02-07 17:33:12 -07:00
}
else if ( it - > save_specific & & isWorldLoaded ( ) )
{
2021-09-22 14:30:10 -06:00
loadScriptFile ( out , rawFolder + it - > path , false ) ;
2015-02-07 17:33:12 -07:00
}
}
}
2013-06-09 22:45:46 -06:00
}
2012-05-17 10:04:09 -06:00
void Core : : onStateChange ( color_ostream & out , state_change_event event )
{
2015-07-23 21:24:00 -06:00
using df : : global : : gametype ;
static md5wrapper md5w ;
static std : : string ostype = " " ;
if ( ! ostype . size ( ) )
{
ostype = " unknown OS " ;
if ( vinfo ) {
switch ( vinfo - > getOS ( ) )
{
case OS_WINDOWS :
ostype = " Windows " ;
break ;
case OS_APPLE :
ostype = " OS X " ;
break ;
case OS_LINUX :
ostype = " Linux " ;
break ;
default :
break ;
}
}
}
switch ( event )
{
2023-01-03 02:01:30 -07:00
case SC_CORE_INITIALIZED :
{
auto L = Lua : : Core : : State ;
Lua : : StackUnwinder top ( L ) ;
2023-01-22 23:10:17 -07:00
Lua : : CallLuaModuleFunction ( con , L , " helpdb " , " refresh " ) ;
2023-01-03 02:01:30 -07:00
Lua : : CallLuaModuleFunction ( con , L , " script-manager " , " reload " ) ;
}
break ;
2015-07-23 21:24:00 -06:00
case SC_WORLD_LOADED :
case SC_WORLD_UNLOADED :
case SC_MAP_LOADED :
case SC_MAP_UNLOADED :
2016-08-14 10:41:09 -06:00
if ( world & & world - > cur_savegame . save_dir . size ( ) )
2015-07-23 21:24:00 -06:00
{
2023-01-16 00:13:58 -07:00
std : : string save_dir = " save/ " + world - > cur_savegame . save_dir ;
2015-09-26 12:26:44 -06:00
std : : string evtlogpath = save_dir + " /events-dfhack.log " ;
2015-07-23 21:24:00 -06:00
std : : ofstream evtlog ;
evtlog . open ( evtlogpath , std : : ios_base : : app ) ; // append
if ( evtlog . fail ( ) )
{
2015-09-26 12:26:44 -06:00
if ( DFHack : : Filesystem : : isdir ( save_dir ) )
out . printerr ( " Could not append to %s \n " , evtlogpath . c_str ( ) ) ;
2015-07-23 21:24:00 -06:00
}
else
{
char timebuf [ 30 ] ;
time_t rawtime = time ( NULL ) ;
struct tm * timeinfo = localtime ( & rawtime ) ;
strftime ( timebuf , sizeof ( timebuf ) , " [%Y-%m-%dT%H:%M:%S%z] " , timeinfo ) ;
evtlog < < timebuf ;
evtlog < < " DFHack " < < Version : : git_description ( ) < < " on " < < ostype < < " ; " ;
evtlog < < " cwd md5: " < < md5w . getHashFromString ( getHackPath ( ) ) . substr ( 0 , 10 ) < < " ; " ;
2016-08-14 10:41:09 -06:00
evtlog < < " save: " < < world - > cur_savegame . save_dir < < " ; " ;
2015-07-23 21:24:00 -06:00
evtlog < < sc_event_name ( event ) < < " ; " ;
if ( gametype )
evtlog < < " game type " < < ENUM_KEY_STR ( game_type , * gametype ) < < " ( " < < * gametype < < " ) " ;
else
evtlog < < " game type unavailable " ;
evtlog < < std : : endl ;
}
}
2023-01-03 02:01:30 -07:00
break ;
case SC_VIEWSCREEN_CHANGED :
Textures : : init ( out ) ;
break ;
2015-07-23 21:24:00 -06:00
default :
break ;
}
2015-12-22 17:34:54 -07:00
if ( event = = SC_WORLD_LOADED & & Version : : is_prerelease ( ) )
2015-12-11 18:27:46 -07:00
{
runCommand ( out , " gui/prerelease-warning " ) ;
std : : cerr < < " loaded map in prerelease build " < < std : : endl ;
}
2023-01-06 20:55:42 -07:00
if ( event = = SC_WORLD_LOADED )
{
doLoadData ( out ) ;
}
2012-12-14 20:14:38 -07:00
EventManager : : onStateChange ( out , event ) ;
2012-05-17 10:38:27 -06:00
buildings_onStateChange ( out , event ) ;
2012-05-17 10:04:09 -06:00
plug_mgr - > OnStateChange ( out , event ) ;
Lua : : Core : : onStateChange ( out , event ) ;
2013-06-09 22:45:46 -06:00
2015-02-07 17:33:12 -07:00
handleLoadAndUnloadScripts ( out , event ) ;
2018-08-26 17:26:09 -06:00
if ( event = = SC_WORLD_UNLOADED )
{
Persistence : : Internal : : clear ( ) ;
}
}
2019-05-29 17:52:03 -06:00
void Core : : doSaveData ( color_ostream & out )
2018-08-26 17:26:09 -06:00
{
2019-05-29 17:52:03 -06:00
plug_mgr - > doSaveData ( out ) ;
2018-08-26 17:26:09 -06:00
Persistence : : Internal : : save ( ) ;
}
2019-05-29 17:52:03 -06:00
void Core : : doLoadData ( color_ostream & out )
2018-08-26 17:26:09 -06:00
{
Persistence : : Internal : : load ( ) ;
2019-05-29 17:52:03 -06:00
plug_mgr - > doLoadData ( out ) ;
2012-05-17 10:04:09 -06:00
}
2011-06-14 08:13:28 -06:00
int Core : : Shutdown ( void )
{
2018-06-21 09:58:16 -06:00
if ( errorstate )
return true ;
errorstate = 1 ;
// Make sure we release main thread if this is called from main thread
2018-07-03 13:19:19 -06:00
if ( MainThread : : suspend ( ) . owns_lock ( ) )
MainThread : : suspend ( ) . unlock ( ) ;
2018-06-21 09:58:16 -06:00
2018-06-12 08:53:43 -06:00
// Make sure the console thread shutdowns before clean up to avoid any
// unlikely data races.
if ( d - > iothread . joinable ( ) ) {
con . shutdown ( ) ;
}
2018-06-21 09:58:16 -06:00
2018-06-21 08:13:22 -06:00
if ( d - > hotkeythread . joinable ( ) ) {
std : : unique_lock < std : : mutex > hot_lock ( HotkeyMutex ) ;
hotkey_set = SHUTDOWN ;
HotkeyCond . notify_one ( ) ;
}
2018-07-07 04:13:55 -06:00
ServerMain : : block ( ) ;
2018-06-21 08:13:22 -06:00
d - > hotkeythread . join ( ) ;
2018-06-21 09:58:16 -06:00
d - > iothread . join ( ) ;
2018-06-21 08:13:22 -06:00
2012-08-23 09:51:55 -06:00
CoreSuspendClaimer suspend ;
2011-06-24 21:35:29 -06:00
if ( plug_mgr )
{
delete plug_mgr ;
plug_mgr = 0 ;
}
2011-06-17 07:02:43 -06:00
// invalidate all modules
allModules . clear ( ) ;
2023-01-03 02:01:30 -07:00
Textures : : cleanup ( ) ;
2023-01-04 18:15:32 -07:00
DFSDL : : cleanup ( ) ;
2011-06-17 07:02:43 -06:00
memset ( & ( s_mods ) , 0 , sizeof ( s_mods ) ) ;
2018-07-08 13:04:53 -06:00
d . reset ( ) ;
2011-06-17 07:02:43 -06:00
return - 1 ;
}
2012-02-27 19:37:56 -07:00
// FIXME: this is HORRIBLY broken
2011-07-31 20:40:23 -06:00
// from ncurses
# define KEY_F0 0410 /* Function keys. Space for 64 */
# define KEY_F(n) (KEY_F0+(n)) /* Value of function key n */
bool Core : : ncurses_wgetch ( int in , int & out )
{
if ( ! started )
{
out = in ;
return true ;
}
if ( in > = KEY_F ( 1 ) & & in < = KEY_F ( 8 ) )
{
2023-01-01 02:03:42 -07:00
/* TODO: understand how this changes for v50
2011-07-31 20:40:23 -06:00
int idx = in - KEY_F ( 1 ) ;
// FIXME: copypasta, push into a method!
2023-01-05 18:11:01 -07:00
if ( df : : global : : plotinfo & & df : : global : : gview )
2011-07-31 20:40:23 -06:00
{
2012-08-24 03:20:08 -06:00
df : : viewscreen * ws = Gui : : getCurViewscreen ( ) ;
2012-03-03 06:38:24 -07:00
if ( strict_virtual_cast < df : : viewscreen_dwarfmodest > ( ws ) & &
2023-01-05 18:11:01 -07:00
df : : global : : plotinfo - > main . mode ! = ui_sidebar_mode : : Hotkeys & &
df : : global : : plotinfo - > main . hotkeys [ idx ] . cmd = = df : : ui_hotkey : : T_cmd : : None )
2011-07-31 20:40:23 -06:00
{
2023-01-05 18:11:01 -07:00
setHotkeyCmd ( df : : global : : plotinfo - > main . hotkeys [ idx ] . name ) ;
2012-03-03 06:38:24 -07:00
return false ;
2011-07-31 20:40:23 -06:00
}
else
{
2012-03-03 06:38:24 -07:00
out = in ;
return true ;
2011-07-31 20:40:23 -06:00
}
}
2023-01-01 02:03:42 -07:00
*/
2011-07-31 20:40:23 -06:00
}
out = in ;
return true ;
}
2022-12-23 20:05:00 -07:00
bool Core : : DFH_ncurses_key ( int key )
{
if ( getenv ( " DFHACK_HEADLESS " ) )
return true ;
int dummy ;
return ! ncurses_wgetch ( key , dummy ) ;
}
2012-06-13 18:25:15 -06:00
int UnicodeAwareSym ( const SDL : : KeyboardEvent & ke )
2012-03-09 01:26:31 -07:00
{
2012-03-09 02:07:47 -07:00
// Assume keyboard layouts don't change the order of numbers:
2012-06-13 18:15:43 -06:00
if ( ' 0 ' < = ke . ksym . sym & & ke . ksym . sym < = ' 9 ' ) return ke . ksym . sym ;
if ( SDL : : K_F1 < = ke . ksym . sym & & ke . ksym . sym < = SDL : : K_F12 ) return ke . ksym . sym ;
2012-03-09 01:26:31 -07:00
2012-05-16 08:10:07 -06:00
// These keys are mapped to the same control codes as Ctrl-?
2012-06-13 18:15:43 -06:00
switch ( ke . ksym . sym )
{
case SDL : : K_RETURN :
case SDL : : K_KP_ENTER :
case SDL : : K_TAB :
case SDL : : K_ESCAPE :
case SDL : : K_DELETE :
return ke . ksym . sym ;
default :
break ;
2012-05-16 08:10:07 -06:00
}
2012-06-13 18:15:43 -06:00
int unicode = ke . ksym . unicode ;
2012-03-09 01:26:31 -07:00
// convert Ctrl characters to their 0x40-0x5F counterparts:
if ( unicode < ' ' )
2012-06-13 18:15:43 -06:00
{
2012-03-09 01:26:31 -07:00
unicode + = ' A ' - 1 ;
}
2012-03-09 02:07:47 -07:00
// convert A-Z to their a-z counterparts:
2015-07-29 07:34:04 -06:00
if ( ' A ' < = unicode & & unicode < = ' Z ' )
2012-03-09 02:07:47 -07:00
{
2012-03-09 01:26:31 -07:00
unicode + = ' a ' - ' A ' ;
2012-06-13 18:15:43 -06:00
}
2012-03-09 02:07:47 -07:00
// convert various other punctuation marks:
if ( ' \" ' = = unicode ) unicode = ' \' ' ;
if ( ' + ' = = unicode ) unicode = ' = ' ;
if ( ' : ' = = unicode ) unicode = ' ; ' ;
if ( ' < ' = = unicode ) unicode = ' , ' ;
if ( ' > ' = = unicode ) unicode = ' . ' ;
if ( ' ? ' = = unicode ) unicode = ' / ' ;
if ( ' { ' = = unicode ) unicode = ' [ ' ;
if ( ' | ' = = unicode ) unicode = ' \\ ' ;
if ( ' } ' = = unicode ) unicode = ' ] ' ;
if ( ' ~ ' = = unicode ) unicode = ' ` ' ;
return unicode ;
2012-03-09 01:26:31 -07:00
}
2012-06-13 18:15:43 -06:00
2012-02-28 04:59:02 -07:00
//MEMO: return false if event is consumed
2012-06-13 18:15:43 -06:00
int Core : : DFH_SDL_Event ( SDL : : Event * ev )
2011-07-07 19:55:37 -06:00
{
2011-07-09 03:33:58 -06:00
// do NOT process events before we are ready.
2012-02-27 19:37:56 -07:00
if ( ! started ) return true ;
2011-07-09 03:33:58 -06:00
if ( ! ev )
2012-02-27 19:37:56 -07:00
return true ;
2021-08-06 08:10:42 -06:00
if ( ev - > type = = SDL : : ET_ACTIVEEVENT & & ev - > active . gain )
{
// clear modstate when gaining focus in case alt-tab was used when
// losing focus and modstate is now incorrectly set
modstate = 0 ;
return true ;
}
if ( ev - > type = = SDL : : ET_KEYDOWN | | ev - > type = = SDL : : ET_KEYUP )
2011-07-07 19:55:37 -06:00
{
2012-06-13 18:15:43 -06:00
auto ke = ( SDL : : KeyboardEvent * ) ev ;
2012-02-11 11:24:44 -07:00
2015-01-11 10:48:52 -07:00
if ( ke - > ksym . sym = = SDL : : K_LSHIFT | | ke - > ksym . sym = = SDL : : K_RSHIFT )
2015-01-17 06:56:49 -07:00
modstate = ( ev - > type = = SDL : : ET_KEYDOWN ) ? modstate | DFH_MOD_SHIFT : modstate & ~ DFH_MOD_SHIFT ;
2015-01-11 10:48:52 -07:00
else if ( ke - > ksym . sym = = SDL : : K_LCTRL | | ke - > ksym . sym = = SDL : : K_RCTRL )
2015-01-17 06:56:49 -07:00
modstate = ( ev - > type = = SDL : : ET_KEYDOWN ) ? modstate | DFH_MOD_CTRL : modstate & ~ DFH_MOD_CTRL ;
2015-01-11 10:48:52 -07:00
else if ( ke - > ksym . sym = = SDL : : K_LALT | | ke - > ksym . sym = = SDL : : K_RALT )
2015-01-17 06:56:49 -07:00
modstate = ( ev - > type = = SDL : : ET_KEYDOWN ) ? modstate | DFH_MOD_ALT : modstate & ~ DFH_MOD_ALT ;
2015-01-11 10:48:52 -07:00
else if ( ke - > state = = SDL : : BTN_PRESSED & & ! hotkey_states [ ke - > ksym . sym ] )
2011-07-09 03:33:58 -06:00
{
2012-06-13 18:15:43 -06:00
hotkey_states [ ke - > ksym . sym ] = true ;
2011-12-30 12:25:50 -07:00
2012-03-09 01:26:31 -07:00
// Use unicode so Windows gives the correct value for the
// user's Input Language
2015-07-29 08:32:19 -06:00
if ( ke - > ksym . unicode & & ( ( ke - > ksym . unicode & 0xff80 ) = = 0 ) )
2012-03-09 02:07:47 -07:00
{
int key = UnicodeAwareSym ( * ke ) ;
2015-01-11 10:48:52 -07:00
SelectHotkey ( key , modstate ) ;
2012-03-09 01:26:31 -07:00
}
else
{
// Pretend non-ascii characters don't happen:
2015-01-11 10:48:52 -07:00
SelectHotkey ( ke - > ksym . sym , modstate ) ;
2012-03-09 01:26:31 -07:00
}
2011-07-09 03:33:58 -06:00
}
2012-06-13 18:15:43 -06:00
else if ( ke - > state = = SDL : : BTN_RELEASED )
2011-07-09 03:33:58 -06:00
{
2012-06-13 18:15:43 -06:00
hotkey_states [ ke - > ksym . sym ] = false ;
2011-07-09 03:33:58 -06:00
}
2011-07-07 19:55:37 -06:00
}
2012-02-27 19:37:56 -07:00
return true ;
2011-07-09 03:33:58 -06:00
// do stuff with the events...
2011-07-07 19:55:37 -06:00
}
2011-12-30 12:25:50 -07:00
bool Core : : SelectHotkey ( int sym , int modifiers )
{
// Find the topmost viewscreen
2023-01-05 18:11:01 -07:00
if ( ! df : : global : : gview | | ! df : : global : : plotinfo )
2011-12-30 12:25:50 -07:00
return false ;
df : : viewscreen * screen = & df : : global : : gview - > view ;
while ( screen - > child )
screen = screen - > child ;
2012-06-13 18:15:43 -06:00
if ( sym = = SDL : : K_KP_ENTER )
sym = SDL : : K_RETURN ;
2012-05-19 11:31:42 -06:00
2011-12-30 12:25:50 -07:00
std : : string cmd ;
2012-05-19 11:31:42 -06:00
2022-11-07 13:17:47 -07:00
DEBUG ( keybinding ) . print ( " checking hotkeys for sym=%d, modifiers=%x \n " , sym , modifiers ) ;
2011-12-30 12:25:50 -07:00
{
2018-06-12 08:33:12 -06:00
std : : lock_guard < std : : mutex > lock ( HotkeyMutex ) ;
2012-05-19 11:31:42 -06:00
2011-12-30 12:25:50 -07:00
// Check the internal keybindings
std : : vector < KeyBinding > & bindings = key_bindings [ sym ] ;
for ( int i = bindings . size ( ) - 1 ; i > = 0 ; - - i ) {
2022-11-07 13:17:47 -07:00
auto & binding = bindings [ i ] ;
DEBUG ( keybinding ) . print ( " examining hotkey with commandline: '%s' \n " , binding . cmdline . c_str ( ) ) ;
if ( binding . modifiers ! = modifiers ) {
DEBUG ( keybinding ) . print ( " skipping keybinding due to modifiers mismatch: 0x%x != 0x%x \n " ,
binding . modifiers , modifiers ) ;
2011-12-30 12:25:50 -07:00
continue ;
2022-11-07 13:17:47 -07:00
}
2023-01-27 11:22:52 -07:00
if ( ! binding . focus . empty ( ) ) {
// TODO: understand more about this to figure out if this solution works
bool found = false ;
std : : vector < std : : string > focusStrings = Gui : : getFocusStrings ( Core : : getTopViewscreen ( ) ) ;
for ( std : : string focusString : focusStrings ) {
if ( prefix_matches ( binding . focus , focusString ) ) {
found = true ;
}
}
if ( ! found ) {
2023-02-01 15:22:56 -07:00
// TODO: fix error: format ‘ %s’ expects argument of type ‘ char*’ , but argument 3 has type ‘ std::string {aka std::basic_string<char>}’ [-Werror=format=]
2023-01-27 11:22:52 -07:00
DEBUG ( keybinding ) . print ( " skipping keybinding due to focus string mismatch: '%s' !~ '%s' \n " ,
join_strings ( " , " , focusStrings ) , binding . focus . c_str ( ) ) ;
continue ;
}
2022-11-07 13:17:47 -07:00
}
if ( ! plug_mgr - > CanInvokeHotkey ( binding . command [ 0 ] , screen ) ) {
DEBUG ( keybinding ) . print ( " skipping keybinding due to hotkey guard rejection (command: '%s') \n " ,
binding . command [ 0 ] . c_str ( ) ) ;
2011-12-30 12:25:50 -07:00
continue ;
2022-11-07 13:17:47 -07:00
}
cmd = binding . cmdline ;
DEBUG ( keybinding ) . print ( " matched hotkey \n " ) ;
2011-12-30 12:25:50 -07:00
break ;
}
if ( cmd . empty ( ) ) {
// Check the hotkey keybindings
2012-06-13 18:15:43 -06:00
int idx = sym - SDL : : K_F1 ;
2011-12-30 12:25:50 -07:00
if ( idx > = 0 & & idx < 8 )
{
2023-01-01 02:03:42 -07:00
/* TODO: understand how this changes for v50
2011-12-30 12:25:50 -07:00
if ( modifiers & 1 )
idx + = 8 ;
2012-01-01 12:05:45 -07:00
if ( strict_virtual_cast < df : : viewscreen_dwarfmodest > ( screen ) & &
2023-01-05 18:11:01 -07:00
df : : global : : plotinfo - > main . mode ! = ui_sidebar_mode : : Hotkeys & &
df : : global : : plotinfo - > main . hotkeys [ idx ] . cmd = = df : : ui_hotkey : : T_cmd : : None )
2011-12-30 12:25:50 -07:00
{
2023-01-05 18:11:01 -07:00
cmd = df : : global : : plotinfo - > main . hotkeys [ idx ] . name ;
2011-12-30 12:25:50 -07:00
}
2023-01-01 02:03:42 -07:00
*/
2011-12-30 12:25:50 -07:00
}
}
}
if ( ! cmd . empty ( ) ) {
setHotkeyCmd ( cmd ) ;
return true ;
}
else
return false ;
}
2012-05-19 11:31:42 -06:00
static bool parseKeySpec ( std : : string keyspec , int * psym , int * pmod , std : : string * pfocus )
2011-12-30 12:25:50 -07:00
{
* pmod = 0 ;
2012-05-19 11:31:42 -06:00
if ( pfocus )
{
* pfocus = " " ;
size_t idx = keyspec . find ( ' @ ' ) ;
if ( idx ! = std : : string : : npos )
{
* pfocus = keyspec . substr ( idx + 1 ) ;
keyspec = keyspec . substr ( 0 , idx ) ;
}
}
2011-12-30 12:25:50 -07:00
// ugh, ugly
for ( ; ; ) {
if ( keyspec . size ( ) > 6 & & keyspec . substr ( 0 , 6 ) = = " Shift- " ) {
* pmod | = 1 ;
keyspec = keyspec . substr ( 6 ) ;
} else if ( keyspec . size ( ) > 5 & & keyspec . substr ( 0 , 5 ) = = " Ctrl- " ) {
* pmod | = 2 ;
keyspec = keyspec . substr ( 5 ) ;
} else if ( keyspec . size ( ) > 4 & & keyspec . substr ( 0 , 4 ) = = " Alt- " ) {
* pmod | = 4 ;
keyspec = keyspec . substr ( 4 ) ;
2015-01-11 10:48:52 -07:00
} else
2011-12-30 12:25:50 -07:00
break ;
}
if ( keyspec . size ( ) = = 1 & & keyspec [ 0 ] > = ' A ' & & keyspec [ 0 ] < = ' Z ' ) {
2012-06-13 18:15:43 -06:00
* psym = SDL : : K_a + ( keyspec [ 0 ] - ' A ' ) ;
2011-12-30 12:25:50 -07:00
return true ;
2022-07-11 18:23:56 -06:00
} else if ( keyspec . size ( ) = = 1 & & keyspec [ 0 ] = = ' ` ' ) {
* psym = SDL : : K_BACKQUOTE ;
return true ;
2015-09-02 13:56:53 -06:00
} else if ( keyspec . size ( ) = = 1 & & keyspec [ 0 ] > = ' 0 ' & & keyspec [ 0 ] < = ' 9 ' ) {
* psym = SDL : : K_0 + ( keyspec [ 0 ] - ' 0 ' ) ;
return true ;
2011-12-30 12:25:50 -07:00
} else if ( keyspec . size ( ) = = 2 & & keyspec [ 0 ] = = ' F ' & & keyspec [ 1 ] > = ' 1 ' & & keyspec [ 1 ] < = ' 9 ' ) {
2012-06-13 18:15:43 -06:00
* psym = SDL : : K_F1 + ( keyspec [ 1 ] - ' 1 ' ) ;
2011-12-30 12:25:50 -07:00
return true ;
2015-09-02 13:56:53 -06:00
} else if ( keyspec . size ( ) = = 3 & & keyspec . substr ( 0 , 2 ) = = " F1 " & & keyspec [ 2 ] > = ' 0 ' & & keyspec [ 2 ] < = ' 2 ' ) {
* psym = SDL : : K_F10 + ( keyspec [ 2 ] - ' 0 ' ) ;
return true ;
2012-05-19 11:31:42 -06:00
} else if ( keyspec = = " Enter " ) {
2012-06-13 18:15:43 -06:00
* psym = SDL : : K_RETURN ;
2012-05-19 11:31:42 -06:00
return true ;
2011-12-30 12:25:50 -07:00
} else
2012-03-04 17:34:04 -07:00
return false ;
2011-12-30 12:25:50 -07:00
}
bool Core : : ClearKeyBindings ( std : : string keyspec )
{
int sym , mod ;
2012-05-19 11:31:42 -06:00
std : : string focus ;
if ( ! parseKeySpec ( keyspec , & sym , & mod , & focus ) )
2011-12-30 12:25:50 -07:00
return false ;
2018-06-12 08:33:12 -06:00
std : : lock_guard < std : : mutex > lock ( HotkeyMutex ) ;
2011-12-30 12:25:50 -07:00
std : : vector < KeyBinding > & bindings = key_bindings [ sym ] ;
for ( int i = bindings . size ( ) - 1 ; i > = 0 ; - - i ) {
2012-05-19 11:31:42 -06:00
if ( bindings [ i ] . modifiers = = mod & & prefix_matches ( focus , bindings [ i ] . focus ) )
2011-12-30 12:25:50 -07:00
bindings . erase ( bindings . begin ( ) + i ) ;
}
return true ;
}
bool Core : : AddKeyBinding ( std : : string keyspec , std : : string cmdline )
{
2015-08-06 18:41:54 -06:00
size_t at_pos = keyspec . find ( ' @ ' ) ;
if ( at_pos ! = std : : string : : npos )
{
std : : string raw_spec = keyspec . substr ( 0 , at_pos ) ;
std : : string raw_focus = keyspec . substr ( at_pos + 1 ) ;
if ( raw_focus . find ( ' | ' ) ! = std : : string : : npos )
{
std : : vector < std : : string > focus_strings ;
split_string ( & focus_strings , raw_focus , " | " ) ;
for ( size_t i = 0 ; i < focus_strings . size ( ) ; i + + )
{
if ( ! AddKeyBinding ( raw_spec + " @ " + focus_strings [ i ] , cmdline ) )
return false ;
}
return true ;
}
}
2011-12-30 12:25:50 -07:00
int sym ;
KeyBinding binding ;
2012-05-19 11:31:42 -06:00
if ( ! parseKeySpec ( keyspec , & sym , & binding . modifiers , & binding . focus ) )
2011-12-30 12:25:50 -07:00
return false ;
cheap_tokenise ( cmdline , binding . command ) ;
if ( binding . command . empty ( ) )
return false ;
2018-06-12 08:33:12 -06:00
std : : lock_guard < std : : mutex > lock ( HotkeyMutex ) ;
2011-12-30 12:25:50 -07:00
2011-12-31 02:25:46 -07:00
// Don't add duplicates
std : : vector < KeyBinding > & bindings = key_bindings [ sym ] ;
for ( int i = bindings . size ( ) - 1 ; i > = 0 ; - - i ) {
if ( bindings [ i ] . modifiers = = binding . modifiers & &
2012-05-19 11:31:42 -06:00
bindings [ i ] . cmdline = = cmdline & &
bindings [ i ] . focus = = binding . focus )
2011-12-31 02:25:46 -07:00
return true ;
}
2011-12-30 12:25:50 -07:00
binding . cmdline = cmdline ;
2011-12-31 02:25:46 -07:00
bindings . push_back ( binding ) ;
2011-12-30 12:25:50 -07:00
return true ;
}
2011-12-31 02:25:46 -07:00
std : : vector < std : : string > Core : : ListKeyBindings ( std : : string keyspec )
2011-12-30 12:25:50 -07:00
{
2011-12-31 02:25:46 -07:00
int sym , mod ;
std : : vector < std : : string > rv ;
2015-03-30 17:03:16 -06:00
std : : string focus ;
if ( ! parseKeySpec ( keyspec , & sym , & mod , & focus ) )
2011-12-31 02:25:46 -07:00
return rv ;
2018-06-12 08:33:12 -06:00
std : : lock_guard < std : : mutex > lock ( HotkeyMutex ) ;
2011-12-31 02:25:46 -07:00
std : : vector < KeyBinding > & bindings = key_bindings [ sym ] ;
for ( int i = bindings . size ( ) - 1 ; i > = 0 ; - - i ) {
2015-03-30 17:03:16 -06:00
if ( focus . size ( ) & & focus ! = bindings [ i ] . focus )
continue ;
2011-12-31 02:25:46 -07:00
if ( bindings [ i ] . modifiers = = mod )
2012-05-19 11:31:42 -06:00
{
std : : string cmd = bindings [ i ] . cmdline ;
if ( ! bindings [ i ] . focus . empty ( ) )
cmd = " @ " + bindings [ i ] . focus + " : " + cmd ;
rv . push_back ( cmd ) ;
}
2011-12-31 02:25:46 -07:00
}
return rv ;
2011-12-30 12:25:50 -07:00
}
2017-06-18 15:39:54 -06:00
bool Core : : AddAlias ( const std : : string & name , const std : : vector < std : : string > & command , bool replace )
{
2018-06-12 08:33:12 -06:00
std : : lock_guard < std : : recursive_mutex > lock ( alias_mutex ) ;
2017-06-18 15:39:54 -06:00
if ( ! IsAlias ( name ) | | replace )
{
aliases [ name ] = command ;
return true ;
}
return false ;
}
bool Core : : RemoveAlias ( const std : : string & name )
{
2018-06-12 08:33:12 -06:00
std : : lock_guard < std : : recursive_mutex > lock ( alias_mutex ) ;
2017-06-18 15:39:54 -06:00
if ( IsAlias ( name ) )
{
aliases . erase ( name ) ;
return true ;
}
return false ;
}
bool Core : : IsAlias ( const std : : string & name )
{
2018-06-12 08:33:12 -06:00
std : : lock_guard < std : : recursive_mutex > lock ( alias_mutex ) ;
2017-06-18 15:39:54 -06:00
return aliases . find ( name ) ! = aliases . end ( ) ;
}
bool Core : : RunAlias ( color_ostream & out , const std : : string & name ,
const std : : vector < std : : string > & parameters , command_result & result )
{
2018-06-12 08:33:12 -06:00
std : : lock_guard < std : : recursive_mutex > lock ( alias_mutex ) ;
2017-06-18 15:39:54 -06:00
if ( ! IsAlias ( name ) )
{
return false ;
}
const string & first = aliases [ name ] [ 0 ] ;
vector < string > parts ( aliases [ name ] . begin ( ) + 1 , aliases [ name ] . end ( ) ) ;
parts . insert ( parts . end ( ) , parameters . begin ( ) , parameters . end ( ) ) ;
result = runCommand ( out , first , parts ) ;
return true ;
}
std : : map < std : : string , std : : vector < std : : string > > Core : : ListAliases ( )
{
2018-06-12 08:33:12 -06:00
std : : lock_guard < std : : recursive_mutex > lock ( alias_mutex ) ;
2017-06-18 15:39:54 -06:00
return aliases ;
}
std : : string Core : : GetAliasCommand ( const std : : string & name , const std : : string & default_ )
{
2018-06-12 08:33:12 -06:00
std : : lock_guard < std : : recursive_mutex > lock ( alias_mutex ) ;
2017-06-18 15:39:54 -06:00
if ( IsAlias ( name ) )
return join_strings ( " " , aliases [ name ] ) ;
else
return default_ ;
}
2011-07-24 22:24:34 -06:00
2015-08-06 18:41:54 -06:00
/////////////////
// ClassNameCheck
/////////////////
// Since there is no Process.cpp, put ClassNameCheck stuff in Core.cpp
2011-07-24 22:24:34 -06:00
static std : : set < std : : string > known_class_names ;
static std : : map < std : : string , void * > known_vptrs ;
ClassNameCheck : : ClassNameCheck ( std : : string _name ) : name ( _name ) , vptr ( 0 )
{
known_class_names . insert ( name ) ;
}
ClassNameCheck & ClassNameCheck : : operator = ( const ClassNameCheck & b )
{
name = b . name ; vptr = b . vptr ; return * this ;
}
bool ClassNameCheck : : operator ( ) ( Process * p , void * ptr ) const {
if ( vptr = = 0 & & p - > readClassName ( ptr ) = = name )
{
vptr = ptr ;
known_vptrs [ name ] = ptr ;
}
return ( vptr & & vptr = = ptr ) ;
}
void ClassNameCheck : : getKnownClassNames ( std : : vector < std : : string > & names )
{
std : : set < std : : string > : : iterator it = known_class_names . begin ( ) ;
for ( ; it ! = known_class_names . end ( ) ; it + + )
names . push_back ( * it ) ;
}
2012-10-27 23:34:50 -06:00
MemoryPatcher : : MemoryPatcher ( Process * p_ ) : p ( p_ )
2012-10-27 11:58:40 -06:00
{
if ( ! p )
2018-07-08 13:04:53 -06:00
p = Core : : getInstance ( ) . p . get ( ) ;
2012-10-27 11:58:40 -06:00
}
MemoryPatcher : : ~ MemoryPatcher ( )
{
close ( ) ;
}
bool MemoryPatcher : : verifyAccess ( void * target , size_t count , bool write )
2012-08-17 04:32:04 -06:00
{
uint8_t * sptr = ( uint8_t * ) target ;
uint8_t * eptr = sptr + count ;
// Find the valid memory ranges
2012-10-27 11:58:40 -06:00
if ( ranges . empty ( ) )
p - > getMemRanges ( ranges ) ;
2012-08-17 04:32:04 -06:00
2012-10-27 11:58:40 -06:00
// Find the ranges that this area spans
2012-08-17 04:32:04 -06:00
unsigned start = 0 ;
while ( start < ranges . size ( ) & & ranges [ start ] . end < = sptr )
start + + ;
if ( start > = ranges . size ( ) | | ranges [ start ] . start > sptr )
return false ;
unsigned end = start + 1 ;
while ( end < ranges . size ( ) & & ranges [ end ] . start < eptr )
{
if ( ranges [ end ] . start ! = ranges [ end - 1 ] . end )
return false ;
end + + ;
}
if ( ranges [ end - 1 ] . end < eptr )
return false ;
// Verify current permissions
for ( unsigned i = start ; i < end ; i + + )
if ( ! ranges [ i ] . valid | | ! ( ranges [ i ] . read | | ranges [ i ] . execute ) | | ranges [ i ] . shared )
return false ;
2012-10-27 11:58:40 -06:00
// Apply writable permissions & update
for ( unsigned i = start ; i < end ; i + + )
2012-08-17 04:32:04 -06:00
{
2012-10-27 11:58:40 -06:00
auto & perms = ranges [ i ] ;
2012-10-28 01:50:28 -06:00
if ( ( perms . write | | ! write ) & & perms . read )
2012-10-27 11:58:40 -06:00
continue ;
save . push_back ( perms ) ;
2012-08-17 04:32:04 -06:00
perms . write = perms . read = true ;
2012-10-27 11:58:40 -06:00
if ( ! p - > setPermisions ( perms , perms ) )
return false ;
2012-08-17 04:32:04 -06:00
}
2012-10-27 11:58:40 -06:00
return true ;
}
bool MemoryPatcher : : write ( void * target , const void * src , size_t size )
{
if ( ! makeWritable ( target , size ) )
return false ;
memmove ( target , src , size ) ;
return true ;
}
2012-08-17 04:32:04 -06:00
2012-10-27 11:58:40 -06:00
void MemoryPatcher : : close ( )
{
for ( size_t i = 0 ; i < save . size ( ) ; i + + )
p - > setPermisions ( save [ i ] , save [ i ] ) ;
save . clear ( ) ;
ranges . clear ( ) ;
} ;
bool Process : : patchMemory ( void * target , const void * src , size_t count )
{
MemoryPatcher patcher ( this ) ;
2012-08-17 04:32:04 -06:00
2012-10-27 11:58:40 -06:00
return patcher . write ( target , src , count ) ;
2012-08-17 04:32:04 -06:00
}
2011-06-17 07:02:43 -06:00
/*******************************************************************************
M O D U L E S
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# define MODULE_GETTER(TYPE) \
TYPE * Core : : get # # TYPE ( ) \
{ \
2011-06-19 17:12:07 -06:00
if ( errorstate ) return NULL ; \
2011-06-17 07:02:43 -06:00
if ( ! s_mods . p # # TYPE ) \
{ \
2018-07-08 21:48:39 -06:00
std : : unique_ptr < Module > mod = create # # TYPE ( ) ; \
s_mods . p # # TYPE = ( TYPE * ) mod . get ( ) ; \
allModules . push_back ( std : : move ( mod ) ) ; \
2011-06-17 07:02:43 -06:00
} \
return s_mods . p # # TYPE ; \
2011-06-14 08:13:28 -06:00
}
2011-06-17 07:02:43 -06:00
MODULE_GETTER ( Materials ) ;
2011-12-07 12:37:09 -07:00
MODULE_GETTER ( Graphic ) ;