From 92084d06a5c87338cc988b5bc5868e617213e6b9 Mon Sep 17 00:00:00 2001
From: jbzdarkid <jbzdarkid@gmail.com>
Date: Sun, 24 Nov 2019 12:28:53 -0800
Subject: Try/catch, and select seed

---
 Source/Memory.cpp           |  34 +++++--------
 Source/Memory.h             |  36 ++++---------
 Source/MemoryException.h    |  53 +++++++++++++++++++
 Source/PuzzleSerializer.cpp | 120 +++++++++++++++++++++++---------------------
 Source/Randomizer.cpp       |  35 ++++++-------
 Source/Randomizer2.cpp      |   8 +--
 Source/Randomizer2.h        |   6 +--
 Source/Source.vcxproj       |   1 +
 8 files changed, 165 insertions(+), 128 deletions(-)
 create mode 100644 Source/MemoryException.h

(limited to 'Source')

diff --git a/Source/Memory.cpp b/Source/Memory.cpp
index 55ef18a..80cd103 100644
--- a/Source/Memory.cpp
+++ b/Source/Memory.cpp
@@ -22,22 +22,22 @@ Memory::~Memory() {
     }
 }
 
-void Memory::StartHeartbeat(HWND window, std::chrono::milliseconds beat) {
+void Memory::StartHeartbeat(HWND window, WPARAM wParam, std::chrono::milliseconds beat) {
     if (_threadActive) return;
     _threadActive = true;
-    _thread = std::thread([sharedThis = shared_from_this(), window, beat]{
+    _thread = std::thread([sharedThis = shared_from_this(), window, wParam, beat]{
         while (sharedThis->_threadActive) {
-            sharedThis->Heartbeat(window);
+            sharedThis->Heartbeat(window, wParam);
             std::this_thread::sleep_for(beat);
         }
     });
     _thread.detach();
 }
 
-void Memory::Heartbeat(HWND window) {
+void Memory::Heartbeat(HWND window, WPARAM wParam) {
     if (!_handle && !Initialize()) {
         // Couldn't initialize, definitely not running
-        PostMessage(window, WM_COMMAND, HEARTBEAT, (LPARAM)ProcStatus::NotRunning);
+        PostMessage(window, WM_COMMAND, wParam, (LPARAM)ProcStatus::NotRunning);
         return;
     }
 
@@ -48,7 +48,7 @@ void Memory::Heartbeat(HWND window) {
         // Process has exited, clean up.
         _computedAddresses.clear();
         _handle = NULL;
-        PostMessage(window, WM_COMMAND, HEARTBEAT, (LPARAM)ProcStatus::NotRunning);
+        PostMessage(window, WM_COMMAND, wParam, (LPARAM)ProcStatus::NotRunning);
         return;
     }
 
@@ -62,13 +62,13 @@ void Memory::Heartbeat(HWND window) {
     if (frameDelta < 0 && currentFrame < 250) {
         // Some addresses (e.g. Entity Manager) may get re-allocated on newgame.
         _computedAddresses.clear();
-        PostMessage(window, WM_COMMAND, HEARTBEAT, (LPARAM)ProcStatus::NewGame);
+        PostMessage(window, WM_COMMAND, wParam, (LPARAM)ProcStatus::NewGame);
         return;
     }
 
     // TODO: Some way to return ProcStatus::Randomized vs ProcStatus::NotRandomized vs ProcStatus::DeRandomized;
 
-    PostMessage(window, WM_COMMAND, HEARTBEAT, (LPARAM)ProcStatus::Running);
+    PostMessage(window, WM_COMMAND, wParam, (LPARAM)ProcStatus::Running);
 }
 
 [[nodiscard]]
@@ -151,16 +151,6 @@ int Memory::ExecuteSigScans()
     return notFound;
 }
 
-void Memory::ThrowError() {
-    std::wstring message(256, '\0');
-    DWORD error = GetLastError();
-    int length = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, error, 1024, &message[0], static_cast<DWORD>(message.size()), nullptr);
-    message.resize(length);
-#ifndef NDEBUG
-    MessageBox(NULL, message.c_str(), L"Please tell darkid about this", MB_OK);
-#endif
-}
-
 void* Memory::ComputeOffset(std::vector<int> offsets) {
     // Leave off the last offset, since it will be either read/write, and may not be of type uintptr_t.
     int final_offset = offsets.back();
@@ -177,11 +167,11 @@ void* Memory::ComputeOffset(std::vector<int> offsets) {
 #endif
             // If the address is not yet computed, then compute it.
             uintptr_t computedAddress = 0;
-            if (bool result = !ReadProcessMemory(_handle, reinterpret_cast<LPVOID>(cumulativeAddress), &computedAddress, sizeof(uintptr_t), NULL)) {
-                ThrowError();
+            if (!ReadProcessMemory(_handle, reinterpret_cast<LPVOID>(cumulativeAddress), &computedAddress, sizeof(uintptr_t), NULL)) {
+                MEMORY_THROW("Couldn't compute offset.", offsets);
             }
-            if (computedAddress == 0) { // Attempting to dereference a nullptr
-                ThrowError();
+            if (computedAddress == 0) {
+                MEMORY_THROW("Attempted to derefence NULL while computing offsets.", offsets);
             }
             _computedAddresses[cumulativeAddress] = computedAddress;
 #ifdef NDEBUG
diff --git a/Source/Memory.h b/Source/Memory.h
index 5332cc3..803a5f1 100644
--- a/Source/Memory.h
+++ b/Source/Memory.h
@@ -4,11 +4,12 @@
 #include <thread>
 #include <vector>
 #include <windows.h>
+#include <cassert>
+#include "MemoryException.h"
 
 // #define GLOBALS 0x5B28C0
 #define GLOBALS 0x62D0A0
 
-#define HEARTBEAT 0x401
 enum class ProcStatus {
     NotRunning,
     Running,
@@ -24,7 +25,7 @@ class Memory final : public std::enable_shared_from_this<Memory> {
 public:
     Memory(const std::wstring& processName);
     ~Memory();
-    void StartHeartbeat(HWND window, std::chrono::milliseconds beat = std::chrono::milliseconds(1000));
+    void StartHeartbeat(HWND window, WPARAM wParam, std::chrono::milliseconds beat = std::chrono::milliseconds(1000));
 
     Memory(const Memory& memory) = delete;
     Memory& operator=(const Memory& other) = delete;
@@ -63,40 +64,25 @@ public:
 private:
     template<class T>
     std::vector<T> ReadData(const std::vector<int>& offsets, size_t numItems) {
-        if (numItems == 0) return {};
+        assert(numItems);
         std::vector<T> data;
         data.resize(numItems);
-        void* computedOffset = ComputeOffset(offsets);
-        for (int i=0; i<5; i++) {
-            if (ReadProcessMemory(_handle, computedOffset, &data[0], sizeof(T) * numItems, nullptr)) {
-                if (i != 0) {
-                    int k = 0;
-                }
-                return data;
-            }
+        if (!ReadProcessMemory(_handle, ComputeOffset(offsets), &data[0], sizeof(T) * numItems, nullptr)) {
+            MEMORY_THROW("Failed to read data.", offsets, numItems);
         }
-        ThrowError();
-        return {};
+        return data;
     }
 
     template <class T>
     void WriteData(const std::vector<int>& offsets, const std::vector<T>& data) {
-        if (data.empty()) return;
-        void* computedOffset = ComputeOffset(offsets);
-        for (int i=0; i<5; i++) {
-            if (WriteProcessMemory(_handle, computedOffset, &data[0], sizeof(T) * data.size(), nullptr)) {
-                if (i != 0) {
-                    int k = 0;
-                }
-                return;
-            }
+        assert(data.size());
+        if (!WriteProcessMemory(_handle, ComputeOffset(offsets), &data[0], sizeof(T) * data.size(), nullptr)) {
+            MEMORY_THROW("Failed to write data.", offsets, data.size());
         }
-        ThrowError();
     }
 
-    void Heartbeat(HWND window);
+    void Heartbeat(HWND window, WPARAM wParam);
     bool Initialize();
-    void ThrowError();
     void* ComputeOffset(std::vector<int> offsets);
 
     int _previousFrame = 0;
diff --git a/Source/MemoryException.h b/Source/MemoryException.h
new file mode 100644
index 0000000..ad2824d
--- /dev/null
+++ b/Source/MemoryException.h
@@ -0,0 +1,53 @@
+#pragma once
+#include <exception>
+#include <string>
+#include <vector>
+
+#define MEMORY_CATCH(expr)                 \
+try {                                      \
+    (expr);                                \
+} catch (MemoryException exc) {            \
+    MemoryException::HandleException(exc); \
+}                                          \
+do {} while (0)
+
+#define MEMORY_THROW(...) throw MemoryException(__func__, __LINE__, ##__VA_ARGS__);
+
+class MemoryException : public std::exception {
+public:
+    inline MemoryException(const char* func, int32_t line, const char* message) noexcept
+        : MemoryException(func, line, message, {}, 0) {}
+    inline MemoryException(const char* func, int32_t line, const char* message, const std::vector<int>& offsets) noexcept
+        : MemoryException(func, line, message, offsets, 0) {}
+    inline MemoryException(const char* func, int32_t line, const char* message, const std::vector<int>& offsets, size_t numItems) noexcept
+        : _func(func), _line(line), _message(message), _offsets(offsets), _numItems(numItems) {}
+    
+    ~MemoryException() = default;
+    inline const char* what() const noexcept {
+        return _message;
+    }
+    static void HandleException(const MemoryException& exc) noexcept {
+        std::string msg = "MemoryException thrown in function ";
+        msg += exc._func;
+        msg += " on line " + std::to_string(exc._line) + ":\n" + exc._message + "\nOffsets:";
+        for (int offset : exc._offsets) {
+            msg += " " + std::to_string(offset);
+        }
+        msg += "\n";
+        if (exc._numItems != 0) {
+            msg += "Num Items: " + std::to_string(exc._numItems) + "\n";
+        }
+        OutputDebugStringA(msg.c_str());
+#ifndef NDEBUG
+        MessageBoxA(NULL, msg.c_str(), "Memory Exception Thrown", MB_OK);
+#endif
+    }
+
+private:
+    const char* _func;
+    int32_t _line;
+    const char* _message;
+    const std::vector<int> _offsets;
+    size_t _numItems = 0;
+};
+
diff --git a/Source/PuzzleSerializer.cpp b/Source/PuzzleSerializer.cpp
index fb4166b..3dffde1 100644
--- a/Source/PuzzleSerializer.cpp
+++ b/Source/PuzzleSerializer.cpp
@@ -8,73 +8,81 @@
 PuzzleSerializer::PuzzleSerializer(const std::shared_ptr<Memory>& memory) : _memory(memory) {}
 
 Puzzle PuzzleSerializer::ReadPuzzle(int id) {
-    int width = _memory->ReadEntityData<int>(id, GRID_SIZE_X, 1)[0];
-    int height = _memory->ReadEntityData<int>(id, GRID_SIZE_Y, 1)[0];
-    if (width == 0) width = height;
-    if (height == 0) height = width;
-    if (width < 0 || height < 0) return Puzzle(); // @Error: Grid size should be always positive? Looks like the starting panels break this rule, though.
-
-    _numGridLocations = width * height; // Highest location which represents a gridded intersection
-    _numIntersections = _memory->ReadEntityData<int>(id, NUM_DOTS, 1)[0];
-    _intersectionFlags = _memory->ReadArray<int>(id, DOT_FLAGS, _numIntersections);
-    int numConnections = _memory->ReadEntityData<int>(id, NUM_CONNECTIONS, 1)[0];
-    _connectionsA = _memory->ReadArray<int>(id, DOT_CONNECTION_A, numConnections);
-    _connectionsB = _memory->ReadArray<int>(id, DOT_CONNECTION_B, numConnections);
-    _intersectionLocations = _memory->ReadArray<float>(id, DOT_POSITIONS, _numIntersections*2);
-
     Puzzle p;
-    p.NewGrid(width - 1, height - 1);
-    ReadIntersections(p);
-    ReadExtras(p);
-    ReadDecorations(p, id);
-    ReadSequence(p, id);
-    ReadSymmetry(p, id);
+    try {
+        int width = _memory->ReadEntityData<int>(id, GRID_SIZE_X, 1)[0];
+        int height = _memory->ReadEntityData<int>(id, GRID_SIZE_Y, 1)[0];
+        if (width == 0) width = height;
+        if (height == 0) height = width;
+        if (width < 0 || height < 0) return Puzzle(); // @Error: Grid size should be always positive? Looks like the starting panels break this rule, though.
+
+        _numGridLocations = width * height; // Highest location which represents a gridded intersection
+        _numIntersections = _memory->ReadEntityData<int>(id, NUM_DOTS, 1)[0];
+        _intersectionFlags = _memory->ReadArray<int>(id, DOT_FLAGS, _numIntersections);
+        int numConnections = _memory->ReadEntityData<int>(id, NUM_CONNECTIONS, 1)[0];
+        _connectionsA = _memory->ReadArray<int>(id, DOT_CONNECTION_A, numConnections);
+        _connectionsB = _memory->ReadArray<int>(id, DOT_CONNECTION_B, numConnections);
+        _intersectionLocations = _memory->ReadArray<float>(id, DOT_POSITIONS, _numIntersections*2);
+
+        p.NewGrid(width - 1, height - 1);
+        ReadIntersections(p);
+        ReadExtras(p);
+        ReadDecorations(p, id);
+        ReadSequence(p, id);
+        ReadSymmetry(p, id);
+    } catch (MemoryException exc) {
+        MemoryException::HandleException(exc);
+    }
     return p;
 }
 
 void PuzzleSerializer::WritePuzzle(const Puzzle& p, int id) {
-    _intersectionFlags.clear();
-    _connectionsA.clear();
-    _connectionsB.clear();
-    _intersectionLocations.clear();
-    _extraLocations.clear();
-
-    MIN = 0.1f;
-    MAX = 0.9f;
-    WIDTH_INTERVAL = (MAX - MIN) / (p.width/2);
-    HEIGHT_INTERVAL = (MAX - MIN) / (p.height/2);
-    GAP_SIZE = min(WIDTH_INTERVAL, HEIGHT_INTERVAL) / 2;
-    // @Improvement: This will make grid cells square... but how do I keep the puzzle centered? Maybe save extra metadata?
-    // INTERVAL = (MAX - MIN) / (max(p.width, p.height) / 2);
-    // GAP_SIZE = INTERVAL / 2;
+    try {
+        _intersectionFlags.clear();
+        _connectionsA.clear();
+        _connectionsB.clear();
+        _intersectionLocations.clear();
+        _extraLocations.clear();
+
+        MIN = 0.1f;
+        MAX = 0.9f;
+        WIDTH_INTERVAL = (MAX - MIN) / (p.width/2);
+        HEIGHT_INTERVAL = (MAX - MIN) / (p.height/2);
+        GAP_SIZE = min(WIDTH_INTERVAL, HEIGHT_INTERVAL) / 2;
+        // @Improvement: This will make grid cells square... but how do I keep the puzzle centered? Maybe save extra metadata?
+        // INTERVAL = (MAX - MIN) / (max(p.width, p.height) / 2);
+        // GAP_SIZE = INTERVAL / 2;
     
-    WriteIntersections(p);
-    WriteEndpoints(p);
-    WriteDots(p);
-    WriteGaps(p);
-    WriteDecorations(p, id);
-    WriteSequence(p, id);
-    WriteSymmetry(p, id);
+        WriteIntersections(p);
+        WriteEndpoints(p);
+        WriteDots(p);
+        WriteGaps(p);
+        WriteDecorations(p, id);
+        WriteSequence(p, id);
+        WriteSymmetry(p, id);
 
 #ifndef NDEBUG
-    int maxDots = _memory->ReadEntityData<int>(id, NUM_DOTS, 1)[0];
-    assert(_intersectionFlags.size() <= maxDots);
-    assert(_intersectionLocations.size() <= maxDots*2);
+        int maxDots = _memory->ReadEntityData<int>(id, NUM_DOTS, 1)[0];
+        assert(_intersectionFlags.size() <= maxDots);
+        assert(_intersectionLocations.size() <= maxDots*2);
 
-    int maxConnections = _memory->ReadEntityData<int>(id, NUM_CONNECTIONS, 1)[0];
-    assert(_connectionsA.size() <= maxConnections);
-    assert(_connectionsB.size() <= maxConnections);
+        int maxConnections = _memory->ReadEntityData<int>(id, NUM_CONNECTIONS, 1)[0];
+        assert(_connectionsA.size() <= maxConnections);
+        assert(_connectionsB.size() <= maxConnections);
 #endif
 
-    _memory->WriteEntityData<int>(id, GRID_SIZE_X, {(p.width + 1)/2});
-    _memory->WriteEntityData<int>(id, GRID_SIZE_Y, {(p.height + 1)/2});
-    _memory->WriteEntityData<int>(id, NUM_DOTS, {static_cast<int>(_intersectionFlags.size())});
-    _memory->WriteArray<float>(id, DOT_POSITIONS, _intersectionLocations);
-    _memory->WriteArray<int>(id, DOT_FLAGS, _intersectionFlags);
-    _memory->WriteEntityData<int>(id, NUM_CONNECTIONS, {static_cast<int>(_connectionsA.size())});
-    _memory->WriteArray<int>(id, DOT_CONNECTION_A, _connectionsA);
-    _memory->WriteArray<int>(id, DOT_CONNECTION_B, _connectionsB);
-    _memory->WriteEntityData<int>(id, NEEDS_REDRAW, {1});
+        _memory->WriteEntityData<int>(id, GRID_SIZE_X, {(p.width + 1)/2});
+        _memory->WriteEntityData<int>(id, GRID_SIZE_Y, {(p.height + 1)/2});
+        _memory->WriteEntityData<int>(id, NUM_DOTS, {static_cast<int>(_intersectionFlags.size())});
+        _memory->WriteArray<float>(id, DOT_POSITIONS, _intersectionLocations);
+        _memory->WriteArray<int>(id, DOT_FLAGS, _intersectionFlags);
+        _memory->WriteEntityData<int>(id, NUM_CONNECTIONS, {static_cast<int>(_connectionsA.size())});
+        _memory->WriteArray<int>(id, DOT_CONNECTION_A, _connectionsA);
+        _memory->WriteArray<int>(id, DOT_CONNECTION_B, _connectionsB);
+        _memory->WriteEntityData<int>(id, NEEDS_REDRAW, {1});
+    } catch (MemoryException exc) {
+        MemoryException::HandleException(exc);
+    }
 }
 
 void PuzzleSerializer::ReadIntersections(Puzzle& p) {
diff --git a/Source/Randomizer.cpp b/Source/Randomizer.cpp
index 13f381a..1427f4d 100644
--- a/Source/Randomizer.cpp
+++ b/Source/Randomizer.cpp
@@ -129,28 +129,28 @@ void Randomizer::Randomize() {
     // Sig scans will be run during challenge randomization.
 
     // Seed challenge first for future-proofing
-    RandomizeChallenge();
+    MEMORY_CATCH(RandomizeChallenge());
 
     // Content swaps -- must happen before squarePanels
-    Randomize(upDownPanels, SWAP::LINES | SWAP::COLORS);
-    Randomize(leftForwardRightPanels, SWAP::LINES | SWAP::COLORS);
+    MEMORY_CATCH(Randomize(upDownPanels, SWAP::LINES | SWAP::COLORS));
+    MEMORY_CATCH(Randomize(leftForwardRightPanels, SWAP::LINES | SWAP::COLORS));
 
-    Randomize(squarePanels, SWAP::LINES | SWAP::COLORS);
+    MEMORY_CATCH(Randomize(squarePanels, SWAP::LINES | SWAP::COLORS));
 
     // Individual area modifications
-    RandomizeTutorial();
-    RandomizeDesert();
-    RandomizeQuarry();
-    RandomizeTreehouse();
-    RandomizeKeep();
-    RandomizeShadows();
-    RandomizeMonastery();
-    RandomizeBunker();
-    RandomizeJungle();
-    RandomizeSwamp();
-    RandomizeMountain();
-    RandomizeTown();
-    RandomizeSymmetry();
+    MEMORY_CATCH(RandomizeTutorial());
+    MEMORY_CATCH(RandomizeDesert());
+    MEMORY_CATCH(RandomizeQuarry());
+    MEMORY_CATCH(RandomizeTreehouse());
+    MEMORY_CATCH(RandomizeKeep());
+    MEMORY_CATCH(RandomizeShadows());
+    MEMORY_CATCH(RandomizeMonastery());
+    MEMORY_CATCH(RandomizeBunker());
+    MEMORY_CATCH(RandomizeJungle());
+    MEMORY_CATCH(RandomizeSwamp());
+    MEMORY_CATCH(RandomizeMountain());
+    MEMORY_CATCH(RandomizeTown());
+    MEMORY_CATCH(RandomizeSymmetry());
     // RandomizeAudioLogs();
 }
 
@@ -210,6 +210,7 @@ void Randomizer::RandomizeQuarry() {
 
 void Randomizer::RandomizeTreehouse() {
     // Ensure that whatever pivot panels we have are flagged as "pivotable"
+    // @Bug: Can return {}, be careful!
     int panelFlags = _memory->ReadEntityData<int>(0x17DD1, STYLE_FLAGS, 1)[0];
     _memory->WriteEntityData<int>(0x17DD1, STYLE_FLAGS, {panelFlags | 0x8000});
     panelFlags = _memory->ReadEntityData<int>(0x17CE3, STYLE_FLAGS, 1)[0];
diff --git a/Source/Randomizer2.cpp b/Source/Randomizer2.cpp
index c823567..782e248 100644
--- a/Source/Randomizer2.cpp
+++ b/Source/Randomizer2.cpp
@@ -1,5 +1,5 @@
-#include "Memory.h"
 #include "Randomizer2.h"
+#include "PuzzleSerializer.h"
 #include "Randomizer2Core.h"
 #include "Puzzle.h"
 #include "Random.h"
@@ -10,7 +10,7 @@
 
 #pragma warning (disable: 26451)
 
-Randomizer2::Randomizer2(const std::shared_ptr<Memory>& memory) : _memory(memory), _serializer(PuzzleSerializer(_memory)) {}
+Randomizer2::Randomizer2(const PuzzleSerializer& serializer) : _serializer(serializer) {}
 
 void Randomizer2::Randomize() {
     RandomizeTutorial();
@@ -361,9 +361,9 @@ void Randomizer2::SetGate(int panel, int X, int Y) {
     }
 
     SetPos(panel, x, y, 19.2f);
-    _memory->WriteEntityData<float>(panel, ORIENTATION, {0.0f, 0.0f, z, w});
+    // _memory->WriteEntityData<float>(panel, ORIENTATION, {0.0f, 0.0f, z, w});
 }
 
 void Randomizer2::SetPos(int panel, float x, float y, float z) {
-    _memory->WriteEntityData<float>(panel, POSITION, {x, y, z});
+    // _memory->WriteEntityData<float>(panel, POSITION, {x, y, z});
 }
\ No newline at end of file
diff --git a/Source/Randomizer2.h b/Source/Randomizer2.h
index 47a9ebd..c8c3db5 100644
--- a/Source/Randomizer2.h
+++ b/Source/Randomizer2.h
@@ -1,11 +1,10 @@
 #pragma once
-#include <memory>
 #include "PuzzleSerializer.h"
 
-class Memory;
+class Puzzle;
 class Randomizer2 {
 public:
-    Randomizer2(const std::shared_ptr<Memory>& memory);
+    Randomizer2(const PuzzleSerializer& serializer);
     void Randomize();
     void RandomizeTutorial();
     void RandomizeSymmetry();
@@ -16,6 +15,5 @@ private:
     void SetGate(int panel, int X, int Y);
     void SetPos(int panel, float x, float y, float z);
 
-    std::shared_ptr<Memory> _memory;
     PuzzleSerializer _serializer;
 };
diff --git a/Source/Source.vcxproj b/Source/Source.vcxproj
index 33e3697..5aaa0b0 100644
--- a/Source/Source.vcxproj
+++ b/Source/Source.vcxproj
@@ -159,6 +159,7 @@
   <ItemGroup>
     <ClInclude Include="ChallengeRandomizer.h" />
     <ClInclude Include="Memory.h" />
+    <ClInclude Include="MemoryException.h" />
     <ClInclude Include="Puzzle.h" />
     <ClInclude Include="Panels.h" />
     <ClInclude Include="PuzzleSerializer.h" />
-- 
cgit 1.4.1