Merge pull request #3686 from x64dbg/release-notes
Release notes dialog
This commit is contained in:
commit
d6294a8370
|
@ -15,7 +15,7 @@
|
||||||
<value>style=allman, convert-tabs, align-pointer=type, align-reference=middle, indent=spaces, indent-namespaces, indent-col1-comments, pad-oper, unpad-paren, keep-one-line-blocks, close-templates</value>
|
<value>style=allman, convert-tabs, align-pointer=type, align-reference=middle, indent=spaces, indent-namespaces, indent-col1-comments, pad-oper, unpad-paren, keep-one-line-blocks, close-templates</value>
|
||||||
</setting>
|
</setting>
|
||||||
<setting name="Ignore" serializeAs="String">
|
<setting name="Ignore" serializeAs="String">
|
||||||
<value>src/cross/vendor</value>
|
<value>src/cross/vendor;src/gui/Src/ThirdPartyLibs/md4c</value>
|
||||||
</setting>
|
</setting>
|
||||||
<setting name="License" serializeAs="String">
|
<setting name="License" serializeAs="String">
|
||||||
<value />
|
<value />
|
||||||
|
|
|
@ -30,7 +30,7 @@ jobs:
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_UNITY_BUILD=ON -DCMAKE_UNITY_BUILD_BATCH_SIZE=6
|
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_UNITY_BUILD=ON -DCMAKE_UNITY_BUILD_BATCH_SIZE=6 -DX64DBG_RELEASE=${{ startsWith(github.ref, 'refs/tags/') && 'ON' || 'OFF' }}
|
||||||
cmake --build build
|
cmake --build build
|
||||||
|
|
||||||
- name: Upload Artifacts
|
- name: Upload Artifacts
|
||||||
|
@ -42,8 +42,31 @@ jobs:
|
||||||
include-hidden-files: true
|
include-hidden-files: true
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
|
|
||||||
|
docs:
|
||||||
|
# Skip building pull requests from the same repository
|
||||||
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository }}
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 1
|
||||||
|
|
||||||
|
- name: Build Documentation
|
||||||
|
run: |
|
||||||
|
docs\makechm.bat
|
||||||
|
|
||||||
|
- name: Upload Documentation
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: docs
|
||||||
|
path: docs/x64dbg.chm
|
||||||
|
if-no-files-found: error
|
||||||
|
include-hidden-files: true
|
||||||
|
retention-days: 1
|
||||||
|
|
||||||
package:
|
package:
|
||||||
needs: cmake
|
needs: [cmake, docs]
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
|
@ -63,7 +86,13 @@ jobs:
|
||||||
name: build-x86
|
name: build-x86
|
||||||
path: bin
|
path: bin
|
||||||
|
|
||||||
- name: Prepare release
|
- name: Download Documentation
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: docs
|
||||||
|
path: docs
|
||||||
|
|
||||||
|
- name: Prepare Release
|
||||||
run: |
|
run: |
|
||||||
curl.exe -L https://github.com/x64dbg/translations/releases/download/translations/qm.zip -o bin\qm.zip
|
curl.exe -L https://github.com/x64dbg/translations/releases/download/translations/qm.zip -o bin\qm.zip
|
||||||
7z x bin\qm.zip -obin
|
7z x bin\qm.zip -obin
|
||||||
|
@ -71,7 +100,7 @@ jobs:
|
||||||
$timestamp = Get-Date (Get-Date).ToUniversalTime() -Format "yyyy-MM-dd_HH-mm"
|
$timestamp = Get-Date (Get-Date).ToUniversalTime() -Format "yyyy-MM-dd_HH-mm"
|
||||||
echo "timestamp=$timestamp" >> $env:GITHUB_ENV
|
echo "timestamp=$timestamp" >> $env:GITHUB_ENV
|
||||||
|
|
||||||
- name: Upload Artifacts
|
- name: Upload Snapshot
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: snapshot_${{ env.timestamp }}
|
name: snapshot_${{ env.timestamp }}
|
||||||
|
@ -83,7 +112,7 @@ jobs:
|
||||||
include-hidden-files: true
|
include-hidden-files: true
|
||||||
compression-level: 9
|
compression-level: 9
|
||||||
|
|
||||||
- name: Upload Artifacts
|
- name: Upload Symbols
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: symbols-snapshot_${{ env.timestamp }}
|
name: symbols-snapshot_${{ env.timestamp }}
|
||||||
|
@ -93,3 +122,13 @@ jobs:
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
include-hidden-files: true
|
include-hidden-files: true
|
||||||
compression-level: 9
|
compression-level: 9
|
||||||
|
|
||||||
|
- name: Upload Plugin SDK
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: x64dbg-pluginsdk
|
||||||
|
path: |
|
||||||
|
release/pluginsdk
|
||||||
|
if-no-files-found: error
|
||||||
|
include-hidden-files: true
|
||||||
|
compression-level: 9
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
/bin/*.ini
|
/bin/*.ini
|
||||||
/bin/*.chm
|
/bin/*.chm
|
||||||
/bin/*.zip
|
/bin/*.zip
|
||||||
|
/bin/release-notes.md
|
||||||
|
!/bin/themes/
|
||||||
/src/**/x64/
|
/src/**/x64/
|
||||||
/src/**/Win32/
|
/src/**/Win32/
|
||||||
/src/gui_build/
|
/src/gui_build/
|
||||||
|
|
|
@ -33,6 +33,7 @@ endif()
|
||||||
|
|
||||||
# Options
|
# Options
|
||||||
option(X64DBG_BUILD_IN_TREE "" ON)
|
option(X64DBG_BUILD_IN_TREE "" ON)
|
||||||
|
option(X64DBG_RELEASE "" OFF)
|
||||||
|
|
||||||
# Variables
|
# Variables
|
||||||
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
|
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
|
||||||
|
@ -53,32 +54,25 @@ find_package(Qt5 REQUIRED
|
||||||
WinExtras
|
WinExtras
|
||||||
)
|
)
|
||||||
|
|
||||||
# Target: zydis_wrapper
|
# Subdirectory: src/zydis_wrapper
|
||||||
set(zydis_wrapper_SOURCES
|
set(CMKR_CMAKE_FOLDER ${CMAKE_FOLDER})
|
||||||
cmake.toml
|
if(CMAKE_FOLDER)
|
||||||
"src/zydis_wrapper/Zydis/Zydis.c"
|
set(CMAKE_FOLDER "${CMAKE_FOLDER}/src/zydis_wrapper")
|
||||||
"src/zydis_wrapper/Zydis/Zydis.h"
|
else()
|
||||||
"src/zydis_wrapper/zydis_wrapper.cpp"
|
set(CMAKE_FOLDER "src/zydis_wrapper")
|
||||||
"src/zydis_wrapper/zydis_wrapper.h"
|
endif()
|
||||||
)
|
add_subdirectory("src/zydis_wrapper")
|
||||||
|
set(CMAKE_FOLDER ${CMKR_CMAKE_FOLDER})
|
||||||
|
|
||||||
add_library(zydis_wrapper STATIC)
|
# Subdirectory: src/gui/Src/ThirdPartyLibs/md4c
|
||||||
|
set(CMKR_CMAKE_FOLDER ${CMAKE_FOLDER})
|
||||||
target_sources(zydis_wrapper PRIVATE ${zydis_wrapper_SOURCES})
|
if(CMAKE_FOLDER)
|
||||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${zydis_wrapper_SOURCES})
|
set(CMAKE_FOLDER "${CMAKE_FOLDER}/src/gui/Src/ThirdPartyLibs/md4c")
|
||||||
|
else()
|
||||||
target_compile_definitions(zydis_wrapper PUBLIC
|
set(CMAKE_FOLDER "src/gui/Src/ThirdPartyLibs/md4c")
|
||||||
ZYCORE_STATIC_BUILD
|
endif()
|
||||||
ZYDIS_STATIC_BUILD
|
add_subdirectory("src/gui/Src/ThirdPartyLibs/md4c")
|
||||||
)
|
set(CMAKE_FOLDER ${CMKR_CMAKE_FOLDER})
|
||||||
|
|
||||||
target_include_directories(zydis_wrapper PUBLIC
|
|
||||||
"src/zydis_wrapper"
|
|
||||||
)
|
|
||||||
|
|
||||||
target_include_directories(zydis_wrapper PRIVATE
|
|
||||||
"src/zydis_wrapper/Zydis"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Target: bridge
|
# Target: bridge
|
||||||
set(bridge_SOURCES
|
set(bridge_SOURCES
|
||||||
|
@ -629,6 +623,8 @@ set(gui_SOURCES
|
||||||
"src/gui/Src/Gui/HexLineEdit.cpp"
|
"src/gui/Src/Gui/HexLineEdit.cpp"
|
||||||
"src/gui/Src/Gui/HexLineEdit.h"
|
"src/gui/Src/Gui/HexLineEdit.h"
|
||||||
"src/gui/Src/Gui/HexLineEdit.ui"
|
"src/gui/Src/Gui/HexLineEdit.ui"
|
||||||
|
"src/gui/Src/Gui/ImageTextBrowser.cpp"
|
||||||
|
"src/gui/Src/Gui/ImageTextBrowser.h"
|
||||||
"src/gui/Src/Gui/LineEditDialog.cpp"
|
"src/gui/Src/Gui/LineEditDialog.cpp"
|
||||||
"src/gui/Src/Gui/LineEditDialog.h"
|
"src/gui/Src/Gui/LineEditDialog.h"
|
||||||
"src/gui/Src/Gui/LineEditDialog.ui"
|
"src/gui/Src/Gui/LineEditDialog.ui"
|
||||||
|
@ -665,6 +661,9 @@ set(gui_SOURCES
|
||||||
"src/gui/Src/Gui/ReferenceManager.h"
|
"src/gui/Src/Gui/ReferenceManager.h"
|
||||||
"src/gui/Src/Gui/RegistersView.cpp"
|
"src/gui/Src/Gui/RegistersView.cpp"
|
||||||
"src/gui/Src/Gui/RegistersView.h"
|
"src/gui/Src/Gui/RegistersView.h"
|
||||||
|
"src/gui/Src/Gui/ReleaseNotesDialog.cpp"
|
||||||
|
"src/gui/Src/Gui/ReleaseNotesDialog.h"
|
||||||
|
"src/gui/Src/Gui/ReleaseNotesDialog.ui"
|
||||||
"src/gui/Src/Gui/RichTextItemDelegate.cpp"
|
"src/gui/Src/Gui/RichTextItemDelegate.cpp"
|
||||||
"src/gui/Src/Gui/RichTextItemDelegate.h"
|
"src/gui/Src/Gui/RichTextItemDelegate.h"
|
||||||
"src/gui/Src/Gui/SEHChainView.cpp"
|
"src/gui/Src/Gui/SEHChainView.cpp"
|
||||||
|
@ -731,6 +730,9 @@ set(gui_SOURCES
|
||||||
"src/gui/Src/QHexEdit/XByteArray.cpp"
|
"src/gui/Src/QHexEdit/XByteArray.cpp"
|
||||||
"src/gui/Src/QHexEdit/XByteArray.h"
|
"src/gui/Src/QHexEdit/XByteArray.h"
|
||||||
"src/gui/Src/ThirdPartyLibs/ldconvert/ldconvert.h"
|
"src/gui/Src/ThirdPartyLibs/ldconvert/ldconvert.h"
|
||||||
|
"src/gui/Src/ThirdPartyLibs/md4c/md4c-entity.h"
|
||||||
|
"src/gui/Src/ThirdPartyLibs/md4c/md4c-html.h"
|
||||||
|
"src/gui/Src/ThirdPartyLibs/md4c/md4c.h"
|
||||||
"src/gui/Src/Tracer/TraceBrowser.cpp"
|
"src/gui/Src/Tracer/TraceBrowser.cpp"
|
||||||
"src/gui/Src/Tracer/TraceBrowser.h"
|
"src/gui/Src/Tracer/TraceBrowser.h"
|
||||||
"src/gui/Src/Tracer/TraceDump.cpp"
|
"src/gui/Src/Tracer/TraceDump.cpp"
|
||||||
|
@ -806,6 +808,12 @@ add_library(gui SHARED)
|
||||||
target_sources(gui PRIVATE ${gui_SOURCES})
|
target_sources(gui PRIVATE ${gui_SOURCES})
|
||||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${gui_SOURCES})
|
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${gui_SOURCES})
|
||||||
|
|
||||||
|
if(X64DBG_RELEASE) # X64DBG_RELEASE
|
||||||
|
target_compile_definitions(gui PUBLIC
|
||||||
|
X64DBG_RELEASE
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_compile_definitions(gui PRIVATE
|
target_compile_definitions(gui PRIVATE
|
||||||
BUILD_LIB
|
BUILD_LIB
|
||||||
NOMINMAX
|
NOMINMAX
|
||||||
|
@ -833,11 +841,16 @@ if(NOT TARGET bridge)
|
||||||
message(FATAL_ERROR "Target \"bridge\" referenced by \"gui\" does not exist!")
|
message(FATAL_ERROR "Target \"bridge\" referenced by \"gui\" does not exist!")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(NOT TARGET md4c-html)
|
||||||
|
message(FATAL_ERROR "Target \"md4c-html\" referenced by \"gui\" does not exist!")
|
||||||
|
endif()
|
||||||
|
|
||||||
target_link_libraries(gui PRIVATE
|
target_link_libraries(gui PRIVATE
|
||||||
Qt5::Widgets
|
Qt5::Widgets
|
||||||
Qt5::WinExtras
|
Qt5::WinExtras
|
||||||
zydis_wrapper
|
zydis_wrapper
|
||||||
bridge
|
bridge
|
||||||
|
md4c-html
|
||||||
winmm
|
winmm
|
||||||
wininet
|
wininet
|
||||||
)
|
)
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
23
cmake.toml
23
cmake.toml
|
@ -4,6 +4,7 @@ cmkr-include = "cmake/cmkr.cmake"
|
||||||
|
|
||||||
[options]
|
[options]
|
||||||
X64DBG_BUILD_IN_TREE = true
|
X64DBG_BUILD_IN_TREE = true
|
||||||
|
X64DBG_RELEASE = false
|
||||||
|
|
||||||
[variables]
|
[variables]
|
||||||
CMAKE_MODULE_PATH = "${CMAKE_SOURCE_DIR}/cmake"
|
CMAKE_MODULE_PATH = "${CMAKE_SOURCE_DIR}/cmake"
|
||||||
|
@ -26,24 +27,8 @@ x64 = "CMAKE_SIZEOF_VOID_P EQUAL 8"
|
||||||
[find-package]
|
[find-package]
|
||||||
Qt5 = { components = ["Widgets", "WinExtras"] }
|
Qt5 = { components = ["Widgets", "WinExtras"] }
|
||||||
|
|
||||||
[target.zydis_wrapper]
|
[subdir."src/zydis_wrapper"]
|
||||||
type = "static"
|
[subdir."src/gui/Src/ThirdPartyLibs/md4c"]
|
||||||
sources = [
|
|
||||||
"src/zydis_wrapper/*.cpp",
|
|
||||||
"src/zydis_wrapper/*.h",
|
|
||||||
"src/zydis_wrapper/Zydis/Zydis.h",
|
|
||||||
"src/zydis_wrapper/Zydis/Zydis.c",
|
|
||||||
]
|
|
||||||
include-directories = [
|
|
||||||
"src/zydis_wrapper",
|
|
||||||
]
|
|
||||||
private-include-directories = [
|
|
||||||
"src/zydis_wrapper/Zydis",
|
|
||||||
]
|
|
||||||
compile-definitions = [
|
|
||||||
"ZYCORE_STATIC_BUILD",
|
|
||||||
"ZYDIS_STATIC_BUILD",
|
|
||||||
]
|
|
||||||
|
|
||||||
[target.bridge]
|
[target.bridge]
|
||||||
type = "shared"
|
type = "shared"
|
||||||
|
@ -160,6 +145,7 @@ private-link-libraries = [
|
||||||
"Qt5::WinExtras",
|
"Qt5::WinExtras",
|
||||||
"::zydis_wrapper",
|
"::zydis_wrapper",
|
||||||
"::bridge",
|
"::bridge",
|
||||||
|
"::md4c-html",
|
||||||
"winmm",
|
"winmm",
|
||||||
"wininet",
|
"wininet",
|
||||||
]
|
]
|
||||||
|
@ -186,6 +172,7 @@ private-compile-definitions = [
|
||||||
"NOMINMAX",
|
"NOMINMAX",
|
||||||
"X64DBG",
|
"X64DBG",
|
||||||
]
|
]
|
||||||
|
X64DBG_RELEASE.compile-definitions = ["X64DBG_RELEASE"]
|
||||||
include-after = ["cmake/deps.cmake"]
|
include-after = ["cmake/deps.cmake"]
|
||||||
|
|
||||||
[target.gui.properties]
|
[target.gui.properties]
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
_build*/
|
_build*/
|
||||||
*.chm
|
*.chm
|
||||||
python-2.7.18.amd64.portable/
|
python-2.7.18.amd64.portable/
|
||||||
|
hha.dll
|
||||||
|
hhc.exe
|
||||||
|
itcc.dll
|
||||||
|
*.7z
|
|
@ -89,7 +89,7 @@ language = None
|
||||||
# List of patterns, relative to source directory, that match files and
|
# List of patterns, relative to source directory, that match files and
|
||||||
# directories to ignore when looking for source files.
|
# directories to ignore when looking for source files.
|
||||||
# This patterns also effect to html_static_path and html_extra_path
|
# This patterns also effect to html_static_path and html_extra_path
|
||||||
exclude_patterns = ['_build*', 'Thumbs.db', '.DS_Store', 'README.md', '.git', 'python-2.7.18.amd64.portable']
|
exclude_patterns = ['_build*', 'Thumbs.db', '.DS_Store', 'README.md', '.git', 'python-2.7.18.amd64.portable*']
|
||||||
|
|
||||||
# The reST default role (used for this markup: `text`) to use for all
|
# The reST default role (used for this markup: `text`) to use for all
|
||||||
# documents.
|
# documents.
|
||||||
|
|
|
@ -2,8 +2,14 @@
|
||||||
|
|
||||||
REM Command file for Sphinx documentation
|
REM Command file for Sphinx documentation
|
||||||
set PYTHONNOUSERSITE=1
|
set PYTHONNOUSERSITE=1
|
||||||
set PYTHONHOME=%CD%\python-2.7.18.amd64.portable
|
set PORTABLE_PYTHON=%~dp0python-2.7.18.amd64.portable
|
||||||
set PATH=%PYTHONHOME%;%PYTHONHOME%\Scripts;%PATH%
|
|
||||||
|
if not exist "%PORTABLE_PYTHON%" (
|
||||||
|
echo Portable Python not found!
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
set PATH=%PORTABLE_PYTHON%;%PORTABLE_PYTHON%\Scripts;%PATH%
|
||||||
set SPHINXBUILD=sphinx-build
|
set SPHINXBUILD=sphinx-build
|
||||||
|
|
||||||
if "%SPHINXBUILD%" == "" (
|
if "%SPHINXBUILD%" == "" (
|
||||||
|
|
|
@ -1,13 +1,32 @@
|
||||||
@echo off
|
@echo off
|
||||||
if "%VSVARSALLPATH%"=="" set VSVARSALLPATH=c:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat
|
|
||||||
echo Setting VS Path
|
cd /d "%~dp0"
|
||||||
call "%VSVARSALLPATH%"
|
|
||||||
|
set PORTABLE_PYTHON=%~dp0python-2.7.18.amd64.portable
|
||||||
|
if not exist "%PORTABLE_PYTHON%\python.exe" (
|
||||||
|
echo Downloading portable Python...
|
||||||
|
curl.exe -L -O https://github.com/x64dbg/docs/releases/download/python27-portable/python-2.7.18.amd64.portable.7z
|
||||||
|
7z x python-2.7.18.amd64.portable.7z -opython-2.7.18.amd64.portable
|
||||||
|
)
|
||||||
|
|
||||||
|
if not exist "%~dp0hhc.exe" (
|
||||||
|
echo Downloading HTML Help Workshop...
|
||||||
|
pause
|
||||||
|
curl.exe -L -O https://github.com/x64dbg/deps/releases/download/dependencies/hhc-4.74.8702.7z
|
||||||
|
7z x hhc-4.74.8702.7z
|
||||||
|
)
|
||||||
|
|
||||||
echo Building Help Project
|
echo Building Help Project
|
||||||
call make htmlhelp
|
call make htmlhelp
|
||||||
|
if %ERRORLEVEL% neq 0 exit /b %ERRORLEVEL%
|
||||||
echo Applying CHM hacks
|
echo Applying CHM hacks
|
||||||
copy theme.js .\_build\htmlhelp\_static\js\theme.js
|
copy theme.js .\_build\htmlhelp\_static\js\theme.js
|
||||||
type hacks.css >> .\_build\htmlhelp\_static\css\theme.css
|
type hacks.css >> .\_build\htmlhelp\_static\css\theme.css
|
||||||
echo Building CHM File
|
echo Building CHM File
|
||||||
hhc .\_build\htmlhelp\x64dbgdoc.hhp
|
hhc.exe .\_build\htmlhelp\x64dbgdoc.hhp
|
||||||
copy .\_build\htmlhelp\x64dbgdoc.chm x64dbg.chm
|
copy /Y .\_build\htmlhelp\x64dbgdoc.chm x64dbg.chm
|
||||||
echo Finished
|
if not exist "x64dbg.chm" (
|
||||||
|
echo Failed to build CHM file!
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
echo Finished
|
||||||
|
|
|
@ -143,11 +143,6 @@ endif()
|
||||||
# Target: release_notes
|
# Target: release_notes
|
||||||
set(release_notes_SOURCES
|
set(release_notes_SOURCES
|
||||||
cmake.toml
|
cmake.toml
|
||||||
"release_notes/ImageTextBrowser.cpp"
|
|
||||||
"release_notes/ImageTextBrowser.h"
|
|
||||||
"release_notes/ReleaseNotesDialog.cpp"
|
|
||||||
"release_notes/ReleaseNotesDialog.h"
|
|
||||||
"release_notes/ReleaseNotesDialog.ui"
|
|
||||||
"release_notes/main.cpp"
|
"release_notes/main.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -162,12 +157,8 @@ target_include_directories(release_notes PRIVATE
|
||||||
release_notes
|
release_notes
|
||||||
)
|
)
|
||||||
|
|
||||||
if(NOT TARGET md4c-html)
|
|
||||||
message(FATAL_ERROR "Target \"md4c-html\" referenced by \"release_notes\" does not exist!")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_link_libraries(release_notes PRIVATE
|
target_link_libraries(release_notes PRIVATE
|
||||||
md4c-html
|
x64dbg::widgets
|
||||||
)
|
)
|
||||||
|
|
||||||
get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT)
|
get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT)
|
||||||
|
|
|
@ -59,7 +59,7 @@ sources = [
|
||||||
]
|
]
|
||||||
include-directories = ["release_notes"]
|
include-directories = ["release_notes"]
|
||||||
link-libraries = [
|
link-libraries = [
|
||||||
"::md4c-html",
|
"x64dbg::widgets",
|
||||||
]
|
]
|
||||||
|
|
||||||
[target.hex_viewer]
|
[target.hex_viewer]
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
#include "ImageTextBrowser.h"
|
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QResizeEvent>
|
|
||||||
|
|
||||||
ImageTextBrowser::ImageTextBrowser(QWidget* parent)
|
|
||||||
: QTextBrowser(parent)
|
|
||||||
, mResizeTimer(new QTimer(this))
|
|
||||||
{
|
|
||||||
mResizeTimer->setSingleShot(true);
|
|
||||||
mResizeTimer->setInterval(300);
|
|
||||||
connect(mResizeTimer, &QTimer::timeout, this, [this]()
|
|
||||||
{
|
|
||||||
qDebug() << "timeout";
|
|
||||||
setText(toHtml());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant ImageTextBrowser::loadResource(int type, const QUrl & name)
|
|
||||||
{
|
|
||||||
auto url = name.toString();
|
|
||||||
if(url.startsWith("http"))
|
|
||||||
{
|
|
||||||
// TODO: download
|
|
||||||
}
|
|
||||||
else if(url.startsWith("data:"))
|
|
||||||
{
|
|
||||||
auto base64Index = url.indexOf(";base64,");
|
|
||||||
if(base64Index != -1)
|
|
||||||
{
|
|
||||||
auto data = QByteArray::fromBase64(url.mid(base64Index + 8).toUtf8());
|
|
||||||
auto image = QImage::fromData(data);
|
|
||||||
auto maxWidth = document()->textWidth() - document()->documentMargin() * 2 - 1;
|
|
||||||
if(image.width() > maxWidth)
|
|
||||||
{
|
|
||||||
image = image.scaledToWidth((int)maxWidth, Qt::SmoothTransformation);
|
|
||||||
}
|
|
||||||
return image;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return QTextBrowser::loadResource(type, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImageTextBrowser::resizeEvent(QResizeEvent* event)
|
|
||||||
{
|
|
||||||
if(event->size() != event->oldSize())
|
|
||||||
{
|
|
||||||
//qDebug() << "resizeEvent";
|
|
||||||
//mResizeTimer->start();
|
|
||||||
}
|
|
||||||
QTextBrowser::resizeEvent(event);
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QTextBrowser>
|
|
||||||
#include <QTimer>
|
|
||||||
|
|
||||||
class ImageTextBrowser : public QTextBrowser
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit ImageTextBrowser(QWidget* parent = nullptr);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
QVariant loadResource(int type, const QUrl & name) override;
|
|
||||||
void resizeEvent(QResizeEvent* event) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QTimer* mResizeTimer = nullptr;
|
|
||||||
};
|
|
|
@ -1,69 +0,0 @@
|
||||||
#include "ReleaseNotesDialog.h"
|
|
||||||
#include "ui_ReleaseNotesDialog.h"
|
|
||||||
|
|
||||||
#include <md4c-html.h>
|
|
||||||
|
|
||||||
static QString fixupHtmlEmojiBug(const QString & html)
|
|
||||||
{
|
|
||||||
// For some reason surrogates do not always display correctly in HTML elements.
|
|
||||||
// As a workaround we add a zero-width space in front of the high surrogate.
|
|
||||||
QString result;
|
|
||||||
int size = html.size();
|
|
||||||
result.reserve(size);
|
|
||||||
for(int i = 0; i < size; i++)
|
|
||||||
{
|
|
||||||
auto ch = html[i];
|
|
||||||
if(ch.isHighSurrogate() && i + 1 < size)
|
|
||||||
{
|
|
||||||
result += QChar(0x200B);
|
|
||||||
}
|
|
||||||
result += ch;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static QString markdownToHtml(const QByteArray & markdown)
|
|
||||||
{
|
|
||||||
auto appendString = [](const MD_CHAR * text, MD_SIZE size, void* userdata)
|
|
||||||
{
|
|
||||||
((std::string*)userdata)->append(text, size);
|
|
||||||
};
|
|
||||||
std::string html;
|
|
||||||
unsigned int parserFlags =
|
|
||||||
MD_FLAG_COLLAPSEWHITESPACE |
|
|
||||||
MD_FLAG_TABLES |
|
|
||||||
MD_FLAG_STRIKETHROUGH |
|
|
||||||
MD_FLAG_TASKLISTS |
|
|
||||||
MD_FLAG_PERMISSIVEAUTOLINKS |
|
|
||||||
MD_FLAG_LATEXMATHSPANS;
|
|
||||||
unsigned int rendererFlags = 0;
|
|
||||||
auto result = md_html(markdown.constData(), markdown.size(), appendString, &html, parserFlags, rendererFlags);
|
|
||||||
if(result != 0)
|
|
||||||
{
|
|
||||||
// TODO: throw an exception instead?
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return QString::fromStdString(html);
|
|
||||||
}
|
|
||||||
|
|
||||||
ReleaseNotesDialog::ReleaseNotesDialog(const QByteArray & markdown, QWidget* parent)
|
|
||||||
: QDialog(parent)
|
|
||||||
, ui(new Ui::ReleaseNotesDialog)
|
|
||||||
{
|
|
||||||
ui->setupUi(this);
|
|
||||||
|
|
||||||
auto font = QApplication::font();
|
|
||||||
font.setPointSize(16);
|
|
||||||
ui->textBrowser->document()->setDefaultFont(font);
|
|
||||||
|
|
||||||
ui->textBrowser->setOpenExternalLinks(true);
|
|
||||||
|
|
||||||
auto html = markdownToHtml(markdown);
|
|
||||||
html = fixupHtmlEmojiBug(html);
|
|
||||||
ui->textBrowser->setText(html);
|
|
||||||
}
|
|
||||||
|
|
||||||
ReleaseNotesDialog::~ReleaseNotesDialog()
|
|
||||||
{
|
|
||||||
delete ui;
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QDialog>
|
|
||||||
|
|
||||||
namespace Ui
|
|
||||||
{
|
|
||||||
class ReleaseNotesDialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
class ReleaseNotesDialog : public QDialog
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
ReleaseNotesDialog(const QByteArray & markdown, QWidget* parent = nullptr);
|
|
||||||
~ReleaseNotesDialog();
|
|
||||||
|
|
||||||
private:
|
|
||||||
Ui::ReleaseNotesDialog* ui;
|
|
||||||
};
|
|
|
@ -1,7 +1,7 @@
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
|
||||||
#include "ReleaseNotesDialog.h"
|
#include <Gui/ReleaseNotesDialog.h>
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
|
@ -20,6 +20,10 @@ int main(int argc, char* argv[])
|
||||||
auto markdown = f.readAll();
|
auto markdown = f.readAll();
|
||||||
|
|
||||||
QApplication a(argc, argv);
|
QApplication a(argc, argv);
|
||||||
ReleaseNotesDialog d(markdown);
|
ReleaseNotesDialog d({});
|
||||||
|
if(!d.setMarkdown(QString::fromUtf8(markdown), "https://github.com/x64dbg/x64dbg/issues/"))
|
||||||
|
{
|
||||||
|
puts("Failed to convert markdown!");
|
||||||
|
}
|
||||||
return d.exec();
|
return d.exec();
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,23 +66,3 @@ FetchContent_Declare(udmp-parser
|
||||||
src
|
src
|
||||||
)
|
)
|
||||||
FetchContent_MakeAvailable(udmp-parser)
|
FetchContent_MakeAvailable(udmp-parser)
|
||||||
|
|
||||||
# Target: md4c-html
|
|
||||||
set(md4c-html_SOURCES
|
|
||||||
cmake.toml
|
|
||||||
"md4c/md4c-entity.c"
|
|
||||||
"md4c/md4c-entity.h"
|
|
||||||
"md4c/md4c-html.c"
|
|
||||||
"md4c/md4c-html.h"
|
|
||||||
"md4c/md4c.c"
|
|
||||||
"md4c/md4c.h"
|
|
||||||
)
|
|
||||||
|
|
||||||
add_library(md4c-html STATIC)
|
|
||||||
|
|
||||||
target_sources(md4c-html PRIVATE ${md4c-html_SOURCES})
|
|
||||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${md4c-html_SOURCES})
|
|
||||||
|
|
||||||
target_include_directories(md4c-html PUBLIC
|
|
||||||
md4c
|
|
||||||
)
|
|
||||||
|
|
|
@ -26,9 +26,3 @@ tag = "3e98b047c52c07bb1816bd0936f561ce7797469d"
|
||||||
git = "https://github.com/mrexodia/udmp-parser"
|
git = "https://github.com/mrexodia/udmp-parser"
|
||||||
tag = "bc5952eda39ba0b1a07fc57f57d166292f1e9563"
|
tag = "bc5952eda39ba0b1a07fc57f57d166292f1e9563"
|
||||||
subdir = "src"
|
subdir = "src"
|
||||||
|
|
||||||
# https://github.com/mity/md4c/commit/481fbfbdf72daab2912380d62bb5f2187d438408
|
|
||||||
[target.md4c-html]
|
|
||||||
type = "static"
|
|
||||||
sources = ["md4c/*.c", "md4c/*.h"]
|
|
||||||
include-directories = ["md4c"]
|
|
||||||
|
|
|
@ -5,6 +5,11 @@ add_subdirectory(
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/zydis_wrapper
|
${CMAKE_CURRENT_BINARY_DIR}/zydis_wrapper
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_subdirectory(
|
||||||
|
${widgets_SOURCE_DIR}/ThirdPartyLibs/md4c
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/md4c
|
||||||
|
)
|
||||||
|
|
||||||
include(Qt.cmake)
|
include(Qt.cmake)
|
||||||
|
|
||||||
set(hook_SOURCES
|
set(hook_SOURCES
|
||||||
|
@ -58,6 +63,11 @@ set(widgets_SOURCES
|
||||||
${widgets_SOURCE_DIR}/Gui/TypeWidget.h
|
${widgets_SOURCE_DIR}/Gui/TypeWidget.h
|
||||||
${widgets_SOURCE_DIR}/Gui/RichTextItemDelegate.cpp
|
${widgets_SOURCE_DIR}/Gui/RichTextItemDelegate.cpp
|
||||||
${widgets_SOURCE_DIR}/Gui/RichTextItemDelegate.h
|
${widgets_SOURCE_DIR}/Gui/RichTextItemDelegate.h
|
||||||
|
${widgets_SOURCE_DIR}/Gui/ReleaseNotesDialog.cpp
|
||||||
|
${widgets_SOURCE_DIR}/Gui/ReleaseNotesDialog.h
|
||||||
|
${widgets_SOURCE_DIR}/Gui/ReleaseNotesDialog.ui
|
||||||
|
${widgets_SOURCE_DIR}/Gui/ImageTextBrowser.cpp
|
||||||
|
${widgets_SOURCE_DIR}/Gui/ImageTextBrowser.h
|
||||||
${widgets_SOURCE_DIR}/Memory/MemoryPage.cpp
|
${widgets_SOURCE_DIR}/Memory/MemoryPage.cpp
|
||||||
${widgets_SOURCE_DIR}/Memory/MemoryPage.h
|
${widgets_SOURCE_DIR}/Memory/MemoryPage.h
|
||||||
${widgets_SOURCE_DIR}/Utils/ActionHelpers.h
|
${widgets_SOURCE_DIR}/Utils/ActionHelpers.h
|
||||||
|
@ -98,6 +108,7 @@ target_compile_definitions(x64dbg_widgets PRIVATE
|
||||||
target_link_libraries(x64dbg_widgets PUBLIC
|
target_link_libraries(x64dbg_widgets PUBLIC
|
||||||
${QT_LIBRARIES}
|
${QT_LIBRARIES}
|
||||||
zydis_wrapper
|
zydis_wrapper
|
||||||
|
md4c-html
|
||||||
)
|
)
|
||||||
|
|
||||||
# https://doc.qt.io/qt-6/wasm.html#asyncify
|
# https://doc.qt.io/qt-6/wasm.html#asyncify
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
// TODO: do something cross platform
|
// TODO: do something cross platform
|
||||||
using duint = uint64_t;
|
using duint = uint64_t;
|
||||||
|
@ -18,7 +19,12 @@ int sprintf_s(char (&Dest)[Count], const char* fmt, Args... args)
|
||||||
|
|
||||||
inline size_t strcpy_s(char* dst, size_t size, const char* src)
|
inline size_t strcpy_s(char* dst, size_t size, const char* src)
|
||||||
{
|
{
|
||||||
return strlcpy(dst, src, size);
|
if(!dst || !src || !size)
|
||||||
|
return 0;
|
||||||
|
size_t i = 0;
|
||||||
|
while(i < size - 1 && src[i]) dst[i] = src[i], i++;
|
||||||
|
dst[i] = 0;
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
#include "ImageTextBrowser.h"
|
||||||
|
|
||||||
|
#include <QResizeEvent>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QScrollBar>
|
||||||
|
#include <QTextBlock>
|
||||||
|
|
||||||
|
ImageTextBrowser::ImageTextBrowser(QWidget* parent)
|
||||||
|
: QTextBrowser(parent)
|
||||||
|
, mResizeTimer(new QTimer(this))
|
||||||
|
{
|
||||||
|
mResizeTimer->setSingleShot(true);
|
||||||
|
mResizeTimer->setInterval(300);
|
||||||
|
connect(mResizeTimer, &QTimer::timeout, this, [this]()
|
||||||
|
{
|
||||||
|
setText(toHtml());
|
||||||
|
auto vbar = verticalScrollBar();
|
||||||
|
vbar->setValue(vbar->maximum() * mSavedScrollPercentage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageTextBrowser::resizeImages()
|
||||||
|
{
|
||||||
|
auto vbar = verticalScrollBar();
|
||||||
|
auto max = vbar->maximum();
|
||||||
|
mSavedScrollPercentage = (max > 0) ? (qreal)vbar->value() / max : 0.0;
|
||||||
|
mResizeTimer->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant ImageTextBrowser::loadResource(int type, const QUrl & name)
|
||||||
|
{
|
||||||
|
QImage image;
|
||||||
|
auto url = name.toString();
|
||||||
|
if(url.startsWith("http"))
|
||||||
|
{
|
||||||
|
auto itr = mImageCache.find(url);
|
||||||
|
if(itr != mImageCache.end())
|
||||||
|
{
|
||||||
|
image = itr.value();
|
||||||
|
}
|
||||||
|
else if(mDownloadFn)
|
||||||
|
{
|
||||||
|
image = mDownloadFn(url);
|
||||||
|
mImageCache.insert(url, image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(url.startsWith("data:"))
|
||||||
|
{
|
||||||
|
auto base64Index = url.indexOf(";base64,");
|
||||||
|
if(base64Index != -1)
|
||||||
|
{
|
||||||
|
auto data = QByteArray::fromBase64(url.mid(base64Index + 8).toUtf8());
|
||||||
|
image = QImage::fromData(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return QTextBrowser::loadResource(type, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scale the image to the width of the document
|
||||||
|
auto maxWidth = viewport()->width() - document()->documentMargin() * 2;
|
||||||
|
if(image.width() > maxWidth)
|
||||||
|
{
|
||||||
|
image = image.scaledToWidth((int)maxWidth, Qt::SmoothTransformation);
|
||||||
|
}
|
||||||
|
return image;
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QTextBrowser>
|
||||||
|
#include <QTextCursor>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QMap>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
class ImageTextBrowser : public QTextBrowser
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit ImageTextBrowser(QWidget* parent = nullptr);
|
||||||
|
void resizeImages();
|
||||||
|
|
||||||
|
using DownloadFn = std::function<QImage(const QString &)>;
|
||||||
|
|
||||||
|
void setDownloadFn(DownloadFn fn)
|
||||||
|
{
|
||||||
|
mDownloadFn = std::move(fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QVariant loadResource(int type, const QUrl & name) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
qreal mSavedScrollPercentage = 0.0;
|
||||||
|
QTimer* mResizeTimer = nullptr;
|
||||||
|
DownloadFn mDownloadFn;
|
||||||
|
QMap<QString, QImage> mImageCache;
|
||||||
|
};
|
|
@ -2,6 +2,7 @@
|
||||||
#include "ui_MainWindow.h"
|
#include "ui_MainWindow.h"
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <QToolButton>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
|
@ -53,6 +54,7 @@
|
||||||
#include "MRUList.h"
|
#include "MRUList.h"
|
||||||
#include "AboutDialog.h"
|
#include "AboutDialog.h"
|
||||||
#include "UpdateChecker.h"
|
#include "UpdateChecker.h"
|
||||||
|
#include "Gui/ReleaseNotesDialog.h"
|
||||||
#include "Tracer/TraceManager.h"
|
#include "Tracer/TraceManager.h"
|
||||||
//#include "Tracer/TraceWidget.h"
|
//#include "Tracer/TraceWidget.h"
|
||||||
#include "Utils/MethodInvoker.h"
|
#include "Utils/MethodInvoker.h"
|
||||||
|
@ -390,6 +392,11 @@ MainWindow::MainWindow(QWidget* parent)
|
||||||
setupThemesMenu();
|
setupThemesMenu();
|
||||||
setupMenuCustomization();
|
setupMenuCustomization();
|
||||||
ui->actionAbout_Qt->setIcon(QApplication::style()->standardIcon(QStyle::SP_TitleBarMenuButton));
|
ui->actionAbout_Qt->setIcon(QApplication::style()->standardIcon(QStyle::SP_TitleBarMenuButton));
|
||||||
|
auto toolButton = qobject_cast<QToolButton*>(ui->mainToolBar->widgetForAction(ui->actionCheckUpdates));
|
||||||
|
if(toolButton)
|
||||||
|
{
|
||||||
|
toolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||||
|
}
|
||||||
|
|
||||||
// Set default setttings (when not set)
|
// Set default setttings (when not set)
|
||||||
SettingsDialog defaultSettings;
|
SettingsDialog defaultSettings;
|
||||||
|
@ -1056,6 +1063,21 @@ void MainWindow::loadWindowSettings()
|
||||||
tr("You are running x64dbg on an unsupported operating system version. <b>Future updates will completely stop running on this system.</b><br><br>For more information, see the official <a href=\"%1\">announcement</a>.").arg("https://transition.x64dbg.com")
|
tr("You are running x64dbg on an unsupported operating system version. <b>Future updates will completely stop running on this system.</b><br><br>For more information, see the official <a href=\"%1\">announcement</a>.").arg("https://transition.x64dbg.com")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef X64DBG_RELEASE
|
||||||
|
auto compileDate = QDateTime(GetCompileDate());
|
||||||
|
compileDate.setTimeSpec(Qt::UTC);
|
||||||
|
auto compileEpoch = compileDate.toSecsSinceEpoch();
|
||||||
|
duint releaseNotesEpoch = 0;
|
||||||
|
BridgeSettingGetUint("Gui", "ReleaseNotesEpoch", &releaseNotesEpoch);
|
||||||
|
if(releaseNotesEpoch < compileEpoch)
|
||||||
|
{
|
||||||
|
showReleaseNotes(releaseNotesEpoch);
|
||||||
|
BridgeSettingSetUint("Gui", "ReleaseNotesPrevEpoch", releaseNotesEpoch);
|
||||||
|
BridgeSettingSetUint("Gui", "ReleaseNotesEpoch", compileEpoch);
|
||||||
|
BridgeSettingFlush();
|
||||||
|
}
|
||||||
|
#endif // X64DBG_RELEASE
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::setGlobalShortcut(QAction* action, const QKeySequence & key)
|
void MainWindow::setGlobalShortcut(QAction* action, const QKeySequence & key)
|
||||||
|
@ -1185,6 +1207,50 @@ QAction* MainWindow::makeCommandAction(QAction* action, const QString & command)
|
||||||
return action;
|
return action;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::showReleaseNotes(duint cutoffEpoch)
|
||||||
|
{
|
||||||
|
QFile file(QString("%1/../release-notes.md").arg(QCoreApplication::applicationDirPath()));
|
||||||
|
if(!file.open(QFile::ReadOnly))
|
||||||
|
{
|
||||||
|
SimpleErrorBox(
|
||||||
|
this,
|
||||||
|
tr("Error"),
|
||||||
|
tr("Release notes are not available, see <a href=\"%1\">%2</a> for the latest updates.")
|
||||||
|
.arg("https://update.x64dbg.com")
|
||||||
|
.arg("update.x64dbg.com")
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto markdown = QString::fromUtf8(file.readAll());
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
if(cutoffEpoch)
|
||||||
|
{
|
||||||
|
static QRegularExpression re(R"(<!-- *(\d\d\d\d.\d\d.\d\d) *-->)");
|
||||||
|
auto i = re.globalMatch(markdown);
|
||||||
|
QStringList words;
|
||||||
|
while(i.hasNext())
|
||||||
|
{
|
||||||
|
QRegularExpressionMatch match = i.next();
|
||||||
|
auto matchText = match.captured(1);
|
||||||
|
auto matchDate = QDateTime::fromString(matchText, "yyyy.MM.dd");
|
||||||
|
matchDate.setTimeSpec(Qt::UTC);
|
||||||
|
auto matchEpoch = matchDate.toSecsSinceEpoch();
|
||||||
|
if(matchEpoch <= cutoffEpoch)
|
||||||
|
{
|
||||||
|
markdown = markdown.left(match.capturedStart(0));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReleaseNotesDialog dialog({}, this);
|
||||||
|
dialog.move(frameGeometry().center() - dialog.rect().center());
|
||||||
|
dialog.setMarkdown(markdown, "https://github.com/x64dbg/x64dbg/issues/");
|
||||||
|
dialog.setWindowIcon(DIcon("bug"));
|
||||||
|
dialog.exec();
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::execCommandSlot()
|
void MainWindow::execCommandSlot()
|
||||||
{
|
{
|
||||||
QAction* action = qobject_cast<QAction*>(sender());
|
QAction* action = qobject_cast<QAction*>(sender());
|
||||||
|
@ -2765,6 +2831,13 @@ void MainWindow::on_actionAbout_Qt_triggered()
|
||||||
delete w;
|
delete w;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::on_actionReleaseNotes_triggered()
|
||||||
|
{
|
||||||
|
duint cutoffEpoch = 0;
|
||||||
|
BridgeSettingGetUint("Gui", "ReleaseNotesPrevEpoch", &cutoffEpoch);
|
||||||
|
showReleaseNotes(cutoffEpoch);
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::updateStyle()
|
void MainWindow::updateStyle()
|
||||||
{
|
{
|
||||||
// Set configured link color
|
// Set configured link color
|
||||||
|
|
|
@ -196,6 +196,7 @@ private:
|
||||||
void onMenuCustomized();
|
void onMenuCustomized();
|
||||||
void setupMenuCustomization();
|
void setupMenuCustomization();
|
||||||
QAction* makeCommandAction(QAction* action, const QString & command);
|
QAction* makeCommandAction(QAction* action, const QString & command);
|
||||||
|
void showReleaseNotes(duint cutoffEpoch);
|
||||||
|
|
||||||
//lists for menu customization
|
//lists for menu customization
|
||||||
QList<QAction*> mFileMenuStrings;
|
QList<QAction*> mFileMenuStrings;
|
||||||
|
@ -289,4 +290,5 @@ private slots:
|
||||||
void on_actionCheckUpdates_triggered();
|
void on_actionCheckUpdates_triggered();
|
||||||
void on_actionDefaultTheme_triggered();
|
void on_actionDefaultTheme_triggered();
|
||||||
void on_actionAbout_Qt_triggered();
|
void on_actionAbout_Qt_triggered();
|
||||||
|
void on_actionReleaseNotes_triggered();
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,6 +16,9 @@
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>x64dbg</string>
|
<string>x64dbg</string>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="toolButtonStyle">
|
||||||
|
<enum>Qt::ToolButtonIconOnly</enum>
|
||||||
|
</property>
|
||||||
<widget class="QWidget" name="centralWidget"/>
|
<widget class="QWidget" name="centralWidget"/>
|
||||||
<widget class="QMenuBar" name="menuBar">
|
<widget class="QMenuBar" name="menuBar">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
|
@ -160,6 +163,7 @@
|
||||||
<addaction name="actionManual"/>
|
<addaction name="actionManual"/>
|
||||||
<addaction name="actionFaq"/>
|
<addaction name="actionFaq"/>
|
||||||
<addaction name="actionAbout"/>
|
<addaction name="actionAbout"/>
|
||||||
|
<addaction name="actionReleaseNotes"/>
|
||||||
<addaction name="actionAbout_Qt"/>
|
<addaction name="actionAbout_Qt"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="actionCrashDump"/>
|
<addaction name="actionCrashDump"/>
|
||||||
|
@ -300,7 +304,7 @@
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="allowedAreas">
|
<property name="allowedAreas">
|
||||||
<set>Qt::BottomToolBarArea</set>
|
<set>Qt::NoToolBarArea</set>
|
||||||
</property>
|
</property>
|
||||||
<attribute name="toolBarArea">
|
<attribute name="toolBarArea">
|
||||||
<enum>BottomToolBarArea</enum>
|
<enum>BottomToolBarArea</enum>
|
||||||
|
@ -1549,6 +1553,15 @@
|
||||||
<string>Output the detailed help information about an assembly mnemonic to the log. Equivalent command "mnemonichelp name".</string>
|
<string>Output the detailed help information about an assembly mnemonic to the log. Equivalent command "mnemonichelp name".</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionReleaseNotes">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset theme="bug" resource="../../resource.qrc">
|
||||||
|
<normaloff>:/Default/icons/bug.png</normaloff>:/Default/icons/bug.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Release Notes</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<layoutdefault spacing="6" margin="11"/>
|
<layoutdefault spacing="6" margin="11"/>
|
||||||
<resources>
|
<resources>
|
||||||
|
|
|
@ -0,0 +1,267 @@
|
||||||
|
#include "ReleaseNotesDialog.h"
|
||||||
|
#include "ui_ReleaseNotesDialog.h"
|
||||||
|
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QDesktopServices>
|
||||||
|
#include <QResizeEvent>
|
||||||
|
#include <QScrollBar>
|
||||||
|
|
||||||
|
#include <md4c-html.h>
|
||||||
|
|
||||||
|
struct BadEmoji
|
||||||
|
{
|
||||||
|
QString original;
|
||||||
|
QString replacement;
|
||||||
|
|
||||||
|
BadEmoji(const char* emoji) // NOLINT(google-explicit-constructor)
|
||||||
|
: original(QString::fromUtf8(emoji))
|
||||||
|
{
|
||||||
|
replacement = QString("<span class=\"emoji\">%1</span>").arg(original);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static QString fixupHtmlEmojiBug(QString html)
|
||||||
|
{
|
||||||
|
// Certain emojis do not display correctly on Windows with the Segoe UI font.
|
||||||
|
// As a workaround we enclose them in a <span class="emoji">
|
||||||
|
#ifdef Q_OS_WINDOWS
|
||||||
|
static BadEmoji badEmojis[] =
|
||||||
|
{
|
||||||
|
"\xe2\x98\xba", // ☺
|
||||||
|
"\xe2\x98\xb9", // ☹
|
||||||
|
"\xe2\x98\xa0", // ☠
|
||||||
|
"\xe2\x9d\xa3", // ❣
|
||||||
|
"\xe2\x9d\xa4", // ❤
|
||||||
|
"\xf0\x9f\x97\xa8", // 🗨
|
||||||
|
"\xe2\x9c\x8c", // ✌
|
||||||
|
"\xe2\x98\x9d", // ☝
|
||||||
|
"\xe2\x9c\x8d", // ✍
|
||||||
|
"\xe2\x99\xa8", // ♨
|
||||||
|
"\xe2\x9c\x88", // ✈
|
||||||
|
"\xe2\x98\x80", // ☀
|
||||||
|
"\xe2\x98\x81", // ☁
|
||||||
|
"\xe2\x9d\x84", // ❄
|
||||||
|
"\xe2\x98\x84", // ☄
|
||||||
|
"\xe2\x99\xa0", // ♠
|
||||||
|
"\xe2\x99\xa5", // ♥
|
||||||
|
"\xe2\x99\xa6", // ♦
|
||||||
|
"\xe2\x99\xa3", // ♣
|
||||||
|
"\xe2\x99\x9f", // ♟
|
||||||
|
"\xe2\x9c\x8f", // ✏
|
||||||
|
"\xe2\x9c\x92", // ✒
|
||||||
|
"\xe2\x9c\x82", // ✂
|
||||||
|
"\xe2\x98\xa2", // ☢
|
||||||
|
"\xe2\x98\xa3", // ☣
|
||||||
|
"\xe2\x86\x97", // ↗
|
||||||
|
"\xe2\x9e\xa1", // ➡
|
||||||
|
"\xe2\x86\x98", // ↘
|
||||||
|
"\xe2\x86\x99", // ↙
|
||||||
|
"\xe2\x86\x96", // ↖
|
||||||
|
"\xe2\x86\x95", // ↕
|
||||||
|
"\xe2\x86\x94", // ↔
|
||||||
|
"\xe2\x86\xa9", // ↩
|
||||||
|
"\xe2\x86\xaa", // ↪
|
||||||
|
"\xe2\xa4\xb4", // ⤴
|
||||||
|
"\xe2\xa4\xb5", // ⤵
|
||||||
|
"\xe2\x9c\xa1", // ✡
|
||||||
|
"\xe2\x98\xb8", // ☸
|
||||||
|
"\xe2\x98\xaf", // ☯
|
||||||
|
"\xe2\x9c\x9d", // ✝
|
||||||
|
"\xe2\x98\xa6", // ☦
|
||||||
|
"\xe2\x98\xaa", // ☪
|
||||||
|
"\xe2\x98\xae", // ☮
|
||||||
|
"\xe2\x99\x88", // ♈
|
||||||
|
"\xe2\x99\x89", // ♉
|
||||||
|
"\xe2\x99\x8a", // ♊
|
||||||
|
"\xe2\x99\x8b", // ♋
|
||||||
|
"\xe2\x99\x8c", // ♌
|
||||||
|
"\xe2\x99\x8d", // ♍
|
||||||
|
"\xe2\x99\x8e", // ♎
|
||||||
|
"\xe2\x99\x8f", // ♏
|
||||||
|
"\xe2\x99\x90", // ♐
|
||||||
|
"\xe2\x99\x91", // ♑
|
||||||
|
"\xe2\x99\x92", // ♒
|
||||||
|
"\xe2\x99\x93", // ♓
|
||||||
|
"\xe2\x96\xb6", // ▶
|
||||||
|
"\xe2\x97\x80", // ◀
|
||||||
|
"\xe2\x99\x80", // ♀
|
||||||
|
"\xe2\x99\x82", // ♂
|
||||||
|
"\xe2\x9a\xa7", // ⚧
|
||||||
|
"\xe2\x9c\x96", // ✖
|
||||||
|
"\xe2\x80\xbc", // ‼
|
||||||
|
"\xe2\x81\x89", // ⁉
|
||||||
|
"\xe3\x80\xb0", // 〰
|
||||||
|
"\xe2\x98\x91", // ☑
|
||||||
|
"\xe2\x9c\x94", // ✔
|
||||||
|
"\xe3\x80\xbd", // 〽
|
||||||
|
"\xe2\x9c\xb3", // ✳
|
||||||
|
"\xe2\x9c\xb4", // ✴
|
||||||
|
"\xe2\x9d\x87", // ❇
|
||||||
|
"\xe2\x84\xa2", // ™
|
||||||
|
"\xe2\x93\x82", // Ⓜ
|
||||||
|
"\xf0\x9f\x88\x81", // 🈁
|
||||||
|
"\xf0\x9f\x88\x82", // 🈂
|
||||||
|
"\xf0\x9f\x88\xb7", // 🈷
|
||||||
|
"\xf0\x9f\x88\xb6", // 🈶
|
||||||
|
"\xf0\x9f\x88\xaf", // 🈯
|
||||||
|
"\xf0\x9f\x89\x90", // 🉐
|
||||||
|
"\xf0\x9f\x88\xb9", // 🈹
|
||||||
|
"\xf0\x9f\x88\x9a", // 🈚
|
||||||
|
"\xf0\x9f\x88\xb2", // 🈲
|
||||||
|
"\xf0\x9f\x89\x91", // 🉑
|
||||||
|
"\xf0\x9f\x88\xb8", // 🈸
|
||||||
|
"\xf0\x9f\x88\xb4", // 🈴
|
||||||
|
"\xf0\x9f\x88\xb3", // 🈳
|
||||||
|
"\xe3\x8a\x97", // ㊗
|
||||||
|
"\xe3\x8a\x99", // ㊙
|
||||||
|
"\xf0\x9f\x88\xba", // 🈺
|
||||||
|
"\xf0\x9f\x88\xb5", // 🈵
|
||||||
|
"\xe2\x97\xbc", // ◼
|
||||||
|
"\xe2\x98\x82", // ☂
|
||||||
|
"\xe2\x9c\x89", // ✉
|
||||||
|
"\xe2\x96\xab", // ▫
|
||||||
|
"\xe2\x96\xaa", // ▪
|
||||||
|
"\xe2\x97\xbd", // ◽
|
||||||
|
"\xe2\x97\xbe", // ◾
|
||||||
|
};
|
||||||
|
for(const auto & badEmoji : badEmojis)
|
||||||
|
{
|
||||||
|
html = html.replace(badEmoji.original, badEmoji.replacement);
|
||||||
|
}
|
||||||
|
#endif // Q_OS_WINDOWS
|
||||||
|
|
||||||
|
// For some reason surrogates do not always display correctly in HTML elements.
|
||||||
|
// As a workaround we add a zero-width space in front of the high surrogate.
|
||||||
|
QString result;
|
||||||
|
int size = html.size();
|
||||||
|
result.reserve(size);
|
||||||
|
for(int i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
auto ch = html.at(i);
|
||||||
|
if(ch.isHighSurrogate())
|
||||||
|
{
|
||||||
|
result += QChar(0x200B);
|
||||||
|
for(; i < size; i++)
|
||||||
|
{
|
||||||
|
ch = html.at(i);
|
||||||
|
result += ch;
|
||||||
|
if(ch.unicode() < 0x0080)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result += ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString markdownToHtml(const QString & markdown)
|
||||||
|
{
|
||||||
|
auto appendString = [](const MD_CHAR * text, MD_SIZE size, void* userdata)
|
||||||
|
{
|
||||||
|
((std::string*)userdata)->append(text, size);
|
||||||
|
};
|
||||||
|
std::string html = "<body>";
|
||||||
|
unsigned int parserFlags =
|
||||||
|
MD_FLAG_COLLAPSEWHITESPACE |
|
||||||
|
MD_FLAG_TABLES |
|
||||||
|
MD_FLAG_STRIKETHROUGH |
|
||||||
|
MD_FLAG_TASKLISTS |
|
||||||
|
MD_FLAG_PERMISSIVEAUTOLINKS |
|
||||||
|
MD_FLAG_LATEXMATHSPANS;
|
||||||
|
unsigned int rendererFlags = 0;
|
||||||
|
auto markdownUtf8 = markdown.toUtf8();
|
||||||
|
if(md_html(markdownUtf8.constData(), markdownUtf8.size(), appendString, &html, parserFlags, rendererFlags) != 0)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
html += "</body>";
|
||||||
|
return QString::fromStdString(html);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void markdownGithubLinks(QString & markdown, const QString & issueUrl)
|
||||||
|
{
|
||||||
|
static QRegularExpression usernameRegex(R"((?<=^|\s)@([a-zA-Z0-9-]{1,39})(?=\s|$))");
|
||||||
|
markdown.replace(usernameRegex, R"([@\1](https://github.com/\1))");
|
||||||
|
if(!issueUrl.isEmpty())
|
||||||
|
{
|
||||||
|
static QRegularExpression issueRegex(R"((?<=^|\s)#(\d+)(?=\s|$))");
|
||||||
|
markdown.replace(issueRegex, QString(R"([#\1](%1\1))").arg(issueUrl));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReleaseNotesDialog::ReleaseNotesDialog(ImageTextBrowser::DownloadFn downloadFn, QWidget* parent)
|
||||||
|
: QDialog(parent)
|
||||||
|
, ui(new Ui::ReleaseNotesDialog)
|
||||||
|
{
|
||||||
|
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||||
|
ui->setupUi(this);
|
||||||
|
ui->textBrowser->setDownloadFn(std::move(downloadFn));
|
||||||
|
|
||||||
|
#ifdef Q_OS_WINDOWS
|
||||||
|
QFont font("Segoe UI");
|
||||||
|
#else
|
||||||
|
QFont font = QApplication::font();
|
||||||
|
#endif // Q_OS_WINDOWS
|
||||||
|
font.setHintingPreference(QFont::PreferNoHinting);
|
||||||
|
font.setPixelSize(16);
|
||||||
|
font.setStyleHint(QFont::SansSerif);
|
||||||
|
ui->textBrowser->document()->setDefaultFont(font);
|
||||||
|
|
||||||
|
QPalette palette = ui->textBrowser->palette();
|
||||||
|
if(palette.color(QPalette::Text) == Qt::black)
|
||||||
|
{
|
||||||
|
palette.setColor(QPalette::Text, QColor("#1f2328"));
|
||||||
|
ui->textBrowser->setPalette(palette);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->textBrowser->document()->setDocumentMargin(14);
|
||||||
|
|
||||||
|
QString styleSheet = R"(
|
||||||
|
h1, h2, h3, h4, h5, h6, strong, b {
|
||||||
|
font-weight: 500;
|
||||||
|
})";
|
||||||
|
#ifdef Q_OS_WINDOWS
|
||||||
|
styleSheet += R"(
|
||||||
|
.emoji {
|
||||||
|
font-family: "Segoe UI Emoji";
|
||||||
|
})";
|
||||||
|
#endif // Q_OS_WINDOWS
|
||||||
|
|
||||||
|
ui->textBrowser->document()->setDefaultStyleSheet(styleSheet);
|
||||||
|
ui->textBrowser->setOpenLinks(false);
|
||||||
|
connect(ui->textBrowser, &QTextBrowser::anchorClicked, this, [this](const QUrl & url)
|
||||||
|
{
|
||||||
|
ui->textBrowser->clearFocus();
|
||||||
|
QDesktopServices::openUrl(url);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ReleaseNotesDialog::~ReleaseNotesDialog()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReleaseNotesDialog::setMarkdown(QString markdown, const QString & issueUrl)
|
||||||
|
{
|
||||||
|
markdownGithubLinks(markdown, issueUrl);
|
||||||
|
auto html = markdownToHtml(markdown);
|
||||||
|
html = fixupHtmlEmojiBug(html);
|
||||||
|
ui->textBrowser->setText(html);
|
||||||
|
return !html.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReleaseNotesDialog::setLabel(const QString & text)
|
||||||
|
{
|
||||||
|
ui->label->setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReleaseNotesDialog::resizeEvent(QResizeEvent* event)
|
||||||
|
{
|
||||||
|
QDialog::resizeEvent(event);
|
||||||
|
ui->textBrowser->resizeImages();
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include <functional>
|
||||||
|
#include "ImageTextBrowser.h"
|
||||||
|
|
||||||
|
namespace Ui
|
||||||
|
{
|
||||||
|
class ReleaseNotesDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReleaseNotesDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ReleaseNotesDialog(ImageTextBrowser::DownloadFn downloadFn, QWidget* parent = nullptr);
|
||||||
|
~ReleaseNotesDialog() override;
|
||||||
|
bool setMarkdown(QString markdown, const QString & issueUrl);
|
||||||
|
void setLabel(const QString & text);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void resizeEvent(QResizeEvent* event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::ReleaseNotesDialog* ui;
|
||||||
|
};
|
|
@ -35,15 +35,15 @@
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
<number>4</number>
|
<number>8</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="rightMargin">
|
<property name="rightMargin">
|
||||||
<number>4</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="horizontalSpacer">
|
<spacer name="horizontalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Orientation::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" stdset="0">
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
|
@ -53,6 +53,19 @@
|
||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="textFormat">
|
||||||
|
<enum>Qt::RichText</enum>
|
||||||
|
</property>
|
||||||
|
<property name="openExternalLinks">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="pushButton">
|
<widget class="QPushButton" name="pushButton">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -68,7 +81,7 @@
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>ImageTextBrowser</class>
|
<class>ImageTextBrowser</class>
|
||||||
<extends>QTextBrowser</extends>
|
<extends>QTextBrowser</extends>
|
||||||
<header>ImageTextBrowser.h</header>
|
<header location="global">Gui/ImageTextBrowser.h</header>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<resources/>
|
<resources/>
|
|
@ -0,0 +1,12 @@
|
||||||
|
# https://github.com/mity/md4c/commit/481fbfbdf72daab2912380d62bb5f2187d438408
|
||||||
|
add_library(md4c-html STATIC
|
||||||
|
md4c.c
|
||||||
|
md4c.h
|
||||||
|
md4c-entity.c
|
||||||
|
md4c-entity.h
|
||||||
|
md4c-html.c
|
||||||
|
md4c-html.h
|
||||||
|
)
|
||||||
|
target_include_directories(md4c-html PUBLIC
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
)
|
|
@ -2,6 +2,10 @@
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <Gui/ReleaseNotesDialog.h>
|
||||||
|
|
||||||
#include "StringUtil.h"
|
#include "StringUtil.h"
|
||||||
#include "MiscUtil.h"
|
#include "MiscUtil.h"
|
||||||
|
@ -136,10 +140,25 @@ static std::string httpGet(const char* url,
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
|
||||||
UpdateChecker::UpdateChecker(QWidget* parent)
|
UpdateChecker::UpdateChecker(QWidget* parent)
|
||||||
: QThread(parent),
|
: QThread(parent)
|
||||||
mParent(parent)
|
, mParent(parent)
|
||||||
|
, mUserAgent("x64dbg " + ToDateString(GetCompileDate()) + " " __TIME__)
|
||||||
{
|
{
|
||||||
connect(this, &UpdateChecker::updateCheckFinished, this, &UpdateChecker::finishedSlot);
|
connect(this, &UpdateChecker::updateCheckFinished, this, &UpdateChecker::finishedSlot);
|
||||||
|
|
||||||
|
auto downloadFn = [this](const QString & url) -> QImage
|
||||||
|
{
|
||||||
|
auto data = httpGet(url.toUtf8().constData(), mUserAgent.toUtf8().constData(), 3000);
|
||||||
|
if(data.empty())
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray byteArray(data.c_str(), (int)(data.size()));
|
||||||
|
return QImage::fromData(byteArray);
|
||||||
|
};
|
||||||
|
mReleaseNotes = new ReleaseNotesDialog(downloadFn, mParent);
|
||||||
|
mReleaseNotes->setWindowIcon(DIcon("bug"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateChecker::checkForUpdates()
|
void UpdateChecker::checkForUpdates()
|
||||||
|
@ -153,9 +172,8 @@ void UpdateChecker::checkForUpdates()
|
||||||
|
|
||||||
void UpdateChecker::run()
|
void UpdateChecker::run()
|
||||||
{
|
{
|
||||||
QString userAgent = "x64dbg " + ToDateString(GetCompileDate()) + " " __TIME__;
|
|
||||||
std::string result = httpGet("https://update.x64dbg.com/releases.json",
|
std::string result = httpGet("https://update.x64dbg.com/releases.json",
|
||||||
userAgent.toUtf8().constData(), 3000);
|
mUserAgent.toUtf8().constData(), 3000);
|
||||||
emit updateCheckFinished(QString::fromStdString(result));
|
emit updateCheckFinished(QString::fromStdString(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,26 +185,72 @@ void UpdateChecker::finishedSlot(const QString & json)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QRegExp regExp("\"published_at\": ?\"([^\"]+)\"");
|
QJsonParseError error;
|
||||||
QDateTime serverTime;
|
auto releases = QJsonDocument::fromJson(json.toUtf8(), &error).array();
|
||||||
if(regExp.indexIn(json) >= 0)
|
if(error.error != QJsonParseError::NoError || releases.isEmpty())
|
||||||
serverTime = QDateTime::fromString(regExp.cap(1), Qt::ISODate);
|
|
||||||
if(!serverTime.isValid())
|
|
||||||
{
|
{
|
||||||
SimpleErrorBox(mParent, tr("Error!"), tr("File on server could not be parsed..."));
|
SimpleErrorBox(mParent, tr("Error!"), tr("File on server could not be parsed..."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QRegExp regUrl("\"browser_download_url\": ?\"([^\"]+)\"");
|
|
||||||
auto url = regUrl.indexIn(json) >= 0 ? regUrl.cap(1) : "https://releases.x64dbg.com";
|
QString markdown;
|
||||||
auto server = serverTime.date();
|
QString label;
|
||||||
auto build = GetCompileDate();
|
auto buildDate = GetCompileDate();
|
||||||
QString info;
|
for(int i = 0; i < releases.size(); i++)
|
||||||
if(server > build)
|
{
|
||||||
info = QString(tr("New build %1 available!<br>Download <a href=\"%2\">here</a><br><br>You are now on build %3")).arg(ToDateString(server)).arg(url).arg(ToDateString(build));
|
QJsonObject release = releases[i].toObject();
|
||||||
else if(server < build)
|
auto publishedAt = release.value("published_at").toString();
|
||||||
info = QString(tr("You have a development build (%1) of x64dbg!")).arg(ToDateString(build));
|
auto publishedDate = QDateTime::fromString(publishedAt, Qt::ISODate).date();
|
||||||
else
|
auto tagName = release.value("tag_name").toString();
|
||||||
info = QString(tr("You have the latest build (%1) of x64dbg!")).arg(ToDateString(build));
|
|
||||||
GuiAddStatusBarMessage((info + "\n").toUtf8().constData());
|
if(i == 0)
|
||||||
SimpleInfoBox(mParent, tr("Information"), info);
|
{
|
||||||
|
if(publishedDate <= buildDate)
|
||||||
|
{
|
||||||
|
QString info;
|
||||||
|
if(publishedDate < buildDate)
|
||||||
|
{
|
||||||
|
info = QString(tr("You have a development build (%1) of x64dbg!")).arg(ToDateString(buildDate));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
info = tr("You have the latest build (%1) of x64dbg!").arg(ToDateString(buildDate));
|
||||||
|
}
|
||||||
|
GuiAddStatusBarMessage((info + "\n").toUtf8().constData());
|
||||||
|
SimpleInfoBox(mParent, tr("Information"), info);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString downloadUrl;
|
||||||
|
auto assets = release.value("assets").toArray();
|
||||||
|
for(int j = 0; j < assets.size(); j++)
|
||||||
|
{
|
||||||
|
auto asset = assets[i].toObject();
|
||||||
|
downloadUrl = asset.value("browser_download_url").toString();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
label = tr("<p><b>New x64dbg version available</b>: <a href=\"%1\">%2</a></p>").arg(downloadUrl, tagName);
|
||||||
|
GuiAddLogMessageHtml((label + "\n").toUtf8().constData());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not show release notes older than the current build
|
||||||
|
if(publishedDate <= buildDate)
|
||||||
|
break;
|
||||||
|
|
||||||
|
auto body = release.value("body").toString();
|
||||||
|
if(!body.startsWith("#"))
|
||||||
|
{
|
||||||
|
markdown += QString("# %1\n").arg(tagName);
|
||||||
|
}
|
||||||
|
|
||||||
|
markdown += body;
|
||||||
|
markdown += QString("\n\n<sub>%1</sub>").arg(publishedAt);
|
||||||
|
markdown += "\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
mReleaseNotes->move(mParent->frameGeometry().center() - mReleaseNotes->rect().center());
|
||||||
|
mReleaseNotes->setMarkdown(markdown, "https://github.com/x64dbg/x64dbg/issues/");
|
||||||
|
mReleaseNotes->setLabel(label);
|
||||||
|
mReleaseNotes->exec();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
|
class ReleaseNotesDialog;
|
||||||
|
|
||||||
class UpdateChecker : public QThread
|
class UpdateChecker : public QThread
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -20,4 +22,6 @@ private slots:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QWidget* mParent = nullptr;
|
QWidget* mParent = nullptr;
|
||||||
|
ReleaseNotesDialog* mReleaseNotes = nullptr;
|
||||||
|
QString mUserAgent;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue