2011-01-18 03:36:43 +00:00
using System ;
using System.Collections.Generic ;
using System.ComponentModel ;
using System.Data ;
using System.Drawing ;
using System.Linq ;
using System.Text ;
using System.Windows.Forms ;
2011-01-18 17:17:14 +00:00
using System.IO ;
2011-01-19 02:49:47 +00:00
using System.Globalization ;
2011-01-18 03:36:43 +00:00
namespace BizHawk.MultiClient
{
public partial class RamWatch : Form
{
2011-01-19 05:46:08 +00:00
//TODO:
//implement separator feature
2011-01-20 03:28:29 +00:00
//Display value differently based on signed or hex, endian, type
//Currently address is 4 digit hex, but at some point it needs to be smart enough to adjust size based on the emulator core used
2011-01-21 04:32:32 +00:00
//Make Edit/Add/Duplicate Watch windows appear in relation to the listview box
//Make a context menu for add/remove/Dup/etc, make the context menu & edit watch windows appear in relation to where they right clicked
2011-01-21 05:13:47 +00:00
//TODO: Call AskSave in main client close function
//TODO: make so that only 1 instance of ram watch can be open at once (so that ask save can be reliable, as well as the watch button in ram search dialogs
2011-01-20 02:28:38 +00:00
int defaultWidth ; //For saving the default size of the dialog, so the user can restore if desired
int defaultHeight ;
2011-01-20 00:17:49 +00:00
List < Watch > watchList = new List < Watch > ( ) ;
string currentWatchFile = "" ;
2011-01-21 04:58:07 +00:00
bool changes = false ;
2011-01-20 00:17:49 +00:00
2011-01-21 05:44:24 +00:00
public void UpdateValues ( )
{
for ( int x = 0 ; x < watchList . Count ; x + + )
{
watchList [ x ] . value = Global . Emulator . MainMemory . PeekByte ( watchList [ x ] . address ) ;
//TODO: readbytes based on type, size, endian values
}
2011-01-21 15:05:15 +00:00
DisplayWatchList ( ) ;
2011-01-21 05:44:24 +00:00
}
2011-01-18 03:36:43 +00:00
public RamWatch ( )
{
InitializeComponent ( ) ;
}
2011-01-18 17:17:14 +00:00
public int HowMany ( string str , char c ) //Shouldn't something like this exist already? Counts how many times c in in str
{
int count = 0 ;
for ( int x = 0 ; x < str . Length ; x + + )
{
if ( str [ x ] = = c )
count + + ;
}
return count ;
}
2011-01-21 05:13:47 +00:00
public bool AskSave ( )
{
if ( changes )
{
DialogResult result = MessageBox . Show ( "Save Changes?" , "Ram Watch" , MessageBoxButtons . YesNoCancel , MessageBoxIcon . Question , MessageBoxDefaultButton . Button3 ) ;
if ( result = = DialogResult . Yes )
{
//TOOD: Do quicksave if filename, else save as
if ( string . Compare ( currentWatchFile , "" ) = = 0 )
{
SaveAs ( ) ;
}
else
SaveWatchFile ( currentWatchFile ) ;
return true ;
}
else if ( result = = DialogResult . No )
return true ;
else if ( result = = DialogResult . Cancel )
return false ;
}
return true ;
}
2011-01-19 04:59:19 +00:00
public void LoadWatchFromRecent ( string file )
2011-01-19 04:41:21 +00:00
{
2011-01-21 05:23:44 +00:00
bool z = true ;
if ( changes ) z = AskSave ( ) ;
if ( z )
2011-01-19 04:41:21 +00:00
{
2011-01-21 05:23:44 +00:00
bool r = LoadWatchFile ( file , false ) ;
if ( ! r )
{
DialogResult result = MessageBox . Show ( "Could not open " + file + "\nRemove from list?" , "File not found" , MessageBoxButtons . YesNo , MessageBoxIcon . Error ) ;
if ( result = = DialogResult . Yes )
Global . Config . RecentWatches . Remove ( file ) ;
}
DisplayWatchList ( ) ;
changes = false ;
2011-01-19 04:41:21 +00:00
}
}
2011-01-19 15:23:00 +00:00
private void NewWatchList ( )
{
2011-01-21 05:13:47 +00:00
bool result = true ;
if ( changes ) result = AskSave ( ) ;
2011-01-21 05:23:44 +00:00
if ( result = = true )
2011-01-21 05:13:47 +00:00
{
watchList . Clear ( ) ;
DisplayWatchList ( ) ;
currentWatchFile = "" ;
changes = false ;
}
2011-01-19 15:23:00 +00:00
}
2011-01-19 16:57:23 +00:00
private bool SaveWatchFile ( string path )
{
var file = new FileInfo ( path ) ;
//if (file.Exists == true) //TODO: prompt to overwrite
using ( StreamWriter sw = new StreamWriter ( path ) )
{
string str = "" ;
for ( int x = 0 ; x < watchList . Count ; x + + )
{
2011-01-20 03:28:29 +00:00
str + = string . Format ( "{0:X4}" , watchList [ x ] . address ) + "\t" ;
2011-01-19 16:57:23 +00:00
str + = watchList [ x ] . GetTypeByChar ( ) . ToString ( ) + "\t" ;
str + = watchList [ x ] . GetSignedByChar ( ) . ToString ( ) + "\t" ;
if ( watchList [ x ] . bigendian = = true )
str + = "1\t" ;
else
str + = "0\t" ;
str + = watchList [ x ] . notes + "\n" ;
}
sw . WriteLine ( str ) ;
}
2011-01-21 04:58:07 +00:00
changes = false ;
2011-01-19 16:57:23 +00:00
return true ;
}
2011-01-19 15:23:00 +00:00
2011-01-19 15:56:42 +00:00
bool LoadWatchFile ( string path , bool append )
2011-01-18 17:17:14 +00:00
{
2011-01-19 02:49:47 +00:00
int y , z ;
2011-01-18 17:17:14 +00:00
var file = new FileInfo ( path ) ;
if ( file . Exists = = false ) return false ;
using ( StreamReader sr = file . OpenText ( ) )
{
2011-01-20 00:17:49 +00:00
currentWatchFile = path ;
2011-01-19 04:05:01 +00:00
int count = 0 ;
2011-01-18 17:17:14 +00:00
string s = "" ;
string temp = "" ;
2011-01-19 15:56:42 +00:00
if ( append = = false )
watchList . Clear ( ) ; //Wipe existing list and read from file
2011-01-18 17:17:14 +00:00
while ( ( s = sr . ReadLine ( ) ) ! = null )
{
//parse each line and add to watchList
//.wch files from other emulators start with a number representing the number of watch, that line can be discarded here
//Any properly formatted line couldn't possibly be this short anyway, this also takes care of any garbage lines that might be in a file
if ( s . Length < 5 ) continue ;
2011-01-19 02:49:47 +00:00
z = HowMany ( s , '\t' ) ;
2011-01-18 17:17:14 +00:00
if ( z = = 5 )
{
//If 5, then this is a .wch file format made from another emulator, the first column (watch position) is not needed here
2011-01-19 02:49:47 +00:00
y = s . IndexOf ( '\t' ) + 1 ;
2011-01-20 00:10:08 +00:00
s = s . Substring ( y , s . Length - y ) ; //5 digit value representing the watch position number
2011-01-18 17:17:14 +00:00
}
else if ( z ! = 4 )
continue ; //If not 4, something is wrong with this line, ignore it
2011-01-19 04:05:01 +00:00
count + + ;
2011-01-18 17:17:14 +00:00
Watch w = new Watch ( ) ;
temp = s . Substring ( 0 , s . IndexOf ( '\t' ) ) ;
2011-01-19 02:49:47 +00:00
w . address = int . Parse ( temp , NumberStyles . HexNumber ) ;
y = s . IndexOf ( '\t' ) + 1 ;
2011-01-20 00:10:08 +00:00
s = s . Substring ( y , s . Length - y ) ; //Type
2011-01-19 02:49:47 +00:00
w . SetTypeByChar ( s [ 0 ] ) ;
y = s . IndexOf ( '\t' ) + 1 ;
2011-01-20 00:10:08 +00:00
s = s . Substring ( y , s . Length - y ) ; //Signed
2011-01-19 02:49:47 +00:00
w . SetSignedByChar ( s [ 0 ] ) ;
y = s . IndexOf ( '\t' ) + 1 ;
2011-01-20 00:10:08 +00:00
s = s . Substring ( y , s . Length - y ) ; //Endian
2011-01-19 02:49:47 +00:00
y = Int16 . Parse ( s [ 0 ] . ToString ( ) ) ;
if ( y = = 0 )
w . bigendian = false ;
else
w . bigendian = true ;
w . notes = s . Substring ( 2 , s . Length - 2 ) ; //User notes
watchList . Add ( w ) ;
2011-01-18 17:17:14 +00:00
}
2011-01-19 04:05:01 +00:00
2011-01-19 04:41:21 +00:00
Global . Config . RecentWatches . Add ( file . FullName ) ;
2011-01-21 04:58:07 +00:00
changes = false ;
2011-01-19 04:05:01 +00:00
//Update the number of watches
WatchCountLabel . Text = count . ToString ( ) + " watches" ;
2011-01-18 17:17:14 +00:00
}
return true ;
}
2011-01-18 03:36:43 +00:00
void AddNewWatch ( )
{
2011-01-20 04:33:16 +00:00
RamWatchNewWatch r = new RamWatchNewWatch ( ) ;
r . ShowDialog ( ) ;
2011-01-21 03:20:45 +00:00
if ( r . userSelected = = true )
{
//TODO: check for duplicates before adding? All parameters would have to match, otherwise it should be allowed
watchList . Add ( r . watch ) ;
DisplayWatchList ( ) ;
}
2011-01-18 03:36:43 +00:00
}
void EditWatch ( )
{
2011-01-21 04:14:30 +00:00
ListView . SelectedIndexCollection indexes = WatchListView . SelectedIndices ;
RamWatchNewWatch r = new RamWatchNewWatch ( ) ;
int x = indexes [ 0 ] ;
2011-01-21 04:32:32 +00:00
r . SetToEditWatch ( watchList [ x ] , "Edit Watch" ) ;
2011-01-21 04:14:30 +00:00
r . ShowDialog ( ) ;
if ( r . userSelected = = true )
{
2011-01-21 04:58:07 +00:00
changes = true ;
2011-01-21 04:14:30 +00:00
watchList [ x ] = r . watch ;
DisplayWatchList ( ) ;
}
2011-01-18 03:36:43 +00:00
}
void RemoveWatch ( )
{
2011-01-21 04:58:07 +00:00
changes = true ;
2011-01-21 03:59:53 +00:00
ListView . SelectedIndexCollection indexes = WatchListView . SelectedIndices ;
foreach ( int index in indexes )
{
watchList . Remove ( watchList [ index ] ) ;
}
DisplayWatchList ( ) ;
2011-01-18 03:36:43 +00:00
}
void DuplicateWatch ( )
{
2011-01-21 04:32:32 +00:00
ListView . SelectedIndexCollection indexes = WatchListView . SelectedIndices ;
RamWatchNewWatch r = new RamWatchNewWatch ( ) ;
int x = indexes [ 0 ] ;
r . SetToEditWatch ( watchList [ x ] , "Duplicate Watch" ) ;
r . ShowDialog ( ) ;
if ( r . userSelected = = true )
{
2011-01-21 04:58:07 +00:00
changes = true ;
2011-01-21 04:32:32 +00:00
watchList . Add ( watchList [ x ] ) ;
DisplayWatchList ( ) ;
}
2011-01-18 03:36:43 +00:00
}
void MoveUp ( )
{
2011-01-21 04:44:30 +00:00
ListView . SelectedIndexCollection indexes = WatchListView . SelectedIndices ;
Watch temp = new Watch ( ) ;
foreach ( int index in indexes )
{
temp = watchList [ index ] ;
watchList . Remove ( watchList [ index ] ) ;
watchList . Insert ( index - 1 , temp ) ;
2011-01-21 04:58:07 +00:00
changes = true ; //Note: here it will get flagged many times redundantly potnetially, but this avoids it being flag falsely when the user did not select an index
2011-01-21 04:44:30 +00:00
}
DisplayWatchList ( ) ;
//TODO: Set highlighted items to be what the user had selected (in their new position)
2011-01-18 03:36:43 +00:00
}
void MoveDown ( )
{
2011-01-21 04:48:36 +00:00
ListView . SelectedIndexCollection indexes = WatchListView . SelectedIndices ;
Watch temp = new Watch ( ) ;
foreach ( int index in indexes )
{
temp = watchList [ index ] ;
if ( index < watchList . Count - 1 )
{
watchList . Remove ( watchList [ index ] ) ;
watchList . Insert ( index + 1 , temp ) ;
}
2011-01-21 04:58:07 +00:00
changes = true ; //Note: here it will get flagged many times redundantly potnetially, but this avoids it being flag falsely when the user did not select an index
2011-01-21 04:48:36 +00:00
}
DisplayWatchList ( ) ;
//TODO: Set highlighted items to be what the user had selected (in their new position)
2011-01-18 03:36:43 +00:00
}
private void exitToolStripMenuItem_Click ( object sender , EventArgs e )
{
this . Close ( ) ;
}
private void newListToolStripMenuItem_Click ( object sender , EventArgs e )
{
2011-01-19 15:23:00 +00:00
NewWatchList ( ) ;
2011-01-18 03:36:43 +00:00
}
2011-01-19 15:56:42 +00:00
private FileInfo GetFileFromUser ( )
2011-01-18 03:36:43 +00:00
{
2011-01-18 17:17:14 +00:00
var ofd = new OpenFileDialog ( ) ;
ofd . InitialDirectory = Global . Config . LastRomPath ;
ofd . Filter = "Watch Files (*.wch)|*.wch|All Files|*.*" ;
ofd . RestoreDirectory = true ;
2011-01-18 03:36:43 +00:00
2011-01-18 17:17:14 +00:00
Global . Sound . StopSound ( ) ;
var result = ofd . ShowDialog ( ) ;
Global . Sound . StartSound ( ) ;
if ( result ! = DialogResult . OK )
2011-01-19 15:56:42 +00:00
return null ;
2011-01-18 17:17:14 +00:00
var file = new FileInfo ( ofd . FileName ) ;
Global . Config . LastRomPath = file . DirectoryName ;
2011-01-19 15:56:42 +00:00
return file ;
}
private void openToolStripMenuItem_Click ( object sender , EventArgs e )
{
var file = GetFileFromUser ( ) ;
if ( file ! = null )
2011-01-21 05:23:44 +00:00
{
bool r = true ;
if ( changes ) r = AskSave ( ) ;
if ( r )
{
LoadWatchFile ( file . FullName , false ) ;
DisplayWatchList ( ) ;
}
}
2011-01-18 03:36:43 +00:00
}
private void saveToolStripMenuItem_Click ( object sender , EventArgs e )
{
2011-01-20 00:17:49 +00:00
if ( string . Compare ( currentWatchFile , "" ) = = 0 ) return ;
2011-01-18 03:36:43 +00:00
2011-01-21 04:58:07 +00:00
if ( changes )
SaveWatchFile ( currentWatchFile ) ;
2011-01-18 03:36:43 +00:00
}
2011-01-19 16:57:23 +00:00
private FileInfo GetSaveFileFromUser ( )
2011-01-18 03:36:43 +00:00
{
2011-01-19 16:57:23 +00:00
var sfd = new SaveFileDialog ( ) ;
sfd . InitialDirectory = Global . Config . LastRomPath ;
sfd . Filter = "Watch Files (*.wch)|*.wch|All Files|*.*" ;
sfd . RestoreDirectory = true ;
Global . Sound . StopSound ( ) ;
var result = sfd . ShowDialog ( ) ;
Global . Sound . StartSound ( ) ;
if ( result ! = DialogResult . OK )
return null ;
var file = new FileInfo ( sfd . FileName ) ;
Global . Config . LastRomPath = file . DirectoryName ;
return file ;
}
2011-01-18 03:36:43 +00:00
2011-01-21 05:13:47 +00:00
private void SaveAs ( )
2011-01-19 16:57:23 +00:00
{
var file = GetSaveFileFromUser ( ) ;
if ( file ! = null )
SaveWatchFile ( file . FullName ) ;
//TODO: inform the user (with using an annoying message box)
2011-01-18 03:36:43 +00:00
}
2011-01-21 05:13:47 +00:00
private void saveAsToolStripMenuItem_Click ( object sender , EventArgs e )
{
SaveAs ( ) ;
}
2011-01-18 03:36:43 +00:00
private void appendFileToolStripMenuItem_Click ( object sender , EventArgs e )
{
2011-01-19 15:56:42 +00:00
var file = GetFileFromUser ( ) ;
if ( file ! = null )
LoadWatchFile ( file . FullName , true ) ;
DisplayWatchList ( ) ;
2011-01-21 04:58:07 +00:00
changes = true ;
2011-01-18 03:36:43 +00:00
}
private void autoLoadToolStripMenuItem_Click ( object sender , EventArgs e )
{
2011-01-19 04:41:21 +00:00
UpdateAutoLoadRamWatch ( ) ;
2011-01-18 03:36:43 +00:00
}
private void newWatchToolStripMenuItem_Click ( object sender , EventArgs e )
{
AddNewWatch ( ) ;
}
private void editWatchToolStripMenuItem_Click ( object sender , EventArgs e )
{
EditWatch ( ) ;
}
private void removeWatchToolStripMenuItem_Click ( object sender , EventArgs e )
{
RemoveWatch ( ) ;
}
private void duplicateWatchToolStripMenuItem_Click ( object sender , EventArgs e )
{
DuplicateWatch ( ) ;
}
private void moveUpToolStripMenuItem_Click ( object sender , EventArgs e )
{
MoveUp ( ) ;
}
private void moveDownToolStripMenuItem_Click ( object sender , EventArgs e )
{
MoveDown ( ) ;
}
2011-01-18 05:13:00 +00:00
2011-01-21 15:05:15 +00:00
public void DisplayWatchList ( )
2011-01-18 05:13:00 +00:00
{
2011-01-19 05:46:08 +00:00
WatchListView . Items . Clear ( ) ;
for ( int x = 0 ; x < watchList . Count ; x + + )
{
2011-01-20 03:28:29 +00:00
ListViewItem item = new ListViewItem ( String . Format ( "{0:X4}" , watchList [ x ] . address ) ) ;
2011-01-19 05:46:08 +00:00
item . SubItems . Add ( watchList [ x ] . value . ToString ( ) ) ;
item . SubItems . Add ( watchList [ x ] . notes ) ;
WatchListView . Items . Add ( item ) ;
2011-01-21 05:44:24 +00:00
}
2011-01-19 05:46:08 +00:00
}
2011-01-18 17:17:14 +00:00
2011-01-19 05:46:08 +00:00
private void RamWatch_Load ( object sender , EventArgs e )
{
2011-01-20 02:28:38 +00:00
defaultWidth = this . Size . Width ; //Save these first so that the user can restore to its original size
defaultHeight = this . Size . Height ;
2011-01-20 00:40:23 +00:00
if ( Global . Config . RamWatchWndx > = 0 & & Global . Config . RamWatchWndy > = 0 )
2011-01-20 01:10:05 +00:00
this . Location = new Point ( Global . Config . RamWatchWndx , Global . Config . RamWatchWndy ) ;
2011-01-20 02:22:15 +00:00
if ( Global . Config . RamWatchWidth > = 0 & & Global . Config . RamWatchHeight > = 0 )
{
this . Size = new System . Drawing . Size ( Global . Config . RamWatchWidth , Global . Config . RamWatchHeight ) ;
}
2011-01-18 05:13:00 +00:00
}
2011-01-19 04:18:33 +00:00
private void filesToolStripMenuItem_DropDownOpened ( object sender , EventArgs e )
{
if ( Global . Config . AutoLoadRamWatch = = true )
autoLoadToolStripMenuItem . Checked = true ;
else
autoLoadToolStripMenuItem . Checked = false ;
2011-01-20 00:17:49 +00:00
2011-01-21 04:58:07 +00:00
if ( string . Compare ( currentWatchFile , "" ) = = 0 | | ! changes )
2011-01-20 00:17:49 +00:00
{
saveToolStripMenuItem . Enabled = false ;
}
else
{
saveToolStripMenuItem . Enabled = true ;
}
2011-01-19 04:18:33 +00:00
}
2011-01-19 04:41:21 +00:00
private void UpdateAutoLoadRamWatch ( )
{
if ( Global . Config . AutoLoadRamWatch = = true )
{
Global . Config . AutoLoadRamWatch = false ;
autoLoadToolStripMenuItem . Checked = false ;
}
else
{
Global . Config . AutoLoadRamWatch = true ;
autoLoadToolStripMenuItem . Checked = true ;
}
}
private void recentToolStripMenuItem_DropDownOpened ( object sender , EventArgs e )
{
//Clear out recent Roms list
//repopulate it with an up to date list
recentToolStripMenuItem . DropDownItems . Clear ( ) ;
if ( Global . Config . RecentWatches . IsEmpty ( ) )
{
recentToolStripMenuItem . DropDownItems . Add ( "None" ) ;
}
else
{
for ( int x = 0 ; x < Global . Config . RecentWatches . Length ( ) ; x + + )
{
string path = Global . Config . RecentWatches . GetRecentFileByPosition ( x ) ;
var item = new ToolStripMenuItem ( ) ;
item . Text = path ;
item . Click + = ( o , ev ) = > LoadWatchFromRecent ( path ) ;
recentToolStripMenuItem . DropDownItems . Add ( item ) ; //TODO: truncate this to a nice size
}
}
recentToolStripMenuItem . DropDownItems . Add ( "-" ) ;
var clearitem = new ToolStripMenuItem ( ) ;
clearitem . Text = "&Clear" ;
clearitem . Click + = ( o , ev ) = > Global . Config . RecentRoms . Clear ( ) ;
recentToolStripMenuItem . DropDownItems . Add ( clearitem ) ;
var auto = new ToolStripMenuItem ( ) ;
auto . Text = "&Auto-Load" ;
auto . Click + = ( o , ev ) = > UpdateAutoLoadRamWatch ( ) ;
if ( Global . Config . AutoLoadRamWatch = = true )
auto . Checked = true ;
else
auto . Checked = false ;
recentToolStripMenuItem . DropDownItems . Add ( auto ) ;
}
2011-01-19 15:56:42 +00:00
private void WatchListView_AfterLabelEdit ( object sender , LabelEditEventArgs e )
{
// Determine if label is changed by checking for null.
if ( e . Label = = null )
return ;
// ASCIIEncoding is used to determine if a number character has been entered.
ASCIIEncoding AE = new ASCIIEncoding ( ) ;
// Convert the new label to a character array.
char [ ] temp = e . Label . ToCharArray ( ) ;
// Check each character in the new label to determine if it is a number.
for ( int x = 0 ; x < temp . Length ; x + + )
{
// Encode the character from the character array to its ASCII code.
byte [ ] bc = AE . GetBytes ( temp [ x ] . ToString ( ) ) ;
// Determine if the ASCII code is within the valid range of numerical values.
if ( bc [ 0 ] > 47 & & bc [ 0 ] < 58 )
{
// Cancel the event and return the lable to its original state.
e . CancelEdit = true ;
// Display a MessageBox alerting the user that numbers are not allowed.
MessageBox . Show ( "The text for the item cannot contain numerical values." ) ;
// Break out of the loop and exit.
return ;
}
}
}
2011-01-20 00:40:23 +00:00
private void RamWatch_LocationChanged ( object sender , EventArgs e )
{
Global . Config . RamWatchWndx = this . Location . X ;
Global . Config . RamWatchWndy = this . Location . Y ;
}
2011-01-20 02:22:15 +00:00
private void RamWatch_Resize ( object sender , EventArgs e )
{
Global . Config . RamWatchWidth = this . Right - this . Left ;
Global . Config . RamWatchHeight = this . Bottom - this . Top ;
}
2011-01-20 02:28:38 +00:00
private void restoreWindowSizeToolStripMenuItem_Click ( object sender , EventArgs e )
{
this . Size = new System . Drawing . Size ( defaultWidth , defaultHeight ) ;
}
2011-01-20 03:28:29 +00:00
private void WatchListView_SelectedIndexChanged ( object sender , EventArgs e )
{
//TODO: debug/testing
ListView . SelectedIndexCollection i = this . WatchListView . SelectedIndices ;
i = WatchListView . SelectedIndices ;
}
2011-01-21 15:05:15 +00:00
private void RamWatch_Paint ( object sender , PaintEventArgs e )
{
UpdateValues ( ) ;
}
2011-01-18 03:36:43 +00:00
}
}