2015-02-14 20:53:06 -07:00
# include "Core.h"
# include "Console.h"
# include "PluginManager.h"
# include "MemAccess.h"
# include "MiscUtils.h"
# include <tinythread.h> //not sure if correct
# include <string>
# include <vector>
# include <sstream>
2016-08-24 14:20:30 -06:00
# include "memutils.h"
2015-02-14 20:53:06 -07:00
using std : : vector ;
using std : : string ;
using namespace DFHack ;
uint64_t timeLast = 0 ;
static tthread : : mutex * mymutex = 0 ;
DFHACK_PLUGIN_IS_ENABLED ( is_enabled ) ;
struct memory_data
{
void * addr ;
size_t len ;
size_t refresh ;
int state ;
uint8_t * buf , * lbuf ;
vector < t_memrange > ranges ;
} memdata ;
enum HEXVIEW_STATES
{
STATE_OFF , STATE_ON
} ;
command_result memview ( color_ostream & out , vector < string > & parameters ) ;
DFHACK_PLUGIN ( " memview " ) ;
DFhackCExport command_result plugin_init ( color_ostream & out , std : : vector < PluginCommand > & commands )
{
2015-11-09 21:59:35 -07:00
commands . push_back ( PluginCommand ( " memview " , " Shows DF memory in real time. " , memview , false , " Shows memory in real time. \n Params: adrr length refresh_rate. If addr==0 then stop viewing. " ) ) ;
2015-02-14 20:53:06 -07:00
memdata . state = STATE_OFF ;
mymutex = new tthread : : mutex ;
return CR_OK ;
}
size_t convert ( const std : : string & p , bool ishex = false )
{
size_t ret ;
std : : stringstream conv ;
if ( ishex )
conv < < std : : hex ;
conv < < p ;
conv > > ret ;
return ret ;
}
2018-02-03 16:10:16 -07:00
bool isAddr ( void * trg , vector < t_memrange > & ranges )
{
for ( auto & r : ranges )
if ( r . isInRange ( trg ) )
return true ;
return false ;
}
bool isAddrAt ( uintptr_t * trg , vector < t_memrange > & ranges )
2015-02-14 20:53:06 -07:00
{
if ( trg [ 0 ] % 4 = = 0 )
for ( size_t i = 0 ; i < ranges . size ( ) ; i + + )
if ( ranges [ i ] . isInRange ( ( void * ) trg [ 0 ] ) )
return true ;
return false ;
}
void outputHex ( uint8_t * buf , uint8_t * lbuf , size_t len , size_t start , color_ostream & con , vector < t_memrange > & ranges )
{
const size_t page_size = 16 ;
for ( size_t i = 0 ; i < len ; i + = page_size )
{
//con.gotoxy(1,i/page_size+1);
2020-02-05 19:29:16 -07:00
con . print ( " 0x%08zX " , i + start ) ;
2015-02-14 20:53:06 -07:00
for ( size_t j = 0 ; ( j < page_size ) & & ( i + j < len ) ; j + + )
{
2016-07-28 09:40:02 -06:00
if ( j % sizeof ( void * ) = = 0 )
2015-02-14 20:53:06 -07:00
{
con . reset_color ( ) ;
2018-02-03 16:10:16 -07:00
if ( isAddrAt ( ( uintptr_t * ) ( buf + j + i ) , ranges ) )
2015-02-14 20:53:06 -07:00
con . color ( COLOR_LIGHTRED ) ; //coloring in the middle does not work
//TODO make something better?
}
if ( lbuf [ j + i ] ! = buf [ j + i ] )
con . print ( " *%02X " , buf [ j + i ] ) ; //if modfied show a star
else
con . print ( " %02X " , buf [ j + i ] ) ;
}
con . reset_color ( ) ;
con . print ( " | " ) ;
for ( size_t j = 0 ; ( j < page_size ) & & ( i + j < len ) ; j + + )
if ( ( buf [ j + i ] > 31 ) & & ( buf [ j + i ] < 128 ) ) //only printable ascii
con . print ( " %c " , buf [ j + i ] ) ;
else
con . print ( " . " ) ;
2015-05-09 17:41:13 -06:00
con . print ( " \n " ) ;
2015-02-14 20:53:06 -07:00
}
con . print ( " \n " ) ;
}
void Deinit ( )
{
if ( memdata . state = = STATE_ON )
{
is_enabled = false ;
memdata . state = STATE_OFF ;
delete [ ] memdata . buf ;
delete [ ] memdata . lbuf ;
}
}
2018-02-03 16:10:16 -07:00
size_t detect_size ( void * addr ) {
size_t * size = ( size_t * ) ( ( char * ) addr - 16 ) ;
2020-02-10 10:57:27 -07:00
uint32_t * tag = ( uint32_t * ) ( ( char * ) addr - 8 ) ;
2020-02-08 19:17:53 -07:00
if ( isAddr ( size , memdata . ranges ) & & ( * tag = = 0x11223344 | | * tag = = 0xdfdf4ac8 ) ) {
2018-02-03 16:10:16 -07:00
return * size ;
}
// default
return 20 * 16 ;
}
2015-02-14 20:53:06 -07:00
DFhackCExport command_result plugin_onupdate ( color_ostream & out )
{
mymutex - > lock ( ) ;
if ( memdata . state = = STATE_OFF )
{
mymutex - > unlock ( ) ;
return CR_OK ;
}
//Console &con=out;
uint64_t time2 = GetTimeMs64 ( ) ;
uint64_t delta = time2 - timeLast ;
if ( memdata . refresh ! = 0 )
if ( delta < memdata . refresh )
{
mymutex - > unlock ( ) ;
return CR_OK ;
}
timeLast = time2 ;
Core : : getInstance ( ) . p - > read ( memdata . addr , memdata . len , memdata . buf ) ;
outputHex ( memdata . buf , memdata . lbuf , memdata . len , ( size_t ) memdata . addr , out , memdata . ranges ) ;
memcpy ( memdata . lbuf , memdata . buf , memdata . len ) ;
if ( memdata . refresh = = 0 )
Deinit ( ) ;
mymutex - > unlock ( ) ;
return CR_OK ;
}
command_result memview ( color_ostream & out , vector < string > & parameters )
{
mymutex - > lock ( ) ;
Core : : getInstance ( ) . p - > getMemRanges ( memdata . ranges ) ;
2016-08-24 14:20:30 -06:00
if ( parameters . empty ( ) )
{
memdata . addr = 0 ;
}
else if ( toLower ( parameters [ 0 ] . substr ( 0 , 2 ) ) = = " 0x " )
{
memdata . addr = ( void * ) convert ( parameters [ 0 ] , true ) ;
}
else
{
memdata . addr = memutils : : lua_expr_to_addr ( parameters [ 0 ] . c_str ( ) ) ;
}
2015-02-14 20:53:06 -07:00
if ( memdata . addr = = 0 )
{
Deinit ( ) ;
memdata . state = STATE_OFF ;
is_enabled = false ;
mymutex - > unlock ( ) ;
return CR_OK ;
}
else
{
Deinit ( ) ;
bool isValid = false ;
for ( size_t i = 0 ; i < memdata . ranges . size ( ) ; i + + )
if ( memdata . ranges [ i ] . isInRange ( memdata . addr ) )
isValid = true ;
if ( ! isValid )
{
2018-06-20 10:03:14 -06:00
out . printerr ( " Invalid address: %p \n " , memdata . addr ) ;
2015-02-14 20:53:06 -07:00
mymutex - > unlock ( ) ;
return CR_OK ;
}
is_enabled = true ;
memdata . state = STATE_ON ;
}
2018-02-03 16:10:16 -07:00
if ( vector_get ( parameters , 1 , string ( " a " ) ) . substr ( 0 , 1 ) = = " a " )
memdata . len = detect_size ( memdata . addr ) ;
else if ( parameters . size ( ) > 1 )
2015-02-14 20:53:06 -07:00
memdata . len = convert ( parameters [ 1 ] ) ;
else
memdata . len = 20 * 16 ;
if ( parameters . size ( ) > 2 )
memdata . refresh = convert ( parameters [ 2 ] ) ;
else
memdata . refresh = 0 ;
memdata . buf = new uint8_t [ memdata . len ] ;
memdata . lbuf = new uint8_t [ memdata . len ] ;
Core : : getInstance ( ) . p - > getMemRanges ( memdata . ranges ) ;
mymutex - > unlock ( ) ;
return CR_OK ;
}
DFhackCExport command_result plugin_shutdown ( color_ostream & out )
{
mymutex - > lock ( ) ;
Deinit ( ) ;
delete mymutex ;
mymutex - > unlock ( ) ;
return CR_OK ;
}