From 92084d06a5c87338cc988b5bc5868e617213e6b9 Mon Sep 17 00:00:00 2001 From: jbzdarkid 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(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 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 offsets) { #endif // If the address is not yet computed, then compute it. uintptr_t computedAddress = 0; - if (bool result = !ReadProcessMemory(_handle, reinterpret_cast(cumulativeAddress), &computedAddress, sizeof(uintptr_t), NULL)) { - ThrowError(); + if (!ReadProcessMemory(_handle, reinterpret_cast(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 #include #include +#include +#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 { 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 std::vector ReadData(const std::vector& offsets, size_t numItems) { - if (numItems == 0) return {}; + assert(numItems); std::vector 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 void WriteData(const std::vector& offsets, const std::vector& 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 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 +#include +#include + +#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& offsets) noexcept + : MemoryException(func, line, message, offsets, 0) {} + inline MemoryException(const char* func, int32_t line, const char* message, const std::vector& 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 _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) {} Puzzle PuzzleSerializer::ReadPuzzle(int id) { - int width = _memory->ReadEntityData(id, GRID_SIZE_X, 1)[0]; - int height = _memory->ReadEntityData(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(id, NUM_DOTS, 1)[0]; - _intersectionFlags = _memory->ReadArray(id, DOT_FLAGS, _numIntersections); - int numConnections = _memory->ReadEntityData(id, NUM_CONNECTIONS, 1)[0]; - _connectionsA = _memory->ReadArray(id, DOT_CONNECTION_A, numConnections); - _connectionsB = _memory->ReadArray(id, DOT_CONNECTION_B, numConnections); - _intersectionLocations = _memory->ReadArray(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(id, GRID_SIZE_X, 1)[0]; + int height = _memory->ReadEntityData(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(id, NUM_DOTS, 1)[0]; + _intersectionFlags = _memory->ReadArray(id, DOT_FLAGS, _numIntersections); + int numConnections = _memory->ReadEntityData(id, NUM_CONNECTIONS, 1)[0]; + _connectionsA = _memory->ReadArray(id, DOT_CONNECTION_A, numConnections); + _connectionsB = _memory->ReadArray(id, DOT_CONNECTION_B, numConnections); + _intersectionLocations = _memory->ReadArray(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(id, NUM_DOTS, 1)[0]; - assert(_intersectionFlags.size() <= maxDots); - assert(_intersectionLocations.size() <= maxDots*2); + int maxDots = _memory->ReadEntityData(id, NUM_DOTS, 1)[0]; + assert(_intersectionFlags.size() <= maxDots); + assert(_intersectionLocations.size() <= maxDots*2); - int maxConnections = _memory->ReadEntityData(id, NUM_CONNECTIONS, 1)[0]; - assert(_connectionsA.size() <= maxConnections); - assert(_connectionsB.size() <= maxConnections); + int maxConnections = _memory->ReadEntityData(id, NUM_CONNECTIONS, 1)[0]; + assert(_connectionsA.size() <= maxConnections); + assert(_connectionsB.size() <= maxConnections); #endif - _memory->WriteEntityData(id, GRID_SIZE_X, {(p.width + 1)/2}); - _memory->WriteEntityData(id, GRID_SIZE_Y, {(p.height + 1)/2}); - _memory->WriteEntityData(id, NUM_DOTS, {static_cast(_intersectionFlags.size())}); - _memory->WriteArray(id, DOT_POSITIONS, _intersectionLocations); - _memory->WriteArray(id, DOT_FLAGS, _intersectionFlags); - _memory->WriteEntityData(id, NUM_CONNECTIONS, {static_cast(_connectionsA.size())}); - _memory->WriteArray(id, DOT_CONNECTION_A, _connectionsA); - _memory->WriteArray(id, DOT_CONNECTION_B, _connectionsB); - _memory->WriteEntityData(id, NEEDS_REDRAW, {1}); + _memory->WriteEntityData(id, GRID_SIZE_X, {(p.width + 1)/2}); + _memory->WriteEntityData(id, GRID_SIZE_Y, {(p.height + 1)/2}); + _memory->WriteEntityData(id, NUM_DOTS, {static_cast(_intersectionFlags.size())}); + _memory->WriteArray(id, DOT_POSITIONS, _intersectionLocations); + _memory->WriteArray(id, DOT_FLAGS, _intersectionFlags); + _memory->WriteEntityData(id, NUM_CONNECTIONS, {static_cast(_connectionsA.size())}); + _memory->WriteArray(id, DOT_CONNECTION_A, _connectionsA); + _memory->WriteArray(id, DOT_CONNECTION_B, _connectionsB); + _memory->WriteEntityData(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(0x17DD1, STYLE_FLAGS, 1)[0]; _memory->WriteEntityData(0x17DD1, STYLE_FLAGS, {panelFlags | 0x8000}); panelFlags = _memory->ReadEntityData(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), _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(panel, ORIENTATION, {0.0f, 0.0f, z, w}); + // _memory->WriteEntityData(panel, ORIENTATION, {0.0f, 0.0f, z, w}); } void Randomizer2::SetPos(int panel, float x, float y, float z) { - _memory->WriteEntityData(panel, POSITION, {x, y, z}); + // _memory->WriteEntityData(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 #include "PuzzleSerializer.h" -class Memory; +class Puzzle; class Randomizer2 { public: - Randomizer2(const std::shared_ptr& 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; 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 @@ + -- cgit 1.4.1