Skip to content

Commit e2097b2

Browse files
committed
Improve handling of dangling RAM using status heartbeat
1 parent c871ec4 commit e2097b2

3 files changed

Lines changed: 54 additions & 21 deletions

File tree

Hacktice/Canary.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@ internal class Canary
1717
public const uint HackticeStatusActive = 0x41435456; // ACTV
1818
public const uint HackticeStatusDisabled = 0x4453424c; // DSBL
1919
public const uint HackticeStatusUpgrading = 0x55504752; // UPGR
20+
public const uint HackticeStatusHeartbeat = 0x48524254; // HRBT
2021
}
2122
}

Hacktice/Emulator.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Hacktice header has 20 bytes in size and consists of
2828
*/
2929
const int HackticeHeaderSize = 20;
3030
private IntPtr? _ptrHackticeHeader;
31+
private IntPtr? _ptrHackticeStatus;
3132
private byte[] _hackticeHeader = new byte[HackticeHeaderSize];
3233
private byte[] _ram; // just a cache
3334

@@ -297,6 +298,7 @@ public bool RefreshHacktice()
297298
return true;
298299

299300
_ptrHackticeHeader = null;
301+
_ptrHackticeStatus = null;
300302
}
301303

302304
if (!(_ram is object))
@@ -309,6 +311,7 @@ public bool RefreshHacktice()
309311
{
310312
// attempt all locations and find if any is reasonable
311313
_ptrHackticeHeader = new IntPtr((long)(_ramPtrBase + (ulong)location));
314+
_ptrHackticeStatus = new IntPtr((long)(_ramPtrBase + (ulong)location + 8));
312315
if (RefreshHackticeHeaderAndCheckIfValid())
313316
return true;
314317
}
@@ -438,5 +441,10 @@ public void FixSapphireTimer()
438441
_process.WriteBytes(new IntPtr((long)(_ramPtrBase + 0x2E3A4C)), new byte[] { 0xf9, 0x00 });
439442
_process.WriteBytes(new IntPtr((long)(_ramPtrBase + 0x2E3A34)), new byte[] { 0xe5, 0x00 });
440443
}
444+
445+
public void WriteStatus(uint status)
446+
{
447+
_process.WriteBytes(_ptrHackticeStatus.Value, BitConverter.GetBytes(status));
448+
}
441449
}
442450
}

Hacktice/Tool.cs

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ enum State
2626
};
2727

2828
const string SapphireEEPROMName = "SM64 Sapphire";
29+
const int MaxBeatsToCheck = 200;
30+
const int HeartbeatLeaveaway = 10;
2931

3032
readonly System.Threading.Timer _timer;
3133

@@ -34,10 +36,11 @@ enum State
3436
readonly Emulator _emulator = new Emulator();
3537
readonly Version _payloadVersion;
3638
Config _lastSeenEmulatorConfig = new Config();
39+
int _heartbeatActive = 0;
3740
private State EmulatorState
3841
{
3942
get { return _stateValue; }
40-
set { var oldValue = _stateValue; _stateValue = value; if (oldValue != _stateValue) { UpdateEmulatorState(); } }
43+
set { var oldValue = _stateValue; _stateValue = value; if (oldValue != _stateValue) { _heartbeatActive = 0; UpdateEmulatorState(); } }
4144
}
4245

4346
// Used from UI thread to avoid event loops
@@ -318,30 +321,44 @@ private void PrepareHacktice()
318321
if (!_emulator.RefreshHacktice())
319322
return;
320323

321-
newState = State.HACKTICE_CORRUPTED;
324+
if (_heartbeatActive >= 0)
322325
{
323-
int status = _emulator.HackticeStatus;
324-
if (status == Canary.HackticeStatusInit)
326+
newState = State.HACKTICE_CORRUPTED;
325327
{
326-
newState = State.HACKTICE_INJECTED;
327-
return;
328-
}
329-
if (status == Canary.HackticeStatusActive)
330-
{
331-
newState = _emulator.HackticeVersion == _payloadVersion ? State.HACKTICE_RUNNING : State.HACKTICE_RUNNING_CAN_UPGRADE;
332-
return;
333-
}
334-
if (status == Canary.HackticeStatusDisabled)
335-
{
336-
newState = State.HACKTICE_UPGRADE_DISABLED;
337-
return;
338-
}
339-
if (status == Canary.HackticeStatusUpgrading)
340-
{
341-
newState = State.HACKTICE_UPGRADE_DATA_WRITTEN;
342-
return;
328+
int status = _emulator.HackticeStatus;
329+
if (status == Canary.HackticeStatusInit)
330+
{
331+
newState = State.HACKTICE_INJECTED;
332+
return;
333+
}
334+
if (status == Canary.HackticeStatusHeartbeat)
335+
{
336+
newState = State.ROM;
337+
return;
338+
}
339+
if (status == Canary.HackticeStatusActive)
340+
{
341+
newState = _emulator.HackticeVersion == _payloadVersion ? State.HACKTICE_RUNNING : State.HACKTICE_RUNNING_CAN_UPGRADE;
342+
return;
343+
}
344+
if (status == Canary.HackticeStatusDisabled)
345+
{
346+
newState = State.HACKTICE_UPGRADE_DISABLED;
347+
return;
348+
}
349+
if (status == Canary.HackticeStatusUpgrading)
350+
{
351+
newState = State.HACKTICE_UPGRADE_DATA_WRITTEN;
352+
return;
353+
}
343354
}
344355
}
356+
else
357+
{
358+
// there is a bit of leaveaway before we start complaining that hacktice does not run
359+
newState = State.HACKTICE_RUNNING;
360+
return;
361+
}
345362
}
346363
finally
347364
{
@@ -407,6 +424,13 @@ private void EmulatorStateUpdate(object state)
407424

408425
if (EmulatorState >= State.HACKTICE_RUNNING)
409426
{
427+
_heartbeatActive++;
428+
if (_heartbeatActive >= MaxBeatsToCheck)
429+
{
430+
_emulator.WriteStatus(Canary.HackticeStatusHeartbeat);
431+
_heartbeatActive = -HeartbeatLeaveaway;
432+
}
433+
410434
var userUpdatedConfig = NeedToUpdateConfig;
411435

412436
// TODO: This logic gets complicated, separate this away

0 commit comments

Comments
 (0)