{"action":"created","comment":{"url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/pulls/comments/51359316","id":51359316,"diff_hunk":"@@ -0,0 +1,1121 @@\n+/*\r\n+ * (c) 2015- Hourglass Resurrection Team\r\n+ * Hourglass Resurrection is licensed under GPL v2.\r\n+ * Refer to the file COPYING.txt in the project root.\r\n+ */\r\n+\r\n+#include <atomic>\r\n+#include <map>\r\n+#include <vector>\r\n+\r\n+#include <global.h>\r\n+#include <MemoryManager\\MemoryManager.h>\r\n+#include <shared\\ipc.h>\r\n+#include <Utils.h>\r\n+/*\r\n+ * TODO: Including this breaks input hooks.\r\n+ * -- Warepire\r\n+ */\r\n+//#include <wintasee.h>\r\n+\r\n+/*\r\n+ * This map will contain all memory allocations made by the GlobalAlloc and LocalAlloc families,\r\n+ * and their respective lock counts and/or flags.\r\n+ * According to MSDN docs the value returned by the [X]Flags calls contain no flasgs or\r\n+ * [x]_DISCARDED. According to the WinBase.h header, the flags may also contain [X]_DISCARDABLE.\r\n+ * Since [X]_FIXED allocations cannot be discarded, or carry a lock count, they are indicated by\r\n+ * the value UINT_MAX. This is because they must be separated from unknown (invalid) pointers.\r\n+ */\r\n+LazyType<std::map<LPVOID,\r\n+                  UINT,\r\n+                  std::less<LPVOID>,\r\n+                  ManagedAllocator<std::pair<LPVOID, UINT>>>> g_alloc_flags;\r\n+\r\n+/*\r\n+ * Emulate the Windows Heap objects using the minimum information necessary.\r\n+ */\r\n+struct HeapObject\r\n+{\r\n+    std::vector<LPVOID, ManagedAllocator<LPVOID>> heap_segments;\r\n+    DWORD heap_flags;\r\n+    std::atomic_flag heap_lock;\r\n+    SIZE_T heap_current_size;\r\n+    SIZE_T heap_maximum_size;\r\n+\r\n+    HeapObject(DWORD flags, SIZE_T current_size, SIZE_T max_size) :\r\n+        heap_flags(flags),\r\n+        heap_current_size(current_size),\r\n+        heap_maximum_size(max_size)\r\n+    {\r\n+        heap_lock.clear();\r\n+    }\r\n+};\r\n+HeapObject* g_default_heap = nullptr;\r\n+LazyType<std::vector<HeapObject*, ManagedAllocator<HeapObject*>>> g_heaps;\r\n+\r\n+HOOKFUNC LPVOID WINAPI MyGlobalLock(HGLOBAL hMem);\r\n+HOOKFUNC BOOL WINAPI MyGlobalUnlock(HGLOBAL hMem);\r\n+HOOKFUNC LPVOID WINAPI MyHeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes);\r\n+HOOKFUNC BOOL WINAPI MyHeapLock(HANDLE hHeap);\r\n+HOOKFUNC BOOL WINAPI MyHeapUnlock(HANDLE hHeap);\r\n+HOOKFUNC BOOL WINAPI MyIsBadReadPtr(const void *lp, UINT_PTR ucb);\r\n+HOOKFUNC BOOL WINAPI MyIsBadWritePtr(LPVOID lp, UINT_PTR ucb);\r\n+\r\n+HOOKFUNC BOOL WINAPI MyAllocateUserPhysicalPages(HANDLE hProcess,\r\n+                                                 PULONG_PTR NumberOfPages,\r\n+                                                 PULONG_PTR PageArray)\r\n+{\r\n+    debugprintf(__FUNCTION__ \"(0x%p, 0x%p) called: 0x%p\\n\", NumberOfPages, PageArray, hProcess);\r\n+    MessageBoxA(nullptr,//gamehwnd,\r\n+                __FUNCTION__ \" is not implemented.\\n\"\r\n+                \"The application will most likely experience problems with SaveStates now.\",\r\n+                \"Warning!\",\r\n+                MB_ICONWARNING | MB_OK);\r\n+    /*\r\n+     * NYI\r\n+     */\r\n+    return AllocateUserPhysicalPages(hProcess, NumberOfPages, PageArray);\r\n+}\r\n+\r\n+HOOKFUNC BOOL WINAPI MyFreeUserPhysicalPages(HANDLE hProcess,\r\n+                                             PULONG_PTR NumberOfPages,\r\n+                                             PULONG_PTR PageArray)\r\n+{\r\n+    debugprintf(__FUNCTION__ \"(0x%p, 0x%p) called: 0x%p\\n\", NumberOfPages, PageArray, hProcess);\r\n+    MessageBoxA(nullptr,//gamehwnd,\r\n+                __FUNCTION__ \" is not implemented.\\n\"\r\n+                \"The application will most likely experience problems with SaveStates now.\",\r\n+                \"Warning!\",\r\n+                MB_ICONWARNING | MB_OK);\r\n+    /*\r\n+     * NYI\r\n+     */\r\n+    return FreeUserPhysicalPages(hProcess, NumberOfPages, PageArray);\r\n+}\r\n+\r\n+/*\r\n+ * Even though assigned a default heap by Windows, we cannot use that one.\r\n+ * It's not guaranteed that heap can be restored on load-state.\r\n+ * Use a different heap as the \"default\".\r\n+ */\r\n+HOOKFUNC HANDLE WINAPI MyGetProcessHeap()\r\n+{\r\n+    debugprintf(__FUNCTION__ \" called.\\n\");\r\n+    HeapObject* rv = g_default_heap;\r\n+    if (rv == nullptr)\r\n+    {\r\n+        /*\r\n+         * TODO: Make this code path use MyHeapCreate\r\n+         * -- Warepire\r\n+         */\r\n+        rv = static_cast<HeapObject*>(MemoryManager::Allocate(sizeof(*rv),\r\n+                                                              MemoryManager::ALLOC_WRITE));\r\n+        if (rv == nullptr)\r\n+        {\r\n+            SetLastError(ERROR_OUTOFMEMORY);\r\n+        }\r\n+        else\r\n+        {\r\n+            rv = ::new HeapObject(0, 0x100000, 0);\r\n+            LPVOID first_segment = MyHeapAlloc(rv, 0, 0x100000);\r\n+            if (first_segment == nullptr)\r\n+            {\r\n+                SetLastError(ERROR_OUTOFMEMORY);\r\n+                MemoryManager::Deallocate(rv);\r\n+                rv = nullptr;\r\n+            }\r\n+            else\r\n+            {\r\n+                rv->heap_segments.push_back(first_segment);\r\n+                g_default_heap = rv;\r\n+                /*\r\n+                 * Keep the default heap in the front of the vector.\r\n+                 */\r\n+                g_heaps().insert(g_heaps().begin(), rv);\r\n+            }\r\n+        }\r\n+    }\r\n+    return rv;\r\n+}\r\n+\r\n+HOOKFUNC DWORD WINAPI MyGetProcessHeaps(DWORD NumberOfHeaps, PHANDLE ProcessHeaps)\r\n+{\r\n+    debugprintf(__FUNCTION__ \"(0x%X 0x%p) called.\\n\", NumberOfHeaps, ProcessHeaps);\r\n+    DWORD rv = (g_heaps().size() > NumberOfHeaps) ? NumberOfHeaps : g_heaps().size();\r\n+    if (g_default_heap == nullptr)\r\n+    {\r\n+        if (MyGetProcessHeap() == nullptr)\r\n+        {\r\n+            *ProcessHeaps = nullptr;\r\n+            return 0;\r\n+        }\r\n+    }\r\n+    for (DWORD i = 0; i < rv; ++i)\r\n+    {\r\n+        ProcessHeaps[i] = g_heaps()[i];\r\n+    }\r\n+    return rv;\r\n+}\r\n+\r\n+HOOKFUNC HGLOBAL WINAPI MyGlobalAlloc(UINT uFlags, SIZE_T dwBytes)\r\n+{\r\n+    UINT alloc_flags = 0;\r\n+    LPVOID address = nullptr;\r\n+    debugprintf(__FUNCTION__ \"(0x%X 0x%X) called.\\n\", uFlags, dwBytes);\r\n+\r\n+    if ((uFlags & GMEM_ZEROINIT) == GMEM_ZEROINIT)\r\n+    {\r\n+        alloc_flags |= MemoryManager::ALLOC_ZEROINIT;\r\n+    }\r\n+    address = MemoryManager::Allocate(dwBytes, alloc_flags);\r\n+    if (address != nullptr)\r\n+    {\r\n+        if ((uFlags & GMEM_MOVEABLE) == GMEM_MOVEABLE)\r\n+        {\r\n+            g_alloc_flags()[address] = (uFlags & GMEM_DISCARDABLE);\r\n+        }\r\n+        else\r\n+        {\r\n+            g_alloc_flags()[address] = UINT_MAX;\r\n+        }\r\n+    }\r\n+    else\r\n+    {\r\n+        SetLastError(ERROR_OUTOFMEMORY);\r\n+    }\r\n+    return address;\r\n+}\r\n+\r\n+HOOKFUNC SIZE_T WINAPI MyGlobalCompact(DWORD dwMinFree)\r\n+{\r\n+    debugprintf(__FUNCTION__ \"(0x%X) called.\\n\", dwMinFree);\r\n+    return 0;\r\n+}\r\n+\r\n+HOOKFUNC void WINAPI MyGlobalFix(HGLOBAL hMem)\r\n+{\r\n+    debugprintf(__FUNCTION__ \"(0x%p) called.\\n\", hMem);\r\n+    MyGlobalLock(hMem);\r\n+}\r\n+\r\n+HOOKFUNC UINT WINAPI MyGlobalFlags(HGLOBAL hMem)\r\n+{\r\n+    debugprintf(__FUNCTION__ \"(0x%p) called.\\n\", hMem);\r\n+    auto it = g_alloc_flags().find(hMem);\r\n+    if (it != g_alloc_flags().end())\r\n+    {\r\n+        if (it->second == UINT_MAX)\r\n+        {\r\n+            return 0;\r\n+        }\r\n+        return it->second;\r\n+    }\r\n+    SetLastError(ERROR_INVALID_HANDLE);\r\n+    return GMEM_INVALID_HANDLE;\r\n+}\r\n+\r\n+HOOKFUNC HGLOBAL WINAPI MyGlobalFree(HGLOBAL hMem)\r\n+{\r\n+    debugprintf(__FUNCTION__ \"(0x%p) called.\\n\", hMem);\r\n+    if (g_alloc_flags().find(hMem) != g_alloc_flags().end())\r\n+    {\r\n+        MemoryManager::Deallocate(hMem);\r\n+        return nullptr;\r\n+    }\r\n+    SetLastError(ERROR_INVALID_HANDLE);\r\n+    return hMem;\r\n+}\r\n+\r\n+HOOKFUNC HGLOBAL WINAPI MyGlobalHandle(LPCVOID pMem)\r\n+{\r\n+    /*\r\n+     * We don't bother with HGLOBAL data structures.\r\n+     */\r\n+    LPVOID address = const_cast<LPVOID>(pMem);\r\n+    debugprintf(__FUNCTION__ \"(0x%p) called.\\n\", pMem);\r\n+    if (address == nullptr)\r\n+    {\r\n+        SetLastError(ERROR_INVALID_PARAMETER);\r\n+        address = nullptr;\r\n+    }\r\n+    else if (g_alloc_flags().find(address) == g_alloc_flags().end())\r\n+    {\r\n+        SetLastError(ERROR_INVALID_HANDLE);\r\n+        address = nullptr;\r\n+    }\r\n+    return address;\r\n+}\r\n+\r\n+HOOKFUNC LPVOID WINAPI MyGlobalLock(HGLOBAL hMem)\r\n+{\r\n+    debugprintf(__FUNCTION__ \"(0x%p) called.\\n\", hMem);\r\n+    LPVOID rv = hMem;\r\n+    auto it = g_alloc_flags().find(hMem);\r\n+    if (hMem == nullptr)\r\n+    {\r\n+        SetLastError(ERROR_DISCARDED);\r\n+        rv = nullptr;\r\n+    }\r\n+    else if (it == g_alloc_flags().end())\r\n+    {\r\n+        SetLastError(ERROR_INVALID_HANDLE);\r\n+        rv = nullptr;\r\n+    }\r\n+    else if (it->second == UINT_MAX)\r\n+    {\r\n+        /*\r\n+         * MyIsBadReadPtr performs SetLastError, don't touch it here.\r\n+         */\r\n+        if (MyIsBadReadPtr(hMem, 1) == TRUE)\r\n+        {\r\n+            rv = nullptr;\r\n+        }\r\n+    }\r\n+    else if ((it->second & GMEM_DISCARDED) == GMEM_DISCARDED)\r\n+    {\r\n+        SetLastError(ERROR_DISCARDED);\r\n+        rv = nullptr;\r\n+    }\r\n+    else if ((it->second & GMEM_LOCKCOUNT) < GMEM_LOCKCOUNT)\r\n+    {\r\n+        it->second++;\r\n+    }\r\n+    return hMem;\r\n+}\r\n+\r\n+HOOKFUNC HGLOBAL WINAPI MyGlobalReAlloc(HGLOBAL hMem, SIZE_T dwBytes, UINT uFlags)\r\n+{\r\n+    debugprintf(__FUNCTION__ \"(0x%p 0x%X 0x%X) called.\\n\", hMem, dwBytes, uFlags);\r\n+    UINT alloc_flags = 0;\r\n+    HGLOBAL rv = nullptr;\r\n+    auto it = g_alloc_flags().find(hMem);\r\n+\r\n+    if ((uFlags & GMEM_ZEROINIT) == GMEM_ZEROINIT)\r\n+    {\r\n+        alloc_flags |= MemoryManager::ALLOC_ZEROINIT;\r\n+    }\r\n+\r\n+    if (it == g_alloc_flags().end())\r\n+    {\r\n+        SetLastError(ERROR_NOACCESS);\r\n+    }\r\n+    else if ((uFlags & GMEM_MODIFY) == GMEM_MODIFY)\r\n+    {\r\n+        if (it->second == UINT_MAX && (uFlags & GMEM_MOVEABLE) == GMEM_MOVEABLE)\r\n+        {\r\n+            /*\r\n+             * Not really allowed on GMEM_FIXED memory, but WINE does it anyway and have not\r\n+             * experienced any problems yet.\r\n+             * Might even help some old game that did something illegal that happened to work.\r\n+             */\r\n+            it->second = 0;\r\n+            rv = hMem;\r\n+        }\r\n+        else if (it->second == UINT_MAX && (uFlags & GMEM_DISCARDABLE) == GMEM_DISCARDABLE)\r\n+        {\r\n+            it->second |= GMEM_DISCARDABLE;\r\n+            rv = hMem;\r\n+        }\r\n+        else\r\n+        {\r\n+            SetLastError(ERROR_INVALID_PARAMETER);\r\n+        }\r\n+    }\r\n+    else\r\n+    {\r\n+        if (it->second == UINT_MAX)\r\n+        {\r\n+            alloc_flags |= MemoryManager::REALLOC_NO_MOVE;\r\n+        }\r\n+        if (dwBytes != 0)\r\n+        {\r\n+            rv = MemoryManager::Reallocate(hMem, dwBytes, alloc_flags);\r\n+            if (rv == nullptr)\r\n+            {\r\n+                SetLastError(ERROR_OUTOFMEMORY);\r\n+            }\r\n+            else\r\n+            {\r\n+                /*\r\n+                 * Transfer flags to new block, unconditionally unset GMEM_DISCARDED.\r\n+                 * WINE does not implement use of the GMEM_DISCARDED flag.\r\n+                 *\r\n+                 * TODO: Does the returned HGLOBAL need to match the passed HGLOBAL when discarded\r\n+                 *       memory is reallocated? Docs are not clear on this.\r\n+                 * -- Warepire\r\n+                 */\r\n+                UINT temp_flags = it->second;\r\n+                if ((uFlags & GMEM_DISCARDABLE) == GMEM_DISCARDABLE)\r\n+                {\r\n+                    temp_flags |= GMEM_DISCARDABLE;\r\n+                }\r\n+                else\r\n+                {\r\n+                    temp_flags &= ~GMEM_DISCARDABLE;\r\n+                }\r\n+                temp_flags &= ~GMEM_DISCARDED;\r\n+                g_alloc_flags().erase(it);\r\n+                g_alloc_flags()[rv] = temp_flags;\r\n+            }\r\n+        }\r\n+        else if ((it->second & GMEM_LOCKCOUNT) == 0 &&\r\n+                 (it->second & GMEM_DISCARDABLE) == GMEM_DISCARDABLE)\r\n+        {\r\n+            it->second |= GMEM_DISCARDED;\r\n+            rv = hMem;\r\n+        }\r\n+        else\r\n+        {\r\n+            MemoryManager::Deallocate(hMem);\r\n+        }\r\n+    }\r\n+    return rv;\r\n+}\r\n+\r\n+HOOKFUNC SIZE_T WINAPI MyGlobalSize(HGLOBAL hMem)\r\n+{\r\n+    debugprintf(__FUNCTION__ \"(0x%p) called.\\n\", hMem);\r\n+    SIZE_T rv = 0;\r\n+    auto it = g_alloc_flags().find(hMem);\r\n+    if (it == g_alloc_flags().end())\r\n+    {\r\n+        SetLastError(ERROR_INVALID_HANDLE);\r\n+    }\r\n+    else if ((it->second & GMEM_DISCARDED) == GMEM_DISCARDED)\r\n+    {\r\n+        /*\r\n+         * Is this accurate? WINE seems to do this.\r\n+         */\r\n+        SetLastError(ERROR_INVALID_HANDLE);\r\n+    }\r\n+    else\r\n+    {\r\n+        rv = MemoryManager::GetSizeOfAllocation(hMem);\r\n+    }\r\n+    return rv;\r\n+}\r\n+\r\n+HOOKFUNC void WINAPI MyGlobalUnfix(HGLOBAL hMem)\r\n+{\r\n+    debugprintf(__FUNCTION__ \"(0x%p) called.\\n\", hMem);\r\n+    MyGlobalUnlock(hMem);\r\n+}\r\n+\r\n+HOOKFUNC BOOL WINAPI MyGlobalUnlock(HGLOBAL hMem)\r\n+{\r\n+    debugprintf(__FUNCTION__ \"(0x%p) called.\\n\", hMem);\r\n+    BOOL rv = TRUE;\r\n+    auto it = g_alloc_flags().find(hMem);\r\n+    if (it == g_alloc_flags().end())\r\n+    {\r\n+        SetLastError(ERROR_INVALID_HANDLE);\r\n+        rv = FALSE;\r\n+    }\r\n+    else if ((it->second & GMEM_LOCKCOUNT) == 0)\r\n+    {\r\n+        SetLastError(ERROR_NOT_LOCKED);\r\n+        rv = FALSE;\r\n+    }\r\n+    else if (it->second != UINT_MAX)\r\n+    {\r\n+        it->second--;\r\n+    }\r\n+    return rv;\r\n+}\r\n+\r\n+HOOKFUNC BOOL WINAPI MyGlobalUnWire(HGLOBAL hMem)\r\n+{\r\n+    debugprintf(__FUNCTION__ \"(0x%p) called.\\n\", hMem);\r\n+    return MyGlobalUnlock(hMem);\r\n+}\r\n+\r\n+HOOKFUNC LPVOID WINAPI MyGlobalWire(HGLOBAL hMem)\r\n+{\r\n+    debugprintf(__FUNCTION__ \"(0x%p) called.\\n\", hMem);\r\n+    return MyGlobalLock(hMem);\r\n+}\r\n+\r\n+HOOKFUNC LPVOID WINAPI MyHeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes)\r\n+{\r\n+    debugprintf(__FUNCTION__ \"(0x%X 0x%X) called: 0x%p\\n\", dwFlags, dwBytes, hHeap);\r\n+    LPVOID rv = nullptr;\r\n+    UINT alloc_flags = 0;\r\n+    for (auto& h : g_heaps())\r\n+    {\r\n+        if (h == hHeap)\r\n+        {\r\n+            if (dwBytes == 0 &&\r\n+                ((h->heap_flags & HEAP_GENERATE_EXCEPTIONS) == HEAP_GENERATE_EXCEPTIONS ||\r\n+                 (dwFlags & HEAP_GENERATE_EXCEPTIONS) == HEAP_GENERATE_EXCEPTIONS))\r\n+            {\r\n+                RaiseException(STATUS_ACCESS_VIOLATION, 0, 0, nullptr);\r\n+            }\r\n+            if (dwBytes + h->heap_current_size > h->heap_maximum_size &&\r\n+                h->heap_maximum_size != 0 &&\r\n+                ((h->heap_flags & HEAP_GENERATE_EXCEPTIONS) == HEAP_GENERATE_EXCEPTIONS ||\r\n+                 (dwFlags & HEAP_GENERATE_EXCEPTIONS) == HEAP_GENERATE_EXCEPTIONS))\r\n+            {\r\n+                RaiseException(STATUS_NO_MEMORY, 0, 0, nullptr);\r\n+            }\r\n+            if ((h->heap_flags & HEAP_CREATE_ENABLE_EXECUTE) == HEAP_CREATE_ENABLE_EXECUTE)\r\n+            {\r\n+                alloc_flags |= MemoryManager::ALLOC_EXECUTE;\r\n+            }\r\n+            if ((dwFlags & HEAP_ZERO_MEMORY) == HEAP_ZERO_MEMORY)\r\n+            {\r\n+                alloc_flags |= MemoryManager::ALLOC_ZEROINIT;\r\n+            }\r\n+            if ((h->heap_flags & HEAP_NO_SERIALIZE) != HEAP_NO_SERIALIZE ||\r\n+                (dwFlags & HEAP_NO_SERIALIZE) != HEAP_NO_SERIALIZE)\r\n+            {\r\n+                MyHeapLock(hHeap);\r\n+            }\r\n+            rv = MemoryManager::Allocate(dwBytes, alloc_flags);\r\n+            if (rv != nullptr)\r\n+            {\r\n+                h->heap_current_size += MemoryManager::GetSizeOfAllocation(rv);\r\n+                h->heap_segments.push_back(rv);\r\n+            }\r\n+            if ((h->heap_flags & HEAP_NO_SERIALIZE) != HEAP_NO_SERIALIZE ||\r\n+                (dwFlags & HEAP_NO_SERIALIZE) != HEAP_NO_SERIALIZE)\r\n+            {\r\n+                MyHeapUnlock(hHeap);\r\n+            }\r\n+            if (rv == nullptr &&\r\n+                ((h->heap_flags & HEAP_GENERATE_EXCEPTIONS) == HEAP_GENERATE_EXCEPTIONS ||\r\n+                 (dwFlags & HEAP_GENERATE_EXCEPTIONS) == HEAP_GENERATE_EXCEPTIONS))\r\n+            {\r\n+                RaiseException(STATUS_NO_MEMORY, 0, 0, nullptr);\r\n+            }\r\n+\r\n+        }\r\n+    }\r\n+    return rv;\r\n+}\r\n+\r\n+HOOKFUNC SIZE_T WINAPI MyHeapCompact(HANDLE hHeap, DWORD dwFlags)\r\n+{\r\n+    debugprintf(__FUNCTION__ \"(0x%X) called: 0x%p\\n\", dwFlags, hHeap);\r\n+    return 0;\r\n+}\r\n+\r\n+HOOKFUNC HANDLE WINAPI MyHeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize)\r\n+{\r\n+    debugprintf(__FUNCTION__ \"(0x%X 0x%X 0x%X) called.\\n\", flOptions, dwInitialSize, dwMaximumSize);\r\n+    HeapObject* heap =\r\n+        static_cast<HeapObject*>(MemoryManager::Allocate(sizeof(*heap),\r\n+                                                         MemoryManager::ALLOC_WRITE));\r\n+    if (heap == nullptr)\r\n+    {\r\n+        SetLastError(ERROR_OUTOFMEMORY);\r\n+    }\r\n+    else\r\n+    {\r\n+        heap = ::new HeapObject(flOptions, dwInitialSize, dwMaximumSize);\r\n+        g_heaps().push_back(heap);\r\n+        LPVOID alloc = MyHeapAlloc(heap, 0, dwInitialSize);\r\n+        if (alloc == nullptr)\r\n+        {\r\n+            SetLastError(ERROR_OUTOFMEMORY);\r\n+            MemoryManager::Deallocate(heap);\r\n+            g_heaps().pop_back();\r\n+            heap = nullptr;\r\n+        }\r\n+    }\r\n+    return heap;\r\n+}\r\n+\r\n+HOOKFUNC BOOL WINAPI MyHeapDestroy(HANDLE hHeap)\r\n+{\r\n+    debugprintf(__FUNCTION__ \"(0x%p) called.\\n\", hHeap);\r\n+    BOOL rv = TRUE;\r\n+    auto it = g_heaps().begin();\r\n+    if (hHeap == nullptr)\r\n+    {\r\n+        SetLastError(ERROR_INVALID_HANDLE);\r\n+    }\r\n+    else if (hHeap != g_default_heap)\r\n+    {\r\n+        if (g_heaps().front() == g_default_heap)\r\n+        {\r\n+            it++;\r\n+        }\r\n+        SetLastError(ERROR_INVALID_HANDLE);\r\n+        for (; it != g_heaps().end(); ++it)\r\n+        {\r\n+            HeapObject* h = *it;\r\n+            if (h == hHeap)\r\n+            {\r\n+                while (h->heap_segments.empty() == false)\r\n+                {\r\n+                    MemoryManager::Deallocate(h->heap_segments.back());\r\n+                    h->heap_segments.pop_back();\r\n+                }\r\n+                g_heaps().erase(it);\r\n+                MemoryManager::Deallocate(hHeap);\r\n+                rv = FALSE;\r\n+                SetLastError(ERROR_SUCCESS);\r\n+                break;\r\n+            }\r\n+        }\r\n+    }\r\n+    return rv;\r\n+}\r\n+\r\n+HOOKFUNC BOOL WINAPI MyHeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem)\r\n+{\r\n+    debugprintf(__FUNCTION__ \"(0x%X 0x%p) called: 0x%p\\n\", dwFlags, lpMem, hHeap);\r\n+    BOOL rv = FALSE;\r\n+    if (lpMem == nullptr)\r\n+    {\r\n+        rv = TRUE;\r\n+    }\r\n+    else if (hHeap == nullptr)\r\n+    {\r\n+        SetLastError(ERROR_INVALID_HANDLE);\r\n+    }\r\n+    else\r\n+    {\r\n+        for (auto& h : g_heaps())\r\n+        {\r\n+            if (h == hHeap)\r\n+            {\r\n+                for (auto s = h->heap_segments.begin(); s != h->heap_segments.end(); ++s)\r\n+                {\r\n+                    if (*s == lpMem)\r\n+                    {\r\n+                        if ((h->heap_flags & HEAP_NO_SERIALIZE) != HEAP_NO_SERIALIZE ||\r\n+                            (dwFlags & HEAP_NO_SERIALIZE) != HEAP_NO_SERIALIZE)\r\n+                        {\r\n+                            MyHeapLock(hHeap);\r\n+                        }\r\n+                        h->heap_current_size -= MemoryManager::GetSizeOfAllocation(*s);\r\n+                        MemoryManager::Deallocate(*s);\r\n+                        h->heap_segments.erase(s);\r\n+                        if ((h->heap_flags & HEAP_NO_SERIALIZE) != HEAP_NO_SERIALIZE ||\r\n+                            (dwFlags & HEAP_NO_SERIALIZE) != HEAP_NO_SERIALIZE)\r\n+                        {\r\n+                            MyHeapUnlock(hHeap);\r\n+                        }\r\n+                        rv = TRUE;\r\n+                        goto heap_segment_freed;\r\n+                    }\r\n+                }\r\n+            }\r\n+        }\r\n+        SetLastError(ERROR_INVALID_PARAMETER);\r\n+    }\r\n+heap_segment_freed:\r\n+    return rv;\r\n+}\r\n+\r\n+HOOKFUNC BOOL WINAPI MyHeapLock(HANDLE hHeap)\r\n+{\r\n+    debugprintf(__FUNCTION__ \"(0x%p) called.\\n\", hHeap);\r\n+    for (auto& h : g_heaps())\r\n+    {\r\n+        if (h == hHeap)\r\n+        {\r\n+            while (h->heap_lock.test_and_set() == true);\r\n+            return TRUE;\r\n+        }\r\n+    }\r\n+    SetLastError(ERROR_INVALID_HANDLE);\r\n+    return FALSE;\r\n+}\r\n+\r\n+/*\r\n+ * Ripped directly out of WINE.\r\n+ */\r\n+HOOKFUNC BOOL WINAPI MyHeapQueryInformation(HANDLE HeapHandle,\r\n+                                            HEAP_INFORMATION_CLASS HeapInformationClass,\r\n+                                            PVOID HeapInformation,\r\n+                                            SIZE_T HeapInformationLength,\r\n+                                            PSIZE_T ReturnLength)\r\n+{\r\n+    debugprintf(__FUNCTION__ \" called: 0x%p\\n\", HeapHandle);\r","path":"wintasee/hooks/memoryhooks.cpp","position":636,"original_position":636,"commit_id":"5a13474287d4ecd5b2fab801f728039675d9263a","original_commit_id":"5a13474287d4ecd5b2fab801f728039675d9263a","user":{"login":"YaLTeR","id":1794388,"avatar_url":"https://avatars.githubusercontent.com/u/1794388?v=3","gravatar_id":"","url":"https://api.github.com/users/YaLTeR","html_url":"https://github.com/YaLTeR","followers_url":"https://api.github.com/users/YaLTeR/followers","following_url":"https://api.github.com/users/YaLTeR/following{/other_user}","gists_url":"https://api.github.com/users/YaLTeR/gists{/gist_id}","starred_url":"https://api.github.com/users/YaLTeR/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/YaLTeR/subscriptions","organizations_url":"https://api.github.com/users/YaLTeR/orgs","repos_url":"https://api.github.com/users/YaLTeR/repos","events_url":"https://api.github.com/users/YaLTeR/events{/privacy}","received_events_url":"https://api.github.com/users/YaLTeR/received_events","type":"User","site_admin":false},"body":"ENTER() can be used","created_at":"2016-01-31T11:48:31Z","updated_at":"2016-01-31T11:48:31Z","html_url":"https://github.com/Hourglass-Resurrection/Hourglass-Resurrection/pull/32#discussion_r51359316","pull_request_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/pulls/32","_links":{"self":{"href":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/pulls/comments/51359316"},"html":{"href":"https://github.com/Hourglass-Resurrection/Hourglass-Resurrection/pull/32#discussion_r51359316"},"pull_request":{"href":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/pulls/32"}}},"pull_request":{"url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/pulls/32","id":57765534,"html_url":"https://github.com/Hourglass-Resurrection/Hourglass-Resurrection/pull/32","diff_url":"https://github.com/Hourglass-Resurrection/Hourglass-Resurrection/pull/32.diff","patch_url":"https://github.com/Hourglass-Resurrection/Hourglass-Resurrection/pull/32.patch","issue_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/issues/32","number":32,"state":"open","locked":false,"title":"[WIP - NOT FOR MERGE] DLL & EXE: Memory manager","user":{"login":"Warepire","id":4831577,"avatar_url":"https://avatars.githubusercontent.com/u/4831577?v=3","gravatar_id":"","url":"https://api.github.com/users/Warepire","html_url":"https://github.com/Warepire","followers_url":"https://api.github.com/users/Warepire/followers","following_url":"https://api.github.com/users/Warepire/following{/other_user}","gists_url":"https://api.github.com/users/Warepire/gists{/gist_id}","starred_url":"https://api.github.com/users/Warepire/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Warepire/subscriptions","organizations_url":"https://api.github.com/users/Warepire/orgs","repos_url":"https://api.github.com/users/Warepire/repos","events_url":"https://api.github.com/users/Warepire/events{/privacy}","received_events_url":"https://api.github.com/users/Warepire/received_events","type":"User","site_admin":false},"body":"Due the size of this patch I am hereby submitting it for continuous review.\r\n\r\nThe goal of this when it is complete is that the Hourglass executable will have write permission by default to all memory that the game process allocates, and thus (hopefully) eliminate the BSoDs that has been seen. This will at least fix some sneaky memory leaks and whatnot.\r\n\r\nAnother long term goal is to enable save-states to become cross-session, and to implement a much better RAM Search.\r\n\r\nThis probably needs a bigger write-up later. Please refer to the comments in the code.","created_at":"2016-01-31T10:44:43Z","updated_at":"2016-01-31T11:48:31Z","closed_at":null,"merged_at":null,"merge_commit_sha":"9ba2896dcc6098dfde54af53e5f29d7d939bcad7","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/pulls/32/commits","review_comments_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/pulls/32/comments","review_comment_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/pulls/comments{/number}","comments_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/issues/32/comments","statuses_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/statuses/5a13474287d4ecd5b2fab801f728039675d9263a","head":{"label":"Warepire:memory_manager","ref":"memory_manager","sha":"5a13474287d4ecd5b2fab801f728039675d9263a","user":{"login":"Warepire","id":4831577,"avatar_url":"https://avatars.githubusercontent.com/u/4831577?v=3","gravatar_id":"","url":"https://api.github.com/users/Warepire","html_url":"https://github.com/Warepire","followers_url":"https://api.github.com/users/Warepire/followers","following_url":"https://api.github.com/users/Warepire/following{/other_user}","gists_url":"https://api.github.com/users/Warepire/gists{/gist_id}","starred_url":"https://api.github.com/users/Warepire/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Warepire/subscriptions","organizations_url":"https://api.github.com/users/Warepire/orgs","repos_url":"https://api.github.com/users/Warepire/repos","events_url":"https://api.github.com/users/Warepire/events{/privacy}","received_events_url":"https://api.github.com/users/Warepire/received_events","type":"User","site_admin":false},"repo":{"id":29933495,"name":"Hourglass-Resurrection","full_name":"Warepire/Hourglass-Resurrection","owner":{"login":"Warepire","id":4831577,"avatar_url":"https://avatars.githubusercontent.com/u/4831577?v=3","gravatar_id":"","url":"https://api.github.com/users/Warepire","html_url":"https://github.com/Warepire","followers_url":"https://api.github.com/users/Warepire/followers","following_url":"https://api.github.com/users/Warepire/following{/other_user}","gists_url":"https://api.github.com/users/Warepire/gists{/gist_id}","starred_url":"https://api.github.com/users/Warepire/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Warepire/subscriptions","organizations_url":"https://api.github.com/users/Warepire/orgs","repos_url":"https://api.github.com/users/Warepire/repos","events_url":"https://api.github.com/users/Warepire/events{/privacy}","received_events_url":"https://api.github.com/users/Warepire/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/Warepire/Hourglass-Resurrection","description":"Main repository for Hourglass-Resurrection, containing the source code for the executable and the DLL.","fork":true,"url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection","forks_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/forks","keys_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/teams","hooks_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/hooks","issue_events_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/issues/events{/number}","events_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/events","assignees_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/assignees{/user}","branches_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/branches{/branch}","tags_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/tags","blobs_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/git/refs{/sha}","trees_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/statuses/{sha}","languages_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/languages","stargazers_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/stargazers","contributors_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/contributors","subscribers_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/subscribers","subscription_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/subscription","commits_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/commits{/sha}","git_commits_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/git/commits{/sha}","comments_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/comments{/number}","issue_comment_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/issues/comments{/number}","contents_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/contents/{+path}","compare_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/merges","archive_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/downloads","issues_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/issues{/number}","pulls_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/pulls{/number}","milestones_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/milestones{/number}","notifications_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/labels{/name}","releases_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/releases{/id}","deployments_url":"https://api.github.com/repos/Warepire/Hourglass-Resurrection/deployments","created_at":"2015-01-27T20:29:48Z","updated_at":"2016-01-30T20:31:17Z","pushed_at":"2016-01-31T06:12:59Z","git_url":"git://github.com/Warepire/Hourglass-Resurrection.git","ssh_url":"git@github.com:Warepire/Hourglass-Resurrection.git","clone_url":"https://github.com/Warepire/Hourglass-Resurrection.git","svn_url":"https://github.com/Warepire/Hourglass-Resurrection","homepage":"","size":2314,"stargazers_count":1,"watchers_count":1,"language":"C++","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":1,"forks":1,"open_issues":1,"watchers":1,"default_branch":"master"}},"base":{"label":"Hourglass-Resurrection:master","ref":"master","sha":"87fe8f1530ad17cd60a231acefff16f3891eb9c1","user":{"login":"Hourglass-Resurrection","id":10700473,"avatar_url":"https://avatars.githubusercontent.com/u/10700473?v=3","gravatar_id":"","url":"https://api.github.com/users/Hourglass-Resurrection","html_url":"https://github.com/Hourglass-Resurrection","followers_url":"https://api.github.com/users/Hourglass-Resurrection/followers","following_url":"https://api.github.com/users/Hourglass-Resurrection/following{/other_user}","gists_url":"https://api.github.com/users/Hourglass-Resurrection/gists{/gist_id}","starred_url":"https://api.github.com/users/Hourglass-Resurrection/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Hourglass-Resurrection/subscriptions","organizations_url":"https://api.github.com/users/Hourglass-Resurrection/orgs","repos_url":"https://api.github.com/users/Hourglass-Resurrection/repos","events_url":"https://api.github.com/users/Hourglass-Resurrection/events{/privacy}","received_events_url":"https://api.github.com/users/Hourglass-Resurrection/received_events","type":"Organization","site_admin":false},"repo":{"id":29834002,"name":"Hourglass-Resurrection","full_name":"Hourglass-Resurrection/Hourglass-Resurrection","owner":{"login":"Hourglass-Resurrection","id":10700473,"avatar_url":"https://avatars.githubusercontent.com/u/10700473?v=3","gravatar_id":"","url":"https://api.github.com/users/Hourglass-Resurrection","html_url":"https://github.com/Hourglass-Resurrection","followers_url":"https://api.github.com/users/Hourglass-Resurrection/followers","following_url":"https://api.github.com/users/Hourglass-Resurrection/following{/other_user}","gists_url":"https://api.github.com/users/Hourglass-Resurrection/gists{/gist_id}","starred_url":"https://api.github.com/users/Hourglass-Resurrection/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Hourglass-Resurrection/subscriptions","organizations_url":"https://api.github.com/users/Hourglass-Resurrection/orgs","repos_url":"https://api.github.com/users/Hourglass-Resurrection/repos","events_url":"https://api.github.com/users/Hourglass-Resurrection/events{/privacy}","received_events_url":"https://api.github.com/users/Hourglass-Resurrection/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/Hourglass-Resurrection/Hourglass-Resurrection","description":"Main repository for Hourglass-Resurrection, containing the source code for the executable and the DLL.","fork":false,"url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection","forks_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/forks","keys_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/teams","hooks_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/hooks","issue_events_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/issues/events{/number}","events_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/events","assignees_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/assignees{/user}","branches_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/branches{/branch}","tags_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/tags","blobs_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/git/refs{/sha}","trees_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/statuses/{sha}","languages_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/languages","stargazers_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/stargazers","contributors_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/contributors","subscribers_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/subscribers","subscription_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/subscription","commits_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/commits{/sha}","git_commits_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/git/commits{/sha}","comments_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/comments{/number}","issue_comment_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/issues/comments{/number}","contents_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/contents/{+path}","compare_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/merges","archive_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/downloads","issues_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/issues{/number}","pulls_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/pulls{/number}","milestones_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/milestones{/number}","notifications_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/labels{/name}","releases_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/releases{/id}","deployments_url":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/deployments","created_at":"2015-01-25T22:22:22Z","updated_at":"2016-01-30T00:53:41Z","pushed_at":"2016-01-31T10:44:43Z","git_url":"git://github.com/Hourglass-Resurrection/Hourglass-Resurrection.git","ssh_url":"git@github.com:Hourglass-Resurrection/Hourglass-Resurrection.git","clone_url":"https://github.com/Hourglass-Resurrection/Hourglass-Resurrection.git","svn_url":"https://github.com/Hourglass-Resurrection/Hourglass-Resurrection","homepage":"","size":2243,"stargazers_count":15,"watchers_count":15,"language":"C++","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":17,"mirror_url":null,"open_issues_count":17,"forks":17,"open_issues":17,"watchers":15,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/pulls/32"},"html":{"href":"https://github.com/Hourglass-Resurrection/Hourglass-Resurrection/pull/32"},"issue":{"href":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/issues/32"},"comments":{"href":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/issues/32/comments"},"review_comments":{"href":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/pulls/32/comments"},"review_comment":{"href":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/pulls/32/commits"},"statuses":{"href":"https://api.github.com/repos/Hourglass-Resurrection/Hourglass-Resurrection/statuses/5a13474287d4ecd5b2fab801f728039675d9263a"}}}}