Archive for April, 2009

29
Apr
09

[Note] Neopets Autobidder Progress

This is the current workup for the neopets auction bot:

Currently, i have all the complex coding done, just have to work out the last few bugs here and there, and then decide how to integrate the bot with an auto stocker (wether to pass the sell price from url to url, until it reaches the stock, or to just query again later on).

Main Auction Page
Create list of items to query
Send request to php
Return list of items maxprice & sell price
Parse list
Loop through each row
Check if item is in list
If it is, check if its a NF only auction
If its not, check to see if maxprice is less than the current price
If it is, check to see if the page is on the ignore list
If it isnt, open the page in a new tab with max price as an url paramater (possibly also sell price?)
Add opened page to ignore list (greasemonkey var?)
Wait between 7 and 15 seconds, refresh page

Bid Page
Parse max price from url
Get username from page
Check to see if the auction is over/invalid/too expensive
If it is, close the page
If it isnt, Check to see if the last bidder was the user
If it is, refresh the page
If it isnt,
modify the form to pass the maxprice in the url when submitted (possibly also sell price)
submit the bid

Bid Submission
Parse max price from url
Check if bid successful (find BID SUCCESSFUL)
If yes,
find link on page (loop through ‘a’ elements)
modify link to pass the maxprice in the url when submitted (possibly also sell price)
If no, check reason
If not enough money, close
If need to wait, history back
If bid twice in a row, back : (Sorry, you are not allowed to bid on an auction two times in a row)

29
Apr
09

[PHP] Hit Counter using MySQL Database

Little script I wrote this morning which uses a database to store number of hits on a page:

<?php
	$dbhost = '1';
	$dbuser = '2';
	$dbpass = '3';
	$conn = mysql_connect($dbhost, $dbuser, $dbpass) or die ('0');
	$dbname = '4';
	mysql_select_db($dbname);

	$page = $_SERVER['REQUEST_URI'];
	$result = mysql_query("SELECT HIT_COUNT FROM  Hit_Count WHERE Page_Name = '$page'");

	$any = false;
	while($row = mysql_fetch_array($result)) {
		$any = true;
		$counter = mysql_result($result, 0) + 1;
		$sql = "UPDATE Hit_Count SET HIT_COUNT = '$counter' WHERE Page_Name = '$page'";
		mysql_query($sql);
		echo($counter);
	}

	if($any == false) {
		$sql = "INSERT INTO Hit_Count (Page_Name, HIT_COUNT) VALUES ('$page', '1')";
		mysql_query($sql);
		echo("1");
	}

	mysql_close($conn);
?>

The script will output the current hit count, you would use the script like this:

<?php include("location/hitcount.php"); ?>

The tables set up is this:

Page_Name [text]		HIT_COUNT [int(11)]
/files/Image%20Gallery.php 	2
/ 				4
28
Apr
09

[Javascript/Php] Neopets Auction Bot Project (2)

A much faster script than my previous version. Instead of querying each item individually, it loops through the items in the list and uses the queryNeopetItem to send the data to another php page, which pareses the items and returns the prices of each of them. When the data is returned, it is then split and the table is formatted depending on what action should be taken (light green=no data, medium green=more expensive than data, grey = neofriend only, blue = make profit)

Neopets auction highlight


And below is the source for the greasemonkey script:

// ==UserScript==
// @name           AuctionBot test v2
// @namespace      Sim0n NeoPets
// @include        http://www.neopets.com/auctions.phtml
// ==/UserScript==

function getElementsByAttribute(oElm, strTagName, strAttributeName, strAttributeValue){
	var arrElements = (strTagName == "*" && oElm.all)? oElm.all : oElm.getElementsByTagName(strTagName);
	var arrReturnElements = new Array();
	var oAttributeValue = (typeof strAttributeValue != "undefined")? new RegExp("(^|\\s)" + strAttributeValue + "(\\s|$)") : null;
	var oCurrent;
	var oAttribute;
	for(var i=0; i<arrElements.length; i++){
		oCurrent = arrElements[i];
		oAttribute = oCurrent.getAttribute && oCurrent.getAttribute(strAttributeName);
		if(typeof oAttribute == "string" && oAttribute.length > 0){
			if(typeof strAttributeValue == "undefined" || (oAttributeValue && oAttributeValue.test(oAttribute))){
				arrReturnElements.push(oCurrent);
			}
		}
	}
	return arrReturnElements;
}

function queryNeopetItem(item, complete) {
	url = "myphpquery.php?items=" + item;
	GM_xmlhttpRequest({
		method: "POST",
		url: url,
		headers:{'Content-type':'application/x-www-form-urlencoded'},
		data:encodeURI(""),
		onload: function(xhr) { complete(xhr.responseText); }
	});
}
function comp(text) {
	var items = text.split("\n");
	for(var i = 0;i<20;i++) {
		if(items[i] == "") {
			table[0].rows[i+1].setAttribute("bgcolor","#ccffd0");
		} else {
			var item = items[i].split("\t");
			var cur_price = table[0].rows[i+1].cells[6].innerHTML.replace(/<b>(\d*)<\/b> NP/, "$1");
			if(Number(cur_price)<=Number(item[0])) {
				if(table[0].rows[i+1].cells[3].innerHTML.toLowerCase().indexOf("<b>[nf]</b>") != -1) {
					table[0].rows[i+1].setAttribute("bgcolor","#aaafa0");
				} else {
					alert(table[0].rows[i+1].cells[3].innerHTML)
					table[0].rows[i+1].setAttribute("bgcolor","#00ffff");
				}
			} else {
				table[0].rows[i+1].setAttribute("bgcolor","#aaffd0");
			}
		}
	}
}
var table = getElementsByAttribute(document.body, "table", "align", "center");
var queryString = "";
for(var i=1;i<table[0].rows.length - 1;i++) {
	queryString = queryString + table[0].rows[i].cells[2].innerHTML.replace(/<a href=\".*\">(.*)<\/a>/, "$1") + "|";
}
queryString = queryString + table[0].rows[table[0].rows.length-1].cells[2].innerHTML.replace(/<a href=\".*\">(.*)<\/a>/, "$1")
queryNeopetItem(queryString, comp);

And for the php query:

<?php
	$dbhost = 'a';
	$dbuser = 'b';
	$dbpass = 'd';
	$conn = mysql_connect($dbhost, $dbuser, $dbpass) or die ('0');
	$dbname = 'e';
	mysql_select_db($dbname);

	$items = explode("|", $_GET['items']);
	for($i=0;$i<count($items);$i++) {
		$sqlQuery = "ITEM_NAME = '$items[$i]'";
		$result = mysql_query("SELECT * FROM  Neopets_Items WHERE $sqlQuery");
		$any = false;
		while($row = mysql_fetch_array($result))
  		{
			$any = true;
  			echo $row['MAX_PRICE'] . "\t" . $row['SELL_PRICE'] . "\n";
  		}
		if($any==false) {
			echo "\n";
		}
	}
	mysql_close($conn);
?> 
28
Apr
09

[Javascript/Php] Neopets Auction Bot Project (1)

Ive been asked by somebody to create a bot for the game Neopets that will view the most recent auctions and bid upon items which on which money can be easily made. One of the constraints for this project, was that it must be made using Javascript + Greasemonkey, so no Perl or Python (or whatever it is people use to code bots on the net these days! Like I know -_-).
My initial attempt (below) uses the GM_xmlhttpRequest to query a database of items (that’s another project ^_^ – lots of faces today?), and it works very nicely, except for one small thing. Its ridiculously slow. It query the database for each of the items, and then either marks it with a green colour to indicate that it is loaded, but you will not make money off it, or will highlight the row brown to show that you will make money off the auction.
Remeber, its v. slow!
Still, here is the source code as it is a nice example of a recursive function and sending & retrieving data to other websites using Greasemonkey.

// ==UserScript==
// @name           Auc7i0nb0t t3s7
// @namespace      n30p375
// @include        http://www.neopets.com/auctions.phtml
// ==/UserScript==

//Some function from google - kinda overkill, but meh.
function getElementsByAttribute(oElm, strTagName, strAttributeName, strAttributeValue){
	var arrElements = (strTagName == "*" && oElm.all)? oElm.all : oElm.getElementsByTagName(strTagName);
	var arrReturnElements = new Array();
	var oAttributeValue = (typeof strAttributeValue != "undefined")? new RegExp("(^|\\s)" + strAttributeValue + "(\\s|$)") : null;
	var oCurrent;
	var oAttribute;
	for(var i=0; i<arrElements.length; i++){
		oCurrent = arrElements[i];
		oAttribute = oCurrent.getAttribute && oCurrent.getAttribute(strAttributeName);
		if(typeof oAttribute == "string" && oAttribute.length > 0){
			if(typeof strAttributeValue == "undefined" || (oAttributeValue && oAttributeValue.test(oAttribute))){
				arrReturnElements.push(oCurrent);
			}
		}
	}
	return arrReturnElements;
}

function queryNeopetItem(item, complete) {
	url = "url to my item query page.php?item=" + item;
	GM_xmlhttpRequest({
		method: "POST",
		url: url,
		headers:{'Content-type':'application/x-www-form-urlencoded'},
		data:encodeURI(""),
		onload: function(xhr) { complete(xhr.responseText); }
	});
}
var x = 1;
var table = getElementsByAttribute(document.body, "table", "align", "center");
var item_name = table[0].rows[x].cells[2].innerHTML.replace(/<a href=\".*\">(.*)<\/a>/, "$1");
function comp(text) {
	var ta = text.split("\n");
	max_price = ta[1];
	sell_price = ta[2];
	if(x<table[0].rows.length) {
		item_name = table[0].rows[x].cells[2].innerHTML.replace(/<a href=\".*\">(.*)<\/a>/, "$1");
		queryNeopetItem(item_name, comp)
		table[0].rows[x-1].setAttribute("bgcolor","#ccffd0");
		if(ta[1].indexOf("www.mywebhostsname.com") == -1) {
			var str = table[0].rows[x-1].cells[6].innerHTML.replace(/<b>(\d*)<\/b> NP/, "$1");
			if(Number(str)<=Number(max_price)) {
				table[0].rows[x-1].setAttribute("bgcolor","#dc9423");
			}
		}
		x++;
	} else {
		item_name = table[0].rows[x-1].cells[2].innerHTML.replace(/<a href=\".*\">(.*)<\/a>/, "$1");
		table[0].rows[x-1].setAttribute("bgcolor","#ccffd0");
		if(ta[1].indexOf("www.mywebhostsname.com") == -1) {
			var str = table[0].rows[x-1].cells[6].innerHTML.replace(/<b>(\d*)<\/b> NP/, "$1");
			if(str<=max_price) {
				table[0].rows[x-1].setAttribute("bgcolor","#dc9423");
			}
		}
		table[0].appendChild(document.createTextNode("Complete"));
	}
}
queryNeopetItem(item_name, comp);

url to my item query page.php?item is a page that querys the database and returns the items details (Max Price and Sell Price). The above function is recursive, as queryNeopetItem calls the function comp (which parses the text returned from the httpRequest) which, in turn calls queryNeopetItem again, until all the rows items have been queried.

<php
	$dbhost = 'x';
	$dbuser = 'y';
	$dbpass = 'z';
	$conn = mysql_connect($dbhost, $dbuser, $dbpass) or die ('0');
	$dbname = 'a';
	mysql_select_db($dbname);

	$item = $_GET['item'];
	$max = $_GET['max'];
	$sell = $_GET['sell']; 

	$sqlQuery = "";

		if($item != "") {
			$sqlQuery = "ITEM_NAME = '$item'";
		}
		if($max != "") {
			if($sqlQuery != "") {
				$sqlQuery = $sqlQuery . " AND MAX_PRICE = '$max'";
			} else {
				$sqlQuery = "MAX_PRICE = '$max'";
		}
		}
		if($sell != "") {
			if($sqlQuery != "") {
				$sqlQuery = $sqlQuery . " AND SELL_PRICE = '$sell'";
			} else {
				$sqlQuery = "SELL_PRICE = '$sell'";
			}
		}

		$result = mysql_query("SELECT * FROM  Neopets_Items WHERE $sqlQuery");

		while($row = mysql_fetch_array($result))
  		{
  			echo $row['ITEM_NAME'] . "\n" . $row['MAX_PRICE'] . "\n" . $row['SELL_PRICE'];
  		}

	mysql_close($conn);
?> 

That code is kind of overkill for the query that the script makes, however it also allows you to query by a combination of the items properties which will be used later on in the project (probably)

28
Apr
09

[Vb.Net] HTML Syntax Highlighter

Based very loosely on the original version I wrote in VBA for use in word.
Instead of looping through “words” this goes through each character and compares it with the colour of the last. Although this method may be slightly slower than splitting the string via spaces, the results are better as strings like Simon(“hithere”) would be incorrectly coloured.

    Private Sub SyntaxBtn_Press(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SyntaxBtn.Click
        CodeBox.SelectionStart = 0
        CodeBox.SelectionLength = 1
        Dim CurColour = CodeBox.SelectionColor
        Dim Output As String = "<div style='border: #660000 5px solid;color:#000000;'><pre style='background-color:#ffffff;padding:3px;line-height:15px;color:#000000;font-family: Courier New !important; font-size: 11px !important'>" & vbNewLine
        Output &= "<span style=""color:rgb(" & CurColour.R & ", " & CurColour.G & ", " & CurColour.B & ");"">"
        Output &= CodeBox.SelectedText
        Dim matches As System.Text.RegularExpressions.MatchCollection = System.Text.RegularExpressions.Regex.Matches(CodeBox.Text, "<")
        Progress.Maximum = CodeBox.TextLength + (matches.Count * 4)
        Progress.Value = 0
        For i As Integer = 1 To (CodeBox.TextLength + (matches.Count * 4))
            Progress.Value += 1
            CodeBox.SelectionStart = i
            CodeBox.SelectionLength = 1
            If CodeBox.SelectedText = "<" Then CodeBox.SelectedText = "\<"
            If CodeBox.SelectionColor <> CurColour Then CurColour = CodeBox.SelectionColor : Output &= "</span><span style=""color:rgb(" & CurColour.R & ", " & CurColour.G & ", " & CurColour.B & ");"">"
            Output &= CodeBox.SelectedText
        Next
        Output &= "</span> " & "</pre></div>"
        My.Computer.Clipboard.SetText(Output)
    End Sub

The example output for this is something like this:


Output &= \</span>\<span style="color:rgb(163, 21, 21);">"<span style=""color:rgb("\</span>\<span style="color:rgb(0, 0, 0);"> & CurColour.R & \</span>\<span style="color:rgb(163, 21, 21);">", "\</span>\<span style="color:rgb(0, 0, 0);"> & CurColour.G & \</span>\<span style="color:rgb(163, 21, 21);">", "\</span>\<span style="color:rgb(0, 0, 0);"> & CurColour.B & \</span>\<span style="color:rgb(163, 21, 21);">");"">"

\</span>\<span style="color:rgb(0, 0, 0);">        Output &= CodeBox.SelectedText

\</span>\<span style="color:rgb(0, 0, 255);">Dim\</span>\<span style="color:rgb(0, 0, 0);"> matches \</span>\<span style="color:rgb(0, 0, 255);">As\</span>\<span style="color:rgb(0, 0, 0);"> System.Text.RegularExpressions.MatchCollection = System.Text.RegularExpressions.Regex.Matches(CodeBox.Text, \</span>\<span style="color:rgb(163, 21, 21);">"<"\</span>\<span style="color:rgb(0, 0, 0);">)

Progress.Maximum = CodeBox.TextLength + (matches.Count * 4)

Progress.Value = 0

\</span>\<span style="color:rgb(0, 0, 255);">For\</span>\<span style="color:rgb(0, 0, 0);"> i \</span>\<span style="color:rgb(0, 0, 255);">As\</span>\<span style="color:rgb(0, 0, 0);"> \</span>\<span style="color:rgb(0, 0, 255);">Integer\</span>\<span style="color:rgb(0, 0, 0);"> = 1 \</span>\<span style="color:rgb(0, 0, 255);">To\</span>\<span style="color:rgb(0, 0, 0);"> (CodeBox.TextLength + (matches.Count * 4))

Progress.Value += 1

CodeBox.SelectionStart = i

CodeBox.SelectionLength = 1

\</span>\<span style="color:rgb(0, 0, 255);">If\</span>\<span style="color:rgb(0, 0, 0);"> CodeBox.SelectedText = \</span>\<span style="color:rgb(163, 21, 21);">"<"\</span>\<span style="color:rgb(0, 0, 0);"> \</span>\<span style="color:rgb(0, 0, 255);">Then\</span>\<span style="color:rgb(0, 0, 0);"> CodeBox.SelectedText = \</span>\<span style="color:rgb(163, 21, 21);">"\<"

\</span>\<span style="color:rgb(0, 0, 0);">            \</span>\<span style="color:rgb(0, 0, 255);">If\</span>\<span style="color:rgb(0, 0, 0);"> CodeBox.SelectionColor <> CurColour \</span>\<span style="color:rgb(0, 0, 255);">Then\</span>\<span style="color:rgb(0, 0, 0);"> CurColour = CodeBox.SelectionColor : Output &= \</span>\<span style="color:rgb(163, 21, 21);">"</span><span style=""color:rgb("\</span>\<span style="color:rgb(0, 0, 0);"> & CurColour.R & \</span>\<span style="color:rgb(163, 21, 21);">", "\</span>\<span style="color:rgb(0, 0, 0);"> & CurColour.G & \</span>\<span style="color:rgb(163, 21, 21);">", "\</span>\<span style="color:rgb(0, 0, 0);"> & CurColour.B & \</span>\<span style="color:rgb(163, 21, 21);">");"">"

\</span>\<span style="color:rgb(0, 0, 0);">            Output &= CodeBox.SelectedText

\</span>\<span style="color:rgb(0, 0, 255);">Next

\</span>\<span style="color:rgb(0, 0, 0);">        Output &= \</span>\<span style="color:rgb(163, 21, 21);">"</span> "\</span>\<span style="color:rgb(0, 0, 0);"> & \</span>\<span style="color:rgb(163, 21, 21);">"</pre></div>"

\</span>\<span style="color:rgb(0, 0, 0);">        \</span>\<span style="color:rgb(0, 0, 255);">My\</span>\<span style="color:rgb(0, 0, 0);">.Computer.Clipboard.SetText(Output)

\</span>\<span style="color:rgb(0, 0, 255);">End\</span>\<span style="color:rgb(0, 0, 0);"> \</span>\<span style="color:rgb(0, 0, 255);">Sub\</span> \</pre>\</div>
26
Apr
09

[C++] :Minesweeper: In-game Toggle Mine Visibility 2

A much cleaner solution than what I made here: /c-minesweeper-in-game-toggle-mine-visibility/
It just uses the in game “ShowMines” function rather than having to write your own.

void __stdcall ShowMines(void)
{
	typedef void (__stdcall *ptrFn)(DWORD);
	ptrFn ptrShowMinesFn = (ptrFn)(0x01002F80);
	ptrShowMinesFn(0xE);
}

You call the function ptrShowMinesFn with one of the possible values for mines, the list can be found here:
/c-minesweeper-toggle-mine-visibility/
So, for example 0xE would display flags.

25
Apr
09

[ASM] Minesweeper God Mode Testing III

Fixed the timer now so that it only stops working if you have won the game.
Original
#########################

0100347C w>    8325 64510001 00                AND DWORD PTR DS:[fTimer],0
01003483   |.  56                              PUSH ESI
01003484       8B7424 08                       MOV ESI,DWORD PTR SS:[ESP+8]

#########################
With Jump
#########################

0100347C w>   /E9 0E160000                     JMP winmine.01004A8F
01003481      |90                              NOP
01003482      |90                              NOP
01003483   |. |56                              PUSH ESI
01003484      |8B7424 08                       MOV ESI,DWORD PTR SS:[ESP+8]

#########################
Code Cave
#########################

01004A8F    > \83F8 01                         CMP EAX,1
01004A92    .^ 0F84 EBE9FFFF                   JE winmine.01003483
01004A98    .  8325 64510001 00                AND DWORD PTR DS:[fTimer],0
01004A9F    .^ E9 DFE9FFFF                     JMP winmine.01003483
25
Apr
09

[ASM] Minesweeper God Mode Testing II

Ok after playing around a bit more, i’ve¬† set it up so that after you die, you have the default smiley icon not the dead one:
Original
#########################

0100348C   |.  0F95C0            SETNE AL
0100348F       40                INC EAX
01003490       40                INC EAX
01003491       50                PUSH EAX
01003492       A3 60510001       MOV DWORD PTR DS:[iButtonCur],EAX
01003497       E8 77F4FFFF       CALL winmine.DisplayButton
0100349C       33C0              XOR EAX,EAX

#########################
With Jump
#########################

0100348C   |.  0F95C0            SETNE AL
0100348F       E9 E6150000       JMP winmine.01004A7A
01003494       90                NOP
01003495       90                NOP
01003496       90                NOP
01003497       E8 77F4FFFF       CALL winmine.DisplayButton
0100349C       33C0              XOR EAX,EAX

#########################
Code Cave
#########################

01004A7A       83F8 01           CMP EAX,1
01004A7D       75 05             JNZ SHORT winmine.01004A84
01004A7F       B8 03000000       MOV EAX,3
01004A84       50                PUSH EAX
01004A85       A3 60510001       MOV DWORD PTR DS:[iButtonCur],EAX
01004A8A     ^ E9 08EAFFFF       JMP winmine.01003497
25
Apr
09

[ASM] Minesweeper God Mode Testing

UPDATED [At End]

Got bored and trying to create an effective god mode in Minesweeper that will allow you to keep on playing even after you die.
If I can get it all to work properly, I may make a small hack of the game that allows you to set a number of lives per game, and then you loose after hitting that many mines.

Ill be updating the blog with posts as I research more.
This is what I have so far.
__________________________________
Pretty much everything of use so far has come from the GameOver function called here:

010035AB   |> \E8 CCFEFFFF       CALL winmine.GameOver        ; (CALL winmine.0100347C)

After a bit of playing around, ive created a cave that allows me to continue playing even after hitting a mine:
Original
#########################

010034D4       85F6                       TEST ESI,ESI
010034D6       C705 00500001 10000000     MOV DWORD PTR DS:[fStatus],10
010034E0       5E                         POP ESI

#########################
With Jump
#########################

010034D4   |.  85F6              TEST ESI,ESI
010034D6       E9 B4150000       JMP winmine.01004A8A
010034DB       90                NOP
010034DC       90                NOP
010034DD       90                NOP
010034DE       90                NOP
010034DF       90                NOP
010034E0   |.  5E                POP ESI

#########################
Code Cave
#########################

01004A8A       833D 60510001 03           CMP DWORD PTR DS:[iButtonCur],3
01004A91       5E                         POP ESI
01004A92     ^ 0F85 77EAFFFF              JNZ winmine.0100350F
01004A98       C705 00500001 10000000     MOV DWORD PTR DS:[fStatus],10
01004AA2     ^ E9 3CEAFFFF                JMP winmine.010034E3

This little cave just checks to see if the game has been won or lost. If its been won, then it sets the gamestate to 0x10 (Game Over) else, it keeps the value as 0 and returns to the function.

__________________________________
Update:
I decided that instead of using a cave, just patching the function would work just as nicely:
Original
#########################

010034CF   |.  E8 19040000                CALL winmine.PlayTune
010034D4       85F6                       TEST ESI,ESI
010034D6       C705 00500001 10000000     MOV DWORD PTR DS:[fStatus],10
010034E0       5E                         POP ESI
010034E1       74 2C                      JE SHORT winmine.0100350F
010034E3       66:A1 A0560001             MOV AX,WORD PTR DS:[Preferences]
010034E9       66:3D 0300                 CMP AX,3
010034ED       74 20                      JE SHORT winmine.0100350F
010034EF       8B0D 9C570001              MOV ECX,DWORD PTR DS:[cSec]

#############
PATCHED
#############

010034CF   |.  E8 19040000                CALL winmine.PlayTune
010034D4       66:A1 A0560001             MOV AX,WORD PTR DS:[Preferences]
010034DA       833D 60510001 03           CMP DWORD PTR DS:[iButtonCur],3
010034E1       5E                         POP ESI
010034E2       75 2B                      JNZ SHORT winmine.0100350F
010034E4       C705 00500001 10000000     MOV DWORD PTR DS:[fStatus],10
010034EE       90                         NOP
010034EF       8B0D 9C570001              MOV ECX,DWORD PTR DS:[cSec]
24
Apr
09

[C++] :Minesweeper: In-game Toggle Mine Visibility

Minesweeper stores its minefield in memory, starting at the address  0x1005360, and is set up looking like this:

>>>>>>>>>
>*---*-->
>-*----*>
>-**-*-->
>------->
>---*--->
>>>>>>>>>

With the >’s representing a 1 Byte border, the *’s being mines, and -‘s being empty spaces.
Knowing this, we can effectively create a loop that reads this memory, and checks if a square is a bomb or not.

In order to calculate the size of the loop, we need to read the width and height from the game. These are stored at the two following addresses: 0x1005334 and 0x1005338. The maximum width of the board, is 0x1E, which is 0x20-2, 2 for the border. So, we now know that we can read each column by looping along the rows between 0 and nwidth, and the columns can be calculated by adding 0x20 to the address, each pass between 0 and nheight.

So, now we have the loop sorted, we just need to check if the current address holds a mine, if so, display it to the user.

bool MinesCheat = false;

void ToggleMinesCheat()
{
	int maxX = *((int*) 0x1005334);
	int maxY = *((int*) 0x1005338);
	int address = 0x1005361;

	for(int y = 0;y<maxY;y++) {
		for (int x = 0;x<maxX;x++) {
			BYTE *xmine = (BYTE*) address+x;
			BYTE mine = 0x8F;
			if(MinesCheat)
				mine = 0x8A;
			if(*xmine==mine) {
				if(MinesCheat)
					*xmine = 0x8F;
				else
					*xmine = 0x8A;
			}
		}
		address = address + 0x20;
	}
	InvalidateRect(mhWnd, NULL, 1);
	UpdateWindow(mhWnd);
	MinesCheat = MinesCheat^true;
}

That will either loop through the memory displaying the mines location, by overwriting the memory with 0x8A (Visible Mine), or if the function has already been ran, will hide the mines again by writing the memory with 0x8F (hidden mine)