Compare commits

..

2 commits

Author SHA1 Message Date
Rafael 20de3627d5
fix manifest release year
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-15 09:19:24 +01:00
Syping 13b6d614fc gta5view 1.10.2 release
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
- fix crash bug and upload issue
2023-03-15 07:10:50 +01:00
187 changed files with 16890 additions and 10037 deletions

33
.ci/app.rc Normal file
View file

@ -0,0 +1,33 @@
IDI_ICON1 ICON DISCARDABLE "5sync.ico"
#define RT_MANIFEST 24
#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "gta5view.exe.manifest"
#include <windows.h>
VS_VERSION_INFO VERSIONINFO
FILEVERSION MAJOR_VER, MINOR_VER, PATCH_VER, INT_BUILD_VER
PRODUCTVERSION MAJOR_VER, MINOR_VER, PATCH_VER, INT_BUILD_VER
FILEFLAGSMASK 0x3fL
FILEFLAGS 0
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0809, 1200
END
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "Syping"
VALUE "FileDescription", "gta5view"
VALUE "FileVersion", "MAJOR_VER.MINOR_VER.PATCH_VERSTR_BUILD_VER"
VALUE "InternalName", "gta5view"
VALUE "LegalCopyright", "Copyright © 2016-2023 Syping"
VALUE "OriginalFilename", "gta5view.exe"
VALUE "ProductName", "gta5view"
VALUE "ProductVersion", "MAJOR_VER.MINOR_VER.PATCH_VERSTR_BUILD_VER"
END
END
END

72
.ci/ci.sh Executable file
View file

@ -0,0 +1,72 @@
#!/usr/bin/env bash
if [ $(git name-rev --tags --name-only $(git rev-parse HEAD)) == "undefined" ]; then
export APPLICATION_VERSION=$(lua -e 'for line in io.lines("config.h") do local m = string.match(line, "#define GTA5SYNC_APPVER \"(.+)\"$"); if m then print(m); os.exit(0) end end')
else
export APPLICATION_VERSION=$(git name-rev --tags --name-only $(git rev-parse HEAD))
fi
export PACKAGE_VERSION=$(grep -oE '^[^\-]*' <<< $APPLICATION_VERSION)
export PACKAGE_BUILD=$(grep -oP '\-\K.+' <<< $APPLICATION_VERSION)
export EXECUTABLE_VERSION=${PACKAGE_VERSION}${PACKAGE_BUILD}${EXECUTABLE_TAG}
export APPLICATION_MAJOR_VERSION=$(cut -d. -f1 <<< $APPLICATION_VERSION)
export APPLICATION_MINOR_VERSION=$(cut -d. -f2 <<< $APPLICATION_VERSION)
export APPLICATION_PATCH_VERSION=$(cut -d. -f3 <<< $APPLICATION_VERSION)
if [ "${PACKAGE_BUILD}" == "" ]; then
export PACKAGE_BUILD=1
else
export APPLICATION_BUILD_INT_VERSION=$(grep -oE '[1-9]*$' <<< $PACKAGE_BUILD)
export APPLICATION_BUILD_STR_VERSION=-${PACKAGE_BUILD}
fi
cat ".ci/app.rc" | sed \
-e "s/MAJOR_VER/$APPLICATION_MAJOR_VERSION/g" \
-e "s/MINOR_VER/$APPLICATION_MINOR_VERSION/g" \
-e "s/PATCH_VER/$APPLICATION_PATCH_VERSION/g" \
-e "s/INT_BUILD_VER/0/g" \
-e "s/STR_BUILD_VER/$APPLICATION_BUILD_STR_VERSION/g" \
-e "s/STR_BUILD_VER/$APPLICATION_BUILD_STR_VERSION/g" \
> "res/app.rc"
if [ "${BUILD_TYPE}" == "ALPHA" ]; then
export CMAKE_BUILD_TYPE="-DGTA5VIEW_BUILDTYPE=Alpha"
export QMAKE_BUILD_TYPE="DEFINES+=GTA5SYNC_BUILDTYPE=\\\\\\\"Alpha\\\\\\\""
elif [ "${BUILD_TYPE}" == "Alpha" ]; then
export CMAKE_BUILD_TYPE="-DGTA5VIEW_BUILDTYPE=Alpha"
export QMAKE_BUILD_TYPE="DEFINES+=GTA5SYNC_BUILDTYPE=\\\\\\\"Alpha\\\\\\\""
elif [ "${BUILD_TYPE}" == "BETA" ]; then
export CMAKE_BUILD_TYPE="-DGTA5VIEW_BUILDTYPE=Beta"
export QMAKE_BUILD_TYPE="DEFINES+=GTA5SYNC_BUILDTYPE=\\\\\\\"Beta\\\\\\\""
elif [ "${BUILD_TYPE}" == "Beta" ]; then
export CMAKE_BUILD_TYPE="-DGTA5VIEW_BUILDTYPE=Beta"
export QMAKE_BUILD_TYPE="DEFINES+=GTA5SYNC_BUILDTYPE=\\\\\\\"Beta\\\\\\\""
elif [ "${BUILD_TYPE}" == "DEV" ]; then
export CMAKE_BUILD_TYPE="-DGTA5VIEW_BUILDTYPE=Developer"
export QMAKE_BUILD_TYPE="DEFINES+=GTA5SYNC_BUILDTYPE=\\\\\\\"Developer\\\\\\\""
elif [ "${BUILD_TYPE}" == "Development" ]; then
export CMAKE_BUILD_TYPE="-DGTA5VIEW_BUILDTYPE=Developer"
export QMAKE_BUILD_TYPE="DEFINES+=GTA5SYNC_BUILDTYPE=\\\\\\\"Developer\\\\\\\""
elif [ "${BUILD_TYPE}" == "DAILY" ]; then
export CMAKE_BUILD_TYPE="-DGTA5VIEW_BUILDTYPE=Daily Build"
export QMAKE_BUILD_TYPE="DEFINES+=GTA5SYNC_BUILDTYPE=\\\\\\\"Daily Build\\\\\\\""
elif [ "${BUILD_TYPE}" == "Daily" ]; then
export CMAKE_BUILD_TYPE="-DGTA5VIEW_BUILDTYPE=Daily Build"
export QMAKE_BUILD_TYPE="DEFINES+=GTA5SYNC_BUILDTYPE=\\\\\\\"Daily Build\\\\\\\""
elif [ "${BUILD_TYPE}" == "RC" ]; then
export CMAKE_BUILD_TYPE="-DGTA5VIEW_BUILDTYPE=Release Candidate"
export QMAKE_BUILD_TYPE="DEFINES+=GTA5SYNC_BUILDTYPE=\\\\\\\"Release Candidate\\\\\\\""
elif [ "${BUILD_TYPE}" == "Release Candidate" ]; then
export CMAKE_BUILD_TYPE="-DGTA5VIEW_BUILDTYPE=Release Candidate"
export QMAKE_BUILD_TYPE="DEFINES+=GTA5SYNC_BUILDTYPE=\\\\\\\"Release Candidate\\\\\\\""
elif [ "${BUILD_TYPE}" == "REL" ]; then
export CMAKE_BUILD_TYPE="-DGTA5VIEW_BUILDTYPE=Release"
export QMAKE_BUILD_TYPE="DEFINES+=GTA5SYNC_BUILDTYPE=\\\\\\\"Release\\\\\\\""
elif [ "${BUILD_TYPE}" == "Release" ]; then
export CMAKE_BUILD_TYPE="-DGTA5VIEW_BUILDTYPE=Release"
export QMAKE_BUILD_TYPE="DEFINES+=GTA5SYNC_BUILDTYPE=\\\\\\\"Release\\\\\\\""
fi
export PROJECT_DIR=$(pwd)
.ci/${BUILD_SCRIPT}

38
.ci/debian_build.sh Executable file
View file

@ -0,0 +1,38 @@
#!/usr/bin/env bash
# Creating folders
cd ${PROJECT_DIR} && \
echo "gta5view build version is ${APPLICATION_VERSION}" && \
mkdir -p build && \
mkdir -p assets && \
chmod -x res/gta5sync_*.qm res/*.desktop res/*gta5view*.png && \
cd build && \
mkdir -p qt5 && \
cd qt5 && \
echo "Grand Theft Auto V Snapmatic and Savegame viewer/editor" > ./description-pak && \
cd .. && \
# Set compiler
export CC=clang && \
export CXX=clang++ && \
# Prepare checkinstall step
mkdir -p /usr/share/gta5view && \
# Starting build
cd qt5 && \
cmake \
"-DCMAKE_INSTALL_PREFIX=/usr" \
"${CMAKE_BUILD_TYPE}" \
"-DFORCE_QT_VERSION=5" \
"-DGTA5VIEW_BUILDCODE=${PACKAGE_CODE}" \
"-DGTA5VIEW_APPVER=${APPLICATION_VERSION}" \
"-DGTA5VIEW_COMMIT=${APPLICATION_COMMIT}" \
"-DWITH_DONATE=ON" \
"-DWITH_TELEMETRY=ON" \
"-DDONATE_ADDRESSES=$(cat ${PROJECT_DIR}/.ci/donate.txt)" \
"-DTELEMETRY_WEBURL=https://dev.syping.de/gta5view-userstats/" \
"-DQCONF_BUILD=ON" \
../../ && \
make -j 4 && \
checkinstall -D --default --nodoc --install=no --pkgname=gta5view --pkgversion=${PACKAGE_VERSION} --pkgrelease=${PACKAGE_BUILD} --pkggroup=utility --maintainer="Syping \<dpkg@syping.de\>" --requires=libqt5core5a,libqt5gui5,libqt5network5,libqt5svg5,libqt5widgets5,qttranslations5-l10n --conflicts=gta5view-qt4,gta5view-qt5 --replaces=gta5view-qt4,gta5view-qt5 --pakdir=${PROJECT_DIR}/assets

8
.ci/debian_ci.sh Executable file
View file

@ -0,0 +1,8 @@
#!/usr/bin/env bash
# Install packages
.ci/debian_install.sh && \
# Build gta5view
.ci/debian_build.sh && \
cd ${PROJECT_DIR}

15
.ci/debian_docker.sh Executable file
View file

@ -0,0 +1,15 @@
#!/usr/bin/env bash
if [ "${DOCKER_USER}" != "" ]; then
DOCKER_IMAGE=${DOCKER_USER}/debian:${DEBIAN_VERSION}
else
DOCKER_IMAGE=debian:${DEBIAN_VERSION}
fi
PROJECT_DIR_DOCKER=/gta5view
cd ${PROJECT_DIR} && \
docker pull ${DOCKER_IMAGE} && \
docker run --rm \
-v "${PROJECT_DIR}:${PROJECT_DIR_DOCKER}" \
${DOCKER_IMAGE} \
/bin/bash -c "export PROJECT_DIR=${PROJECT_DIR_DOCKER} && export QT_SELECT=${QT_SELECT} && export APPLICATION_VERSION=${APPLICATION_VERSION} && export APPLICATION_COMMIT=${APPLICATION_COMMIT} && export BUILD_TYPE=${BUILD_TYPE} && export APT_INSTALL=${APT_INSTALL} && export QMAKE_FLAGS_QT4=${QMAKE_FLAGS_QT4} && export QMAKE_FLAGS_QT5=${QMAKE_FLAGS_QT5} && export CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} && export QMAKE_BUILD_TYPE=${QMAKE_BUILD_TYPE} && export PACKAGE_VERSION=${PACKAGE_VERSION} && export PACKAGE_BUILD=${PACKAGE_BUILD} && export PACKAGE_CODE=${PACKAGE_CODE} && export EXECUTABLE_VERSION=${EXECUTABLE_VERSION} && export EXECUTABLE_ARCH=${EXECUTABLE_ARCH} && cd ${PROJECT_DIR_DOCKER} && .ci/debian_install.sh && .ci/debian_build.sh"

13
.ci/debian_install.sh Executable file
View file

@ -0,0 +1,13 @@
#!/usr/bin/env bash
# Source OS Release
source /etc/os-release
# When Debian add backports
if [ "${ID}" == "debian" ]; then
echo "deb http://deb.debian.org/debian ${VERSION_CODENAME}-backports main" >> /etc/apt/sources.list
fi
# Install packages
apt-get update -qq && \
apt-get install -qq ${APT_INSTALL} checkinstall cmake dpkg-dev fakeroot g++ gcc qtbase5-dev qt5-qmake qttranslations5-l10n libqt5svg5-dev

1763
.ci/dropbox_uploader.sh Executable file

File diff suppressed because it is too large Load diff

364
.ci/gta5view.nsi Normal file
View file

@ -0,0 +1,364 @@
######################################################################
!define APP_NAME "gta5view"
!define APP_EXT ".g5e"
!define COMP_NAME "Syping"
!define WEB_SITE "https://gta5view.syping.de/"
!define VERSION "1.10.2.0"
!define COPYRIGHT "Copyright © 2016-2022 Syping"
!define DESCRIPTION "Open Source Snapmatic and Savegame viewer/editor for GTA V"
!define INSTALLER_NAME "gta5view_setup.exe"
!define MAIN_APP_EXE "gta5view.exe"
!define INSTALL_TYPE "SetShellVarContext all"
!define REG_ROOT "HKLM"
!define REG_APP_PATH "Software\Microsoft\Windows\CurrentVersion\App Paths\${MAIN_APP_EXE}"
!define UNINSTALL_PATH "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}"
!define LICENSE_TXT "../LICENSE"
######################################################################
VIProductVersion "${VERSION}"
VIAddVersionKey "ProductName" "${APP_NAME}"
VIAddVersionKey "ProductVersion" "${VERSION}"
VIAddVersionKey "CompanyName" "${COMP_NAME}"
VIAddVersionKey "LegalCopyright" "${COPYRIGHT}"
VIAddVersionKey "FileDescription" "${DESCRIPTION}"
VIAddVersionKey "FileVersion" "${VERSION}"
######################################################################
!include "x64.nsh"
SetCompressor LZMA
Name "${APP_NAME}"
Caption "${APP_NAME}"
OutFile "${INSTALLER_NAME}"
#BrandingText "${APP_NAME}"
XPStyle on
Unicode true
InstallDirRegKey "${REG_ROOT}" "${REG_APP_PATH}" ""
InstallDir "$PROGRAMFILES64\Syping\gta5view"
######################################################################
!include "MUI2.nsh"
!define MUI_ABORTWARNING
!define MUI_UNABORTWARNING
!define MUI_LANGDLL_REGISTRY_ROOT "${REG_ROOT}"
!define MUI_LANGDLL_REGISTRY_KEY "${UNINSTALL_PATH}"
!define MUI_LANGDLL_REGISTRY_VALUENAME "Installer Language"
!insertmacro MUI_PAGE_WELCOME
!ifdef LICENSE_TXT
!insertmacro MUI_PAGE_LICENSE "${LICENSE_TXT}"
!endif
!insertmacro MUI_PAGE_DIRECTORY
!ifdef REG_START_MENU
!define MUI_STARTMENUPAGE_NODISABLE
!define MUI_STARTMENUPAGE_DEFAULTFOLDER "gta5view"
!define MUI_STARTMENUPAGE_REGISTRY_ROOT "${REG_ROOT}"
!define MUI_STARTMENUPAGE_REGISTRY_KEY "${UNINSTALL_PATH}"
!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "${REG_START_MENU}"
!insertmacro MUI_PAGE_STARTMENU Application $SM_Folder
!endif
!insertmacro MUI_PAGE_INSTFILES
!define MUI_FINISHPAGE_RUN "$INSTDIR\${MAIN_APP_EXE}"
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_UNPAGE_FINISH
!insertmacro MUI_LANGUAGE "English"
!insertmacro MUI_LANGUAGE "French"
!insertmacro MUI_LANGUAGE "German"
!insertmacro MUI_LANGUAGE "Korean"
!insertmacro MUI_LANGUAGE "Russian"
!insertmacro MUI_LANGUAGE "Ukrainian"
!insertmacro MUI_LANGUAGE "TradChinese"
!insertmacro MUI_RESERVEFILE_LANGDLL
######################################################################
Function .onInit
!insertmacro MUI_LANGDLL_DISPLAY
!ifdef WIN32
MessageBox MB_OK|MB_ICONSTOP "Windows 32-Bit is not supported anymore!"
Quit
!endif
SetRegView 64
FunctionEnd
######################################################################
Section -MainProgram
${INSTALL_TYPE}
SetOverwrite ifnewer
SetOutPath "$INSTDIR"
File "../build/gta5view.exe"
File "/opt/llvm-mingw/x86_64-w64-mingw32/bin/libc++.dll"
File "/opt/llvm-mingw/x86_64-w64-mingw32/bin/libunwind.dll"
File "/usr/local/lib/x86_64-w64-mingw32/openssl/bin/libcrypto-1_1-x64.dll"
File "/usr/local/lib/x86_64-w64-mingw32/openssl/bin/libssl-1_1-x64.dll"
File "/usr/local/lib/x86_64-w64-mingw32/qt5/bin/Qt5Core.dll"
File "/usr/local/lib/x86_64-w64-mingw32/qt5/bin/Qt5Gui.dll"
File "/usr/local/lib/x86_64-w64-mingw32/qt5/bin/Qt5Network.dll"
File "/usr/local/lib/x86_64-w64-mingw32/qt5/bin/Qt5Svg.dll"
File "/usr/local/lib/x86_64-w64-mingw32/qt5/bin/Qt5Widgets.dll"
SetOutPath "$INSTDIR\lang"
File "../build/gta5sync_en_US.qm"
File "../build/gta5sync_de.qm"
File "../build/gta5sync_fr.qm"
File "../build/gta5sync_ko.qm"
File "../build/gta5sync_ru.qm"
File "../build/gta5sync_uk.qm"
File "../build/gta5sync_zh_TW.qm"
File "../build/qtbase_en_GB.qm"
File "../res/qt5/qtbase_de.qm"
File "../res/qt5/qtbase_fr.qm"
File "../res/qt5/qtbase_ko.qm"
File "../res/qt5/qtbase_ru.qm"
File "../res/qt5/qtbase_uk.qm"
File "../res/qt5/qtbase_zh_TW.qm"
SetOutPath "$INSTDIR\resources"
File "../res/add.svgz"
File "../res/avatararea.png"
File "../res/avatarareaimport.png"
File "../res/back.svgz"
File "../res/flag-de.png"
File "../res/flag-fr.png"
File "../res/flag-gb.png"
File "../res/flag-kr.png"
File "../res/flag-ru.png"
File "../res/flag-tw.png"
File "../res/flag-ua.png"
File "../res/flag-us.png"
File "../res/gta5view-16.png"
File "../res/gta5view-24.png"
File "../res/gta5view-32.png"
File "../res/gta5view-40.png"
File "../res/gta5view-48.png"
File "../res/gta5view-64.png"
File "../res/gta5view-96.png"
File "../res/gta5view-128.png"
File "../res/gta5view-256.png"
File "../res/mapcayoperico.jpg"
File "../res/mappreview.jpg"
File "../res/next.svgz"
File "../res/pointmaker-8.png"
File "../res/pointmaker-16.png"
File "../res/pointmaker-24.png"
File "../res/pointmaker-32.png"
File "../res/savegame.svgz"
File "../res/watermark_1b.png"
File "../res/watermark_2b.png"
File "../res/watermark_2r.png"
SetOutPath "$INSTDIR\imageformats"
File "/usr/local/lib/x86_64-w64-mingw32/qt5/plugins/imageformats/qgif.dll"
File "/usr/local/lib/x86_64-w64-mingw32/qt5/plugins/imageformats/qicns.dll"
File "/usr/local/lib/x86_64-w64-mingw32/qt5/plugins/imageformats/qico.dll"
File "/usr/local/lib/x86_64-w64-mingw32/qt5/plugins/imageformats/qjpeg.dll"
File "/usr/local/lib/x86_64-w64-mingw32/qt5/plugins/imageformats/qsvg.dll"
File "/usr/local/lib/x86_64-w64-mingw32/qt5/plugins/imageformats/qtga.dll"
File "/usr/local/lib/x86_64-w64-mingw32/qt5/plugins/imageformats/qtiff.dll"
File "/usr/local/lib/x86_64-w64-mingw32/qt5/plugins/imageformats/qwbmp.dll"
File "/usr/local/lib/x86_64-w64-mingw32/qt5/plugins/imageformats/qwebp.dll"
SetOutPath "$INSTDIR\platforms"
File "/usr/local/lib/x86_64-w64-mingw32/qt5/plugins/platforms/qwindows.dll"
SetOutPath "$INSTDIR\styles"
File "/usr/local/lib/x86_64-w64-mingw32/qt5/plugins/styles/qcleanlooksstyle.dll"
File "/usr/local/lib/x86_64-w64-mingw32/qt5/plugins/styles/qplastiquestyle.dll"
File "/usr/local/lib/x86_64-w64-mingw32/qt5/plugins/styles/qwindowsvistastyle.dll"
SectionEnd
######################################################################
Section -Icons_Reg
SetOutPath "$INSTDIR"
WriteUninstaller "$INSTDIR\uninstall.exe"
!ifdef REG_START_MENU
!insertmacro MUI_STARTMENU_WRITE_BEGIN Application
CreateDirectory "$SMPROGRAMS\$SM_Folder"
CreateShortCut "$SMPROGRAMS\$SM_Folder\${APP_NAME}.lnk" "$INSTDIR\${MAIN_APP_EXE}"
CreateShortCut "$SMPROGRAMS\$SM_Folder\Uninstall ${APP_NAME}.lnk" "$INSTDIR\uninstall.exe"
!ifdef WEB_SITE
WriteIniStr "$INSTDIR\${APP_NAME} website.url" "InternetShortcut" "URL" "${WEB_SITE}"
CreateShortCut "$SMPROGRAMS\$SM_Folder\gta5view Website.lnk" "$INSTDIR\${APP_NAME} website.url"
!endif
!insertmacro MUI_STARTMENU_WRITE_END
!endif
!ifndef REG_START_MENU
CreateDirectory "$SMPROGRAMS\gta5view"
CreateShortCut "$SMPROGRAMS\gta5view\${APP_NAME}.lnk" "$INSTDIR\${MAIN_APP_EXE}"
CreateShortCut "$SMPROGRAMS\gta5view\Uninstall ${APP_NAME}.lnk" "$INSTDIR\uninstall.exe"
!ifdef WEB_SITE
WriteIniStr "$INSTDIR\${APP_NAME} website.url" "InternetShortcut" "URL" "${WEB_SITE}"
CreateShortCut "$SMPROGRAMS\gta5view\gta5view Website.lnk" "$INSTDIR\${APP_NAME} website.url"
!endif
!endif
WriteRegStr ${REG_ROOT} "${REG_APP_PATH}" "" "$INSTDIR\${MAIN_APP_EXE}"
WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "DisplayName" "${APP_NAME}"
WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "UninstallString" "$INSTDIR\uninstall.exe"
WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "DisplayIcon" "$INSTDIR\${MAIN_APP_EXE}"
WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "DisplayVersion" "${VERSION}"
WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "Publisher" "${COMP_NAME}"
!ifdef WEB_SITE
WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "URLInfoAbout" "${WEB_SITE}"
!endif
SectionEnd
######################################################################
Section -ShellAssoc
WriteRegStr ${REG_ROOT} "Software\Classes\${APP_NAME}\DefaultIcon" "" "$INSTDIR\${MAIN_APP_EXE},0"
WriteRegStr ${REG_ROOT} "Software\Classes\${APP_NAME}\shell\open\command" "" '"$INSTDIR\${MAIN_APP_EXE}" "%1"'
WriteRegStr ${REG_ROOT} "Software\Classes\${APP_EXT}" "" "${APP_NAME}"
WriteRegStr ${REG_ROOT} "Software\Classes\${APP_EXT}" "Content Type" "application/x-gta5view-export"
System::Call 'SHELL32::SHChangeNotify(i0x8000000,i0,p0,p0)'
SectionEnd
######################################################################
Section -un.ShellAssoc
ClearErrors
ReadRegStr $0 ${REG_ROOT} "Software\Classes\${APP_EXT}" ""
DeleteRegKey ${REG_ROOT} "Software\Classes\${APP_NAME}"
${IfNot} ${Errors}
${AndIf} $0 == "${APP_NAME}"
DeleteRegValue ${REG_ROOT} "Software\Classes\${APP_EXT}" ""
DeleteRegKey /IfEmpty ${REG_ROOT} "Software\Classes\${APP_EXT}"
${EndIf}
System::Call 'SHELL32::SHChangeNotify(i0x8000000,i0,p0,p0)'
SectionEnd
######################################################################
Section Uninstall
${INSTALL_TYPE}
Delete "$INSTDIR\gta5view.exe"
Delete "$INSTDIR\libc++.dll"
Delete "$INSTDIR\libunwind.dll"
Delete "$INSTDIR\libcrypto-1_1-x64.dll"
Delete "$INSTDIR\libssl-1_1-x64.dll"
Delete "$INSTDIR\Qt5Core.dll"
Delete "$INSTDIR\Qt5Gui.dll"
Delete "$INSTDIR\Qt5Network.dll"
Delete "$INSTDIR\Qt5Svg.dll"
Delete "$INSTDIR\Qt5Widgets.dll"
Delete "$INSTDIR\lang\gta5sync_en_US.qm"
Delete "$INSTDIR\lang\gta5sync_de.qm"
Delete "$INSTDIR\lang\gta5sync_fr.qm"
Delete "$INSTDIR\lang\gta5sync_ko.qm"
Delete "$INSTDIR\lang\gta5sync_ru.qm"
Delete "$INSTDIR\lang\gta5sync_uk.qm"
Delete "$INSTDIR\lang\gta5sync_zh_TW.qm"
Delete "$INSTDIR\lang\qtbase_en_GB.qm"
Delete "$INSTDIR\lang\qtbase_de.qm"
Delete "$INSTDIR\lang\qtbase_fr.qm"
Delete "$INSTDIR\lang\qtbase_ko.qm"
Delete "$INSTDIR\lang\qtbase_ru.qm"
Delete "$INSTDIR\lang\qtbase_uk.qm"
Delete "$INSTDIR\lang\qtbase_zh_TW.qm"
Delete "$INSTDIR\resources\add.svgz"
Delete "$INSTDIR\resources\avatararea.png"
Delete "$INSTDIR\resources\avatarareaimport.png"
Delete "$INSTDIR\resources\back.svgz"
Delete "$INSTDIR\resources\flag-de.png"
Delete "$INSTDIR\resources\flag-fr.png"
Delete "$INSTDIR\resources\flag-gb.png"
Delete "$INSTDIR\resources\flag-kr.png"
Delete "$INSTDIR\resources\flag-ru.png"
Delete "$INSTDIR\resources\flag-tw.png"
Delete "$INSTDIR\resources\flag-ua.png"
Delete "$INSTDIR\resources\flag-us.png"
Delete "$INSTDIR\resources\gta5view-16.png"
Delete "$INSTDIR\resources\gta5view-24.png"
Delete "$INSTDIR\resources\gta5view-32.png"
Delete "$INSTDIR\resources\gta5view-40.png"
Delete "$INSTDIR\resources\gta5view-48.png"
Delete "$INSTDIR\resources\gta5view-64.png"
Delete "$INSTDIR\resources\gta5view-96.png"
Delete "$INSTDIR\resources\gta5view-128.png"
Delete "$INSTDIR\resources\gta5view-256.png"
Delete "$INSTDIR\resources\mapcayoperico.jpg"
Delete "$INSTDIR\resources\mappreview.jpg"
Delete "$INSTDIR\resources\next.svgz"
Delete "$INSTDIR\resources\pointmaker-8.png"
Delete "$INSTDIR\resources\pointmaker-16.png"
Delete "$INSTDIR\resources\pointmaker-24.png"
Delete "$INSTDIR\resources\pointmaker-32.png"
Delete "$INSTDIR\resources\savegame.svgz"
Delete "$INSTDIR\resources\watermark_1b.png"
Delete "$INSTDIR\resources\watermark_2b.png"
Delete "$INSTDIR\resources\watermark_2r.png"
Delete "$INSTDIR\imageformats\qgif.dll"
Delete "$INSTDIR\imageformats\qicns.dll"
Delete "$INSTDIR\imageformats\qico.dll"
Delete "$INSTDIR\imageformats\qjpeg.dll"
Delete "$INSTDIR\imageformats\qsvg.dll"
Delete "$INSTDIR\imageformats\qtga.dll"
Delete "$INSTDIR\imageformats\qtiff.dll"
Delete "$INSTDIR\imageformats\qwbmp.dll"
Delete "$INSTDIR\imageformats\qwebp.dll"
Delete "$INSTDIR\platforms\qwindows.dll"
Delete "$INSTDIR\styles\qcleanlooksstyle.dll"
Delete "$INSTDIR\styles\qplastiquestyle.dll"
Delete "$INSTDIR\styles\qwindowsvistastyle.dll"
RmDir "$INSTDIR\lang"
RmDir "$INSTDIR\imageformats"
RmDir "$INSTDIR\platforms"
RmDir "$INSTDIR\styles"
Delete "$INSTDIR\uninstall.exe"
!ifdef WEB_SITE
Delete "$INSTDIR\${APP_NAME} website.url"
!endif
RmDir "$INSTDIR"
!ifdef REG_START_MENU
!insertmacro MUI_STARTMENU_GETFOLDER "Application" $SM_Folder
Delete "$SMPROGRAMS\$SM_Folder\${APP_NAME}.lnk"
Delete "$SMPROGRAMS\$SM_Folder\Uninstall ${APP_NAME}.lnk"
!ifdef WEB_SITE
Delete "$SMPROGRAMS\$SM_Folder\gta5view Website.lnk"
!endif
RmDir "$SMPROGRAMS\$SM_Folder"
!endif
!ifndef REG_START_MENU
Delete "$SMPROGRAMS\gta5view\${APP_NAME}.lnk"
Delete "$SMPROGRAMS\gta5view\Uninstall ${APP_NAME}.lnk"
!ifdef WEB_SITE
Delete "$SMPROGRAMS\gta5view\gta5view Website.lnk"
!endif
RmDir "$SMPROGRAMS\gta5view"
!endif
DeleteRegKey ${REG_ROOT} "${REG_APP_PATH}"
DeleteRegKey ${REG_ROOT} "${UNINSTALL_PATH}"
SectionEnd
######################################################################
Function un.onInit
!insertmacro MUI_UNGETLANGUAGE
SetRegView 64
FunctionEnd
######################################################################

24
.ci/osx_build.sh Executable file
View file

@ -0,0 +1,24 @@
#!/usr/bin/env bash
# Creating folders
cd ${PROJECT_DIR} && \
echo "gta5view build version is ${APPLICATION_VERSION}" && \
echo "gta5view image name is gta5view-osx_${APPLICATION_VERSION}.dmg" && \
mkdir -p build && \
mkdir -p assets && \
cd build && \
/usr/local/bin/cmake \
"-DCMAKE_PREFIX_PATH=/usr/local/opt/qt" \
"${CMAKE_BUILD_TYPE}" \
"-DGTA5VIEW_BUILDCODE=${PACKAGE_CODE}" \
"-DGTA5VIEW_APPVER=${APPLICATION_VERSION}" \
"-DGTA5VIEW_COMMIT=${APPLICATION_COMMIT}" \
"-DWITH_DONATE=ON" \
"-DWITH_TELEMETRY=ON" \
"-DDONATE_ADDRESSES=$(cat ${PROJECT_DIR}/.ci/donate.txt)" \
"-DTELEMETRY_WEBURL=https://dev.syping.de/gta5view-userstats/" \
../ && \
make -j 4 && \
/usr/local/opt/qt/bin/macdeployqt gta5view.app -dmg && \
cp -Rf gta5view.dmg ../assets/gta5view-osx_${APPLICATION_VERSION}.dmg

8
.ci/osx_ci.sh Executable file
View file

@ -0,0 +1,8 @@
#!/usr/bin/env bash
# Install packages
.ci/osx_install.sh && \
# Build gta5view
.ci/osx_build.sh && \
cd ${PROJECT_DIR}

4
.ci/osx_install.sh Executable file
View file

@ -0,0 +1,4 @@
#!/usr/bin/env bash
# Install packages
brew upgrade cmake qt

28
.ci/windows_build.sh Executable file
View file

@ -0,0 +1,28 @@
#!/usr/bin/env bash
# Prepare environment variable
export GTA5VIEW_EXECUTABLE=gta5view-${EXECUTABLE_VERSION}${EXECUTABLE_ARCH}.exe && \
# Creating folders
cd ${PROJECT_DIR} && \
echo "gta5view build version is ${APPLICATION_VERSION}" && \
echo "gta5view executable is ${GTA5VIEW_EXECUTABLE}" && \
mkdir -p build && \
mkdir -p assets && \
# Starting build
cd build && \
mingw64-qt-cmake \
"${CMAKE_BUILD_TYPE}" \
"-DGTA5VIEW_BUILDCODE=${PACKAGE_CODE}" \
"-DGTA5VIEW_APPVER=${APPLICATION_VERSION}" \
"-DGTA5VIEW_COMMIT=${APPLICATION_COMMIT}" \
"-DWITH_DONATE=ON" \
"-DWITH_TELEMETRY=ON" \
"-DDONATE_ADDRESSES=$(cat ${PROJECT_DIR}/.ci/donate.txt)" \
"-DTELEMETRY_WEBURL=https://dev.syping.de/gta5view-userstats/" \
.. && \
make -j 4 && \
x86_64-w64-mingw32-strip -s gta5view.exe && \
cp -Rf *.exe ${PROJECT_DIR}/assets/${GTA5VIEW_EXECUTABLE} && \
cd ${PROJECT_DIR}/assets

25
.ci/windows_docker.sh Executable file
View file

@ -0,0 +1,25 @@
#!/usr/bin/env bash
DOCKER_IMAGE=sypingauto/gta5view-build:1.10-static
PROJECT_DIR_DOCKER=/gta5view
cd ${PROJECT_DIR} && \
docker pull ${DOCKER_IMAGE} && \
docker run --rm \
-v "${PROJECT_DIR}:${PROJECT_DIR_DOCKER}" \
${DOCKER_IMAGE} \
/bin/bash -c "export PROJECT_DIR=${PROJECT_DIR_DOCKER} && export QT_SELECT=${QT_SELECT} && export APPLICATION_VERSION=${APPLICATION_VERSION} && export APPLICATION_COMMIT=${APPLICATION_COMMIT} && export BUILD_TYPE=${BUILD_TYPE} && export QMAKE_FLAGS_QT4=${QMAKE_FLAGS_QT4} && export QMAKE_FLAGS_QT5=${QMAKE_FLAGS_QT5} && export CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} && export QMAKE_BUILD_TYPE=${QMAKE_BUILD_TYPE} && export PACKAGE_VERSION=${PACKAGE_VERSION} && export PACKAGE_BUILD=${PACKAGE_BUILD} && export PACKAGE_CODE=${PACKAGE_CODE} && export EXECUTABLE_VERSION=${EXECUTABLE_VERSION} && export EXECUTABLE_ARCH=${EXECUTABLE_ARCH} && cd ${PROJECT_DIR_DOCKER} && .ci/windows_build.sh" && \
# Prepare environment variable
export GTA5VIEW_EXECUTABLE=gta5view-${EXECUTABLE_VERSION}${EXECUTABLE_ARCH}.exe && \
# Upload Assets to Dropbox
if [ "${PACKAGE_CODE}" == "gta5-mods" ]; then
${PROJECT_DIR}/.ci/dropbox_uploader.sh mkdir gta5-mods/${PACKAGE_VERSION}
${PROJECT_DIR}/.ci/dropbox_uploader.sh upload ${PROJECT_DIR}/assets/${GTA5VIEW_EXECUTABLE} gta5-mods/${PACKAGE_VERSION}/${GTA5VIEW_EXECUTABLE} && \
rm -rf ${GTA5VIEW_EXECUTABLE}
elif [ "${PACKAGE_CODE}" == "gtainside" ]; then
${PROJECT_DIR}/.ci/dropbox_uploader.sh mkdir gtainside/${PACKAGE_VERSION}
${PROJECT_DIR}/.ci/dropbox_uploader.sh upload ${PROJECT_DIR}/assets/${GTA5VIEW_EXECUTABLE} gtainside/${PACKAGE_VERSION}/${GTA5VIEW_EXECUTABLE} && \
rm -rf ${GTA5VIEW_EXECUTABLE}
fi

29
.ci/wininstall_build.sh Executable file
View file

@ -0,0 +1,29 @@
#!/usr/bin/env bash
# Creating folders
cd ${PROJECT_DIR} && \
echo "gta5view build version is ${APPLICATION_VERSION}" && \
mkdir -p build && \
mkdir -p assets && \
# Starting build
cd build && \
mingw64-qt-cmake \
"${CMAKE_BUILD_TYPE}" \
"-DGTA5VIEW_BUILDCODE=${PACKAGE_CODE}" \
"-DGTA5VIEW_APPVER=${APPLICATION_VERSION}" \
"-DGTA5VIEW_COMMIT=${APPLICATION_COMMIT}" \
"-DWITH_DONATE=ON" \
"-DWITH_TELEMETRY=ON" \
"-DDONATE_ADDRESSES=$(cat ${PROJECT_DIR}/.ci/donate.txt)" \
"-DTELEMETRY_WEBURL=https://dev.syping.de/gta5view-userstats/" \
"-DQCONF_BUILD=ON" \
"-DGTA5VIEW_INLANG=RUNDIR:SEPARATOR:lang" \
"-DGTA5VIEW_LANG=RUNDIR:SEPARATOR:lang" \
"-DGTA5VIEW_PLUG=RUNDIR:SEPARATOR:plugins" \
.. && \
make -j 4 && \
x86_64-w64-mingw32-strip -s gta5view.exe && \
cd ${PROJECT_DIR}/assets && \
makensis "-XTarget amd64-unicode" -NOCD ${PROJECT_DIR}/.ci/gta5view.nsi && \
mv -f gta5view_setup.exe gta5view-${EXECUTABLE_VERSION}_setup.exe

11
.ci/wininstall_docker.sh Executable file
View file

@ -0,0 +1,11 @@
#!/usr/bin/env bash
DOCKER_IMAGE=sypingauto/gta5view-build:1.10-shared
PROJECT_DIR_DOCKER=/gta5view
cd ${PROJECT_DIR} && \
docker pull ${DOCKER_IMAGE} && \
docker run --rm \
-v "${PROJECT_DIR}:${PROJECT_DIR_DOCKER}" \
${DOCKER_IMAGE} \
/bin/bash -c "export PROJECT_DIR=${PROJECT_DIR_DOCKER} && export QT_SELECT=${QT_SELECT} && export APPLICATION_VERSION=${APPLICATION_VERSION} && export APPLICATION_COMMIT=${APPLICATION_COMMIT} && export BUILD_TYPE=${BUILD_TYPE} && export QMAKE_FLAGS_QT4=${QMAKE_FLAGS_QT4} && export QMAKE_FLAGS_QT5=${QMAKE_FLAGS_QT5} && export CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} && export QMAKE_BUILD_TYPE=${QMAKE_BUILD_TYPE} && export PACKAGE_VERSION=${PACKAGE_VERSION} && export PACKAGE_BUILD=${PACKAGE_BUILD} && export PACKAGE_CODE=${PACKAGE_CODE} && export EXECUTABLE_VERSION=${EXECUTABLE_VERSION} && export EXECUTABLE_ARCH=${EXECUTABLE_ARCH} && cd ${PROJECT_DIR_DOCKER} && .ci/wininstall_build.sh"

36
.drone.yml Normal file
View file

@ -0,0 +1,36 @@
kind: pipeline
type: docker
environment:
BUILD_TYPE: "REL"
steps:
- name: Windows Installer
image: sypingauto/gta5view-build:1.10-shared
environment:
BUILD_SCRIPT: "wininstall_build.sh"
QT_SELECT: "qt5-x86_64-w64-mingw32"
TCA_PASS:
from_secret: tca_pass
commands:
- .drone/drone.sh
volumes:
- name: gta5view
path: /srv/gta5view
- name: Windows Portable
image: sypingauto/gta5view-build:1.10-static
environment:
BUILD_SCRIPT: "windows_build.sh"
QT_SELECT: "qt5-x86_64-w64-mingw32"
TCA_PASS:
from_secret: tca_pass
commands:
- .drone/drone.sh
volumes:
- name: gta5view
path: /srv/gta5view
volumes:
- name: gta5view
host:
path: /srv/gta5view

Binary file not shown.

33
.drone/drone.sh Executable file
View file

@ -0,0 +1,33 @@
#!/bin/bash
# Cleanup previous Drone build
if [ -d "assets" ]; then
rm -rf assets
fi
if [ -d "build" ]; then
rm -rf build
fi
# Decrypt Telemetry Authenticator
rm -rf tmext/TelemetryClassAuthenticator.cpp && \
openssl aes-256-cbc -k ${TCA_PASS} -in .drone/TelemetryClassAuthenticator.cpp.enc -out tmext/TelemetryClassAuthenticator.cpp -d -pbkdf2
# Check if build is not tagged
if [ "${DRONE_TAG}" == "" ]; then
export EXECUTABLE_TAG=-$(git rev-parse --short HEAD)
else
export EXECUTABLE_TAG=
fi
# Check if package code is not set
if [ "${PACKAGE_CODE}" == "" ]; then
export PACKAGE_CODE=Drone
fi
# Init Application Commit Hash
export APPLICATION_COMMIT=$(git rev-parse --short HEAD)
# Start CI script and copying assets into gta5view directory
.ci/ci.sh && \
mkdir -p /srv/gta5view/${APPLICATION_COMMIT} && \
cp -Rf assets/* /srv/gta5view/${APPLICATION_COMMIT}/

View file

@ -0,0 +1,22 @@
app-id: de.syping.gta5view
runtime: org.kde.Platform
runtime-version: '5.15-21.08'
sdk: org.kde.Sdk
command: gta5view
finish-args:
- --share=network
- --share=ipc
- --socket=fallback-x11
- --socket=wayland
- --device=dri
modules:
- name: gta5view
buildsystem: cmake-ninja
config-opts:
- -DFLATPAK_BUILD=ON
- -DQCONF_BUILD=ON
- -DGTA5VIEW_BUILDCODE=Flatpak
- -DGTA5VIEW_BUILDTYPE=Release
sources:
- type: dir
path: ../

3
.gitignore vendored
View file

@ -27,8 +27,7 @@
*.out
*.app
# Qt project user files
CMakeLists.txt.user
# Qt project user file
*.pro.user
# Gettext translation files

31
.gitlab-ci.yml Normal file
View file

@ -0,0 +1,31 @@
stages:
- build
variables:
BUILD_TYPE: "REL"
Windows Installer:
stage: build
image: sypingauto/gta5view-build:1.10-shared
variables:
BUILD_SCRIPT: "wininstall_build.sh"
QT_SELECT: "qt5-x86_64-w64-mingw32"
script:
- .gitlab/gitlab.sh
artifacts:
name: "gta5view-$CI_COMMIT_REF_NAME-${CI_COMMIT_SHA:0:8}_setup"
paths:
- "gta5view-*.exe"
Windows Portable:
stage: build
image: sypingauto/gta5view-build:1.10-static
variables:
BUILD_SCRIPT: "windows_build.sh"
QT_SELECT: "qt5-x86_64-w64-mingw32"
script:
- .gitlab/gitlab.sh
artifacts:
name: "gta5view-$CI_COMMIT_REF_NAME-${CI_COMMIT_SHA:0:8}_portable"
paths:
- "gta5view-*.exe"

Binary file not shown.

24
.gitlab/gitlab.sh Executable file
View file

@ -0,0 +1,24 @@
#!/bin/bash
# Decrypt Telemetry Authenticator
rm -rf tmext/TelemetryClassAuthenticator.cpp && \
openssl aes-256-cbc -k ${tca_pass} -in .gitlab/TelemetryClassAuthenticator.cpp.enc -out tmext/TelemetryClassAuthenticator.cpp -d
# Check if build is not tagged
if [ "${CI_COMMIT_TAG}" == "" ]; then
export EXECUTABLE_TAG=-$(git rev-parse --short HEAD)
else
export EXECUTABLE_TAG=
fi
# Check if package code is not set
if [ "${PACKAGE_CODE}" == "" ]; then
export PACKAGE_CODE=GitLab
fi
# Init Application Commit Hash
export APPLICATION_COMMIT=$(git rev-parse --short HEAD)
# Start CI script and copying assets into base directory
.ci/ci.sh && \
cp -Rf assets/* ./

6
.gitmodules vendored
View file

@ -1,6 +0,0 @@
[submodule "src/libragephoto"]
path = src/libragephoto
url = https://github.com/Syping/libragephoto.git
[submodule "src/json"]
path = src/json
url = https://github.com/boostorg/json.git

50
.travis.yml Normal file
View file

@ -0,0 +1,50 @@
dist: bionic
sudo: required
language: cpp
services:
- docker
env:
global:
- BUILD_TYPE=REL
matrix:
include:
- env:
- BUILD_SCRIPT=debian_docker.sh
- RELEASE_LABEL="Debian 64-Bit Package"
- DEBIAN_VERSION=buster
- DOCKER_USER=amd64
- APT_INSTALL=clang
- env:
- BUILD_SCRIPT=windows_docker.sh
- QT_SELECT=qt5-x86_64-w64-mingw32
- RELEASE_LABEL="Windows 64-Bit Portable"
- env:
- BUILD_SCRIPT=wininstall_docker.sh
- QT_SELECT=qt5-x86_64-w64-mingw32
- RELEASE_LABEL="Windows 64-Bit Installer"
- os: osx
osx_image: xcode14.2
env:
- BUILD_SCRIPT=osx_ci.sh
- RELEASE_LABEL="Mac OS X 64-Bit Disk Image"
before_install:
- ".travis/source.sh"
script:
- ".travis/travis.sh"
deploy:
provider: releases
api_key:
secure: o7VneEz1aHfdVwZvOZLfopf6uJWNrFsZaBvunTmXFzpmNFhlNS1qwqgMUkIA2yBRbZ3wIzVs4vfwIHv7W9yE/PqK+AYL+R8+AwKGrwlgT4HqJNuk6VM/LNJ6GwT/qkQuaoOVw29bUjmzzgIRdHmw53SlJv6Hh1VE8HphlTT//aex6nCfcFhUZ0BETdZDWz5FSHwL3NalUoqfKfQrJeky5RXzCyCANQC2tKt0bV46GaWIgWrDo2KCTNqPtRWWf5GDmnkXE5IYRMQ3mXvO9iYh0v5Y2jo4PiXGUiFUU6Z3aAWFAiPdGclrBO697cf3lCTzDMhuCETR153qFYsLShUlFf61ITAmCeHAWETjZDri0lmPONo3GoNB6alGfYEA51qw14kXakrTpICtTJj7gw/gtUYOabW6hrzmieNzMBIy62RikDPjyakFnuwW2qNHRlD65e0jYv+6nCpb6E+OV16Ysh1zhV2vTfpfzVmSuyu2J+ELqXD3OZCXRSPpDIih9UQ8335p8FBji6jHORcgym/TRgdgRmENibh8tLzWp+UjpWHuWfcpvZgOskjfwU0iDMCayMJ7tDpOhXHcAhDRnd6XRIiOJ5YZCzflj2nEwmt3YUd7DwXS/AU+WHOmcNQBjXBxF/FJa35XXcy3HKJM5TTKqtph3medo30us5yXHeG6NNg=
label: ${RELEASE_LABEL}
file_glob: true
file: assets/*
skip_cleanup: true
on:
tags: true

Binary file not shown.

View file

@ -0,0 +1 @@
Poァ鏖<EFBFBD>劾ラ<作1x」%几<>ャネw|RtZv<>kホ銓顴Z肄2チ廁湮ォ`<1A>,4vヨマ・@€<>ヲeハ・~U$+<2B><>€<EFBFBD><1A><牾&蓬熙

5
.travis/source.sh Executable file
View file

@ -0,0 +1,5 @@
#!/bin/bash
rm -rf tmext/TelemetryClassAuthenticator.cpp && \
openssl aes-256-cbc -K $encrypted_db000a5d87d6_key -iv $encrypted_db000a5d87d6_iv -in .travis/TelemetryClassAuthenticator.cpp.enc -out tmext/TelemetryClassAuthenticator.cpp -d && \
openssl aes-256-cbc -K $encrypted_d57e7d2f8877_key -iv $encrypted_d57e7d2f8877_iv -in .travis/dropbox_uploader.enc -out ~/.dropbox_uploader -d

27
.travis/travis.sh Executable file
View file

@ -0,0 +1,27 @@
#!/bin/bash
# Install lua
if [ "${TRAVIS_OS_NAME}" == "osx" ]; then
brew install lua
else
sudo apt-get update -qq && \
sudo apt-get install -qq lua5.2
fi
# Check if build is not tagged
if [ "${TRAVIS_TAG}" == "" ]; then
export EXECUTABLE_TAG=-$(git rev-parse --short HEAD)
else
export EXECUTABLE_TAG=
fi
# Check if package code is not set
if [ "${PACKAGE_CODE}" == "" ]; then
export PACKAGE_CODE=GitHub
fi
# Init Application Commit Hash
export APPLICATION_COMMIT=$(git rev-parse --short HEAD)
# Start CI script
.ci/ci.sh

8
.travis/ubuntu_travis.sh Executable file
View file

@ -0,0 +1,8 @@
#!/bin/bash
# Install packages
sudo .ci/debian_install.sh && \
# Build gta5view
sudo .ci/debian_build.sh && \
cd ${PROJECT_DIR}

View file

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2023 Syping
* Copyright (C) 2016-2021 Syping
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -29,7 +29,11 @@ AboutDialog::AboutDialog(QWidget *parent) :
ui(new Ui::AboutDialog)
{
// Set Window Flags
#if QT_VERSION >= 0x050900
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
#else
setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint);
#endif
// Build Strings
QString appVersion = QApplication::applicationVersion();

View file

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2023 Syping
* Copyright (C) 2016-2021 Syping
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -51,25 +51,21 @@ QString AppEnv::getBuildCode()
// Folder Stuff
QString AppEnv::getGTAVFolder(bool *ok)
QString AppEnv::getGameFolder(bool *ok)
{
QDir dir;
QString GTAV_FOLDER = QString::fromUtf8(qgetenv("GTAV_FOLDER"));
if (!GTAV_FOLDER.isEmpty()) {
if (GTAV_FOLDER != "") {
dir.setPath(GTAV_FOLDER);
if (dir.exists()) {
if (ok)
if (ok != NULL)
*ok = true;
qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8());
return dir.absolutePath();
}
}
#ifdef Q_OS_UNIX
// TODO: Try to locate the Steam Proton GTA V folder
const QString GTAV_defaultFolder = StandardPaths::documentsLocation() % "/Rockstar Games/GTA V";
#else
const QString GTAV_defaultFolder = StandardPaths::documentsLocation() % "/Rockstar Games/GTA V";
#endif
QString GTAV_returnFolder = GTAV_defaultFolder;
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
@ -78,100 +74,48 @@ QString AppEnv::getGTAVFolder(bool *ok)
GTAV_returnFolder = settings.value("dir", GTAV_defaultFolder).toString();
settings.endGroup();
settings.beginGroup("GameDirectory");
settings.beginGroup("GTA V");
forceDir = settings.value("ForceCustom", forceDir).toBool();
GTAV_returnFolder = settings.value("Directory", GTAV_returnFolder).toString();
settings.endGroup();
settings.endGroup();
if (forceDir) {
dir.setPath(GTAV_returnFolder);
if (dir.exists()) {
if (ok)
if (ok != 0)
*ok = true;
qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8());
return dir.absolutePath();
}
}
dir.setPath(GTAV_defaultFolder);
if (dir.exists()) {
if (ok)
if (ok != 0)
*ok = true;
qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8());
return dir.absolutePath();
}
if (!forceDir) {
dir.setPath(GTAV_returnFolder);
if (dir.exists()) {
if (ok)
if (ok != 0)
*ok = true;
qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8());
return dir.absolutePath();
}
}
if (ok)
if (ok != 0)
*ok = false;
return QString();
}
QString AppEnv::getRDR2Folder(bool *ok)
bool AppEnv::setGameFolder(QString gameFolder)
{
QDir dir;
QString RDR2_FOLDER = QString::fromUtf8(qgetenv("RDR2_FOLDER"));
if (!RDR2_FOLDER.isEmpty()) {
dir.setPath(RDR2_FOLDER);
if (dir.exists()) {
if (ok)
*ok = true;
return dir.absolutePath();
}
}
#ifdef Q_OS_UNIX
// TODO: Try to locate the Steam Proton RDR 2 folder
const QString RDR2_defaultFolder = StandardPaths::documentsLocation() % "/Rockstar Games/Red Dead Redemption 2";
#else
const QString RDR2_defaultFolder = StandardPaths::documentsLocation() % "/Rockstar Games/Red Dead Redemption 2";
#endif
QString RDR2_returnFolder = RDR2_defaultFolder;
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
settings.beginGroup("GameDirectory");
settings.beginGroup("RDR 2");
bool forceDir = settings.value("ForceCustom", false).toBool();
RDR2_returnFolder = settings.value("Directory", RDR2_defaultFolder).toString();
settings.endGroup();
settings.endGroup();
if (forceDir) {
dir.setPath(RDR2_returnFolder);
if (dir.exists()) {
if (ok)
*ok = true;
return dir.absolutePath();
}
}
dir.setPath(RDR2_defaultFolder);
dir.setPath(gameFolder);
if (dir.exists()) {
if (ok)
*ok = true;
return dir.absolutePath();
qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8());
return true;
}
if (!forceDir) {
dir.setPath(RDR2_returnFolder);
if (dir.exists()) {
if (ok)
*ok = true;
return dir.absolutePath();
}
}
if (ok)
*ok = false;
return QString();
return false;
}
QString AppEnv::getExLangFolder()
@ -223,7 +167,33 @@ QString AppEnv::getShareFolder()
QByteArray AppEnv::getUserAgent()
{
#if QT_VERSION >= 0x050400
#ifdef Q_OS_WIN
QString kernelVersion = QSysInfo::kernelVersion();
const QStringList &kernelVersionList = kernelVersion.split(".");
if (kernelVersionList.length() > 2) {
kernelVersion = kernelVersionList.at(0) % "." % kernelVersionList.at(1);
}
QString runArch = QSysInfo::buildCpuArchitecture();
if (runArch == "x86_64") {
runArch = "Win64; x64";
}
else if (runArch == "i686") {
const QString &curArch = QSysInfo::currentCpuArchitecture();
if (curArch == "x86_64") {
runArch = "WOW64";
}
else if (curArch == "i686") {
runArch = "Win32; x86";
}
}
return QString("Mozilla/5.0 (Windows NT %1; %2) %3/%4").arg(kernelVersion, runArch, GTA5SYNC_APPSTR, GTA5SYNC_APPVER).toUtf8();
#else
return QString("Mozilla/5.0 (%1; %2) %3/%4").arg(QSysInfo::kernelType(), QSysInfo::kernelVersion(), GTA5SYNC_APPSTR, GTA5SYNC_APPVER).toUtf8();
#endif
#else
return QString("Mozilla/5.0 %1/%2").arg(GTA5SYNC_APPSTR, GTA5SYNC_APPVER).toUtf8();
#endif
}
QUrl AppEnv::getCrewFetchingUrl(QString crewID)
@ -243,7 +213,7 @@ QUrl AppEnv::getPlayerFetchingUrl(QString crewID, int pageNumber)
// Game Stuff
GameVersion AppEnv::getGTAVVersion()
GameVersion AppEnv::getGameVersion()
{
#ifdef Q_OS_WIN
QString argumentValue;
@ -292,7 +262,7 @@ GameVersion AppEnv::getGTAVVersion()
#endif
}
GameLanguage AppEnv::getGTAVLanguage(GameVersion gameVersion)
GameLanguage AppEnv::getGameLanguage(GameVersion gameVersion)
{
if (gameVersion == GameVersion::SocialClubVersion) {
#ifdef Q_OS_WIN
@ -399,11 +369,77 @@ QString AppEnv::gameLanguageToString(GameLanguage gameLanguage)
}
}
bool AppEnv::setGameLanguage(GameVersion gameVersion, GameLanguage gameLanguage)
{
bool socialClubVersion = false;
bool steamVersion = false;
if (gameVersion == GameVersion::SocialClubVersion) {
socialClubVersion = true;
}
else if (gameVersion == GameVersion::SteamVersion) {
steamVersion = true;
}
else if (gameVersion == GameVersion::BothVersions) {
socialClubVersion = true;
steamVersion = true;
}
else {
return false;
}
if (socialClubVersion) {
#ifdef Q_OS_WIN
QString argumentValue;
#ifdef _WIN64
argumentValue = "\\WOW6432Node";
#endif
QSettings registrySettingsSc(QString("HKEY_LOCAL_MACHINE\\SOFTWARE%1\\Rockstar Games\\Grand Theft Auto V").arg(argumentValue), QSettings::NativeFormat);
if (gameLanguage != GameLanguage::Undefined) {
registrySettingsSc.setValue("Language", gameLanguageToString(gameLanguage));
}
else {
registrySettingsSc.remove("Language");
}
registrySettingsSc.sync();
if (registrySettingsSc.status() != QSettings::NoError) {
return false;
}
#else
Q_UNUSED(gameLanguage)
#endif
}
if (steamVersion) {
#ifdef Q_OS_WIN
QString argumentValue;
#ifdef _WIN64
argumentValue = "\\WOW6432Node";
#endif
QSettings registrySettingsSteam(QString("HKEY_LOCAL_MACHINE\\SOFTWARE%1\\Rockstar Games\\Grand Theft Auto V Steam").arg(argumentValue), QSettings::NativeFormat);
if (gameLanguage != GameLanguage::Undefined) {
registrySettingsSteam.setValue("Language", gameLanguageToString(gameLanguage));
}
else {
registrySettingsSteam.remove("Language");
}
registrySettingsSteam.sync();
if (registrySettingsSteam.status() != QSettings::NoError) {
return false;
}
#else
Q_UNUSED(gameLanguage)
#endif
}
return true;
}
// Screen Stuff
qreal AppEnv::screenRatio()
{
#if QT_VERSION >= 0x050000
qreal dpi = QApplication::primaryScreen()->logicalDotsPerInch();
#else
qreal dpi = QApplication::desktop()->logicalDpiX();
#endif
#ifdef Q_OS_MAC
return (dpi / 72);
#else
@ -413,5 +449,9 @@ qreal AppEnv::screenRatio()
qreal AppEnv::screenRatioPR()
{
#if QT_VERSION >= 0x050600
return QApplication::primaryScreen()->devicePixelRatio();
#else
return 1;
#endif
}

View file

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2023 Syping
* Copyright (C) 2016-2021 Syping
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -35,8 +35,8 @@ public:
static QString getBuildCode();
// Folder Stuff
static QString getGTAVFolder(bool *ok = 0);
static QString getRDR2Folder(bool *ok = 0);
static QString getGameFolder(bool *ok = 0);
static bool setGameFolder(QString gameFolder);
static QString getExLangFolder();
static QString getInLangFolder();
static QString getImagesFolder();
@ -50,10 +50,11 @@ public:
static QUrl getPlayerFetchingUrl(QString crewID, int pageNumber);
// Game Stuff
static GameVersion getGTAVVersion();
static GameLanguage getGTAVLanguage(GameVersion gameVersion);
static GameVersion getGameVersion();
static GameLanguage getGameLanguage(GameVersion gameVersion);
static GameLanguage gameLanguageFromString(QString gameLanguage);
static QString gameLanguageToString(GameLanguage gameLanguage);
static bool setGameLanguage(GameVersion gameVersion, GameLanguage gameLanguage);
// Screen Stuff
static qreal screenRatio();

View file

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.7)
project(gta5view VERSION 1.11.0 LANGUAGES C CXX)
project(gta5view LANGUAGES C CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
@ -8,7 +8,7 @@ set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17 CACHE STRING "gta5view C++ standard")
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(FORCE_QT_VERSION "" CACHE STRING "Force Qt Version")
@ -20,9 +20,6 @@ endif()
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Network Svg Widgets REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS LinguistTools QUIET)
set(RAGEPHOTO_STATIC ON CACHE BOOL "Build libragephoto as static library")
add_subdirectory(src/libragephoto)
if(WIN32)
list(APPEND GTA5VIEW_LIBS
dwmapi
@ -41,7 +38,7 @@ if(APPLE)
res/gta5view.icns
)
set(MACOSX_BUNDLE_BUNDLE_NAME gta5view)
set(MACOSX_BUNDLE_BUNDLE_VERSION ${gta5view_VERSION})
set(MACOSX_BUNDLE_BUNDLE_VERSION 1.10.2)
set(MACOSX_BUNDLE_ICON_FILE gta5view.icns)
set(MACOSX_BUNDLE_GUI_IDENTIFIER de.syping.gta5view)
set_source_files_properties(res/gta5view.icns PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
@ -53,107 +50,111 @@ list(APPEND GTA5VIEW_DEFINES
)
set(GTA5VIEW_SOURCES
src/main.cpp
src/AboutDialog.cpp
src/AppEnv.cpp
src/CrewDatabase.cpp
src/DatabaseThread.cpp
src/ExportDialog.cpp
src/ExportThread.cpp
src/GlobalString.cpp
src/IconLoader.cpp
src/ImportDialog.cpp
src/JsonEditorDialog.cpp
src/MapLocationDialog.cpp
src/OptionsDialog.cpp
src/PictureDialog.cpp
src/PictureExport.cpp
src/PictureWidget.cpp
src/PlayerListDialog.cpp
src/ProfileDatabase.cpp
src/ProfileInterface.cpp
src/ProfileLoader.cpp
src/ProfileWidget.cpp
src/SavegameCopy.cpp
src/SavegameData.cpp
src/SavegameDialog.cpp
src/SavegameWidget.cpp
src/SnapmaticEditor.cpp
src/SnapmaticJson.cpp
src/SnapmaticPicture.cpp
src/SnapmaticWidget.cpp
src/StandardPaths.cpp
src/StringParser.cpp
src/TranslationClass.cpp
src/UserInterface.cpp
src/pcg/pcg_basic.c
src/uimod/JSHighlighter.cpp
src/uimod/UiModLabel.cpp
src/uimod/UiModWidget.cpp
main.cpp
AboutDialog.cpp
AppEnv.cpp
CrewDatabase.cpp
DatabaseThread.cpp
ExportDialog.cpp
ExportThread.cpp
GlobalString.cpp
IconLoader.cpp
ImportDialog.cpp
JsonEditorDialog.cpp
MapLocationDialog.cpp
OptionsDialog.cpp
PictureDialog.cpp
PictureExport.cpp
PictureWidget.cpp
PlayerListDialog.cpp
ProfileDatabase.cpp
ProfileInterface.cpp
ProfileLoader.cpp
ProfileWidget.cpp
RagePhoto.cpp
SavegameCopy.cpp
SavegameData.cpp
SavegameDialog.cpp
SavegameWidget.cpp
SidebarGenerator.cpp
SnapmaticEditor.cpp
SnapmaticPicture.cpp
SnapmaticWidget.cpp
StandardPaths.cpp
StringParser.cpp
TranslationClass.cpp
UserInterface.cpp
anpro/imagecropper.cpp
pcg/pcg_basic.c
uimod/JSHighlighter.cpp
uimod/UiModLabel.cpp
uimod/UiModWidget.cpp
)
set(GTA5VIEW_HEADERS
src/config.h
src/AboutDialog.h
src/AppEnv.h
src/CrewDatabase.h
src/DatabaseThread.h
src/ExportDialog.h
src/ExportThread.h
src/GlobalString.h
src/IconLoader.h
src/ImportDialog.h
src/JsonEditorDialog.h
src/MapLocationDialog.h
src/OptionsDialog.h
src/PictureDialog.h
src/PictureExport.h
src/PictureWidget.h
src/PlayerListDialog.h
src/ProfileDatabase.h
src/ProfileInterface.h
src/ProfileLoader.h
src/ProfileWidget.h
src/SavegameCopy.h
src/SavegameData.h
src/SavegameDialog.h
src/SavegameWidget.h
src/SnapmaticEditor.h
src/SnapmaticJson.h
src/SnapmaticPicture.h
src/SnapmaticWidget.h
src/StandardPaths.h
src/StringParser.h
src/TranslationClass.h
src/UserInterface.h
src/pcg/pcg_basic.h
src/uimod/JSHighlighter.h
src/uimod/UiModLabel.h
src/uimod/UiModWidget.h
config.h
wrapper.h
AboutDialog.h
AppEnv.h
CrewDatabase.h
DatabaseThread.h
ExportDialog.h
ExportThread.h
GlobalString.h
IconLoader.h
ImportDialog.h
JsonEditorDialog.h
MapLocationDialog.h
OptionsDialog.h
PictureDialog.h
PictureExport.h
PictureWidget.h
PlayerListDialog.h
ProfileDatabase.h
ProfileInterface.h
ProfileLoader.h
ProfileWidget.h
RagePhoto.h
SavegameCopy.h
SavegameData.h
SavegameDialog.h
SavegameWidget.h
SidebarGenerator.h
SnapmaticEditor.h
SnapmaticPicture.h
SnapmaticWidget.h
StandardPaths.h
StringParser.h
TranslationClass.h
UserInterface.h
anpro/imagecropper.h
pcg/pcg_basic.h
uimod/JSHighlighter.h
uimod/UiModLabel.h
uimod/UiModWidget.h
)
set(GTA5VIEW_INCLUDEDIR
src
src/anpro
src/pcg
src/uimod
anpro
pcg
uimod
)
set(GTA5VIEW_FORMS
src/AboutDialog.ui
src/ExportDialog.ui
src/ImportDialog.ui
src/JsonEditorDialog.ui
src/MapLocationDialog.ui
src/OptionsDialog.ui
src/PictureDialog.ui
src/PlayerListDialog.ui
src/ProfileInterface.ui
src/SavegameDialog.ui
src/SavegameWidget.ui
src/SnapmaticEditor.ui
src/SnapmaticWidget.ui
src/UserInterface.ui
AboutDialog.ui
ExportDialog.ui
ImportDialog.ui
JsonEditorDialog.ui
MapLocationDialog.ui
OptionsDialog.ui
PictureDialog.ui
PlayerListDialog.ui
ProfileInterface.ui
SavegameDialog.ui
SavegameWidget.ui
SnapmaticEditor.ui
SnapmaticWidget.ui
UserInterface.ui
)
set(GTA5VIEW_TRANSLATIONS
@ -168,6 +169,7 @@ set(GTA5VIEW_TRANSLATIONS
list(APPEND GTA5VIEW_RESOURCES
res/global.qrc
res/template.qrc
)
set_property(SOURCE res/global.qrc PROPERTY AUTORCC_OPTIONS "-threshold;0;-compress;9")
@ -216,45 +218,14 @@ if(FLATPAK_BUILD)
)
endif()
option(RAGEPHOTO_ABI_WRAPPER "Use libragephoto's ABI wrapper" OFF)
if(RAGEPHOTO_ABI_WRAPPER)
set(RAGEPHOTO_C_API ON CACHE BOOL "Build libragephoto with C API support" FORCE)
list(APPEND GTA5VIEW_DEFINES
-DRAGEPHOTO_USE_ABI_WRAPPER
)
endif()
option(WITH_BOOST "Use linked Boost library" OFF)
if(WITH_BOOST)
find_package(Boost REQUIRED
COMPONENTS json
)
list(APPEND GTA5VIEW_LIBS
Boost::json
)
list(APPEND GTA5VIEW_DEFINES
-DGTA5SYNC_BOOST
)
list(APPEND GTA5VIEW_INCLUDEDIR
${Boost_INCLUDE_DIRS}
)
else()
list(APPEND GTA5VIEW_DEFINES
-DBOOST_JSON_STANDALONE
)
list(APPEND GTA5VIEW_INCLUDEDIR
src/json/include
)
endif()
option(WITH_DONATE "Donate menu option and donation dialog" OFF)
if(WITH_DONATE)
set(DONATE_ADDRESSES "" CACHE STRING "Donation addresses")
list(APPEND GTA5VIEW_HEADERS
src/anpro/QrCode.h
anpro/QrCode.h
)
list(APPEND GTA5VIEW_SOURCES
src/anpro/QrCode.cpp
anpro/QrCode.cpp
)
list(APPEND GTA5VIEW_DEFINES
-DGTA5SYNC_DONATE
@ -273,10 +244,10 @@ option(WITH_MOTD "Developer message system directed to users" OFF)
if(WITH_MOTD)
set(MOTD_WEBURL "" CACHE STRING "Messages WebURL")
list(APPEND GTA5VIEW_HEADERS
src/MessageThread.h
MessageThread.h
)
list(APPEND GTA5VIEW_SOURCES
src/MessageThread.cpp
MessageThread.cpp
)
list(APPEND GTA5VIEW_DEFINES
-DGTA5SYNC_MOTD
@ -296,15 +267,15 @@ if(WITH_TELEMETRY)
set(TELEMETRY_REGURL "" CACHE STRING "Telemetry RegURL")
set(TELEMETRY_WEBURL "" CACHE STRING "Telemetry WebURL")
list(APPEND GTA5VIEW_HEADERS
src/TelemetryClass.h
src/tmext/TelemetryClassAuthenticator.h
TelemetryClass.h
tmext/TelemetryClassAuthenticator.h
)
list(APPEND GTA5VIEW_SOURCES
src/TelemetryClass.cpp
src/tmext/TelemetryClassAuthenticator.cpp
TelemetryClass.cpp
tmext/TelemetryClassAuthenticator.cpp
)
list(APPEND GTA5VIEW_INCLUDEDIR
src/tmext
tmext
)
list(APPEND GTA5VIEW_DEFINES
-DGTA5SYNC_TELEMETRY
@ -380,7 +351,7 @@ if(LINGUIST_FOUND AND QCONF_BUILD)
endif()
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.16.0")
target_precompile_headers(gta5view PRIVATE src/config.h)
target_precompile_headers(gta5view PRIVATE config.h)
endif()
if(Qt5Core_VERSION VERSION_GREATER_EQUAL "5.14.0")
@ -388,8 +359,8 @@ if(Qt5Core_VERSION VERSION_GREATER_EQUAL "5.14.0")
endif()
target_compile_definitions(gta5view PRIVATE ${GTA5VIEW_DEFINES})
target_include_directories(gta5view PRIVATE ${GTA5VIEW_INCLUDEDIR} ${RAGEPHOTO_INCLUDE_DIRS})
target_link_libraries(gta5view PRIVATE Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Svg Qt${QT_VERSION_MAJOR}::Widgets ragephoto ${GTA5VIEW_LIBS})
target_include_directories(gta5view PRIVATE ${GTA5VIEW_INCLUDEDIR})
target_link_libraries(gta5view PRIVATE Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Svg Qt${QT_VERSION_MAJOR}::Widgets ${GTA5VIEW_LIBS})
install(TARGETS gta5view DESTINATION bin)
install(FILES res/de.syping.gta5view.desktop DESTINATION share/applications)

View file

@ -26,7 +26,6 @@
#include "config.h"
#include <QStringBuilder>
#include <QApplication>
#include <QSaveFile>
#include <QFileInfo>
#include <QFile>
@ -36,56 +35,131 @@
#include <QDesktopWidget>
#endif
ExportThread::ExportThread(QHash<ProfileWidget*,QString> profileMap, QString exportDirectory, bool pictureCopyEnabled, bool pictureExportEnabled, int exportCount, QObject *parent) : QThread(parent),
ExportThread::ExportThread(QMap<ProfileWidget*,QString> profileMap, QString exportDirectory, bool pictureCopyEnabled, bool pictureExportEnabled, int exportCount, QObject *parent) : QThread(parent),
profileMap(profileMap), exportDirectory(exportDirectory), pictureCopyEnabled(pictureCopyEnabled), pictureExportEnabled(pictureExportEnabled), exportCount(exportCount)
{
}
void ExportThread::run()
{
size_t intExportProgress = 0;
for (ProfileWidget *widget : profileMap.keys()) {
if (widget->isSelected()) {
if (widget->getWidgetType() == "SnapmaticWidget") {
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
// Picture Settings
// Quality Settings
settings.beginGroup("Pictures");
int defaultQuality = 100;
QSize defExportSize = SnapmaticPicture::getSnapmaticResolution();
int customQuality = settings.value("CustomQuality", defaultQuality).toInt();
if (customQuality < 1 || customQuality > 100)
{
customQuality = 100;
}
bool useCustomQuality = settings.value("CustomQualityEnabled", false).toBool();
// Size Settings
QSize cusExportSize = settings.value("CustomSize", defExportSize).toSize();
if (cusExportSize.width() > 3840)
{
cusExportSize.setWidth(3840);
}
else if (cusExportSize.height() > 2160)
{
cusExportSize.setHeight(2160);
}
if (cusExportSize.width() < 1)
{
cusExportSize.setWidth(1);
}
else if (cusExportSize.height() < 1)
{
cusExportSize.setHeight(1);
}
QString sizeMode = settings.value("ExportSizeMode", "Default").toString();
Qt::AspectRatioMode aspectRatio = (Qt::AspectRatioMode)settings.value("AspectRatio", Qt::KeepAspectRatio).toInt();
settings.endGroup();
// End Picture Settings
int intExportProgress = 0;
for (ProfileWidget *widget : profileMap.keys())
{
if (widget->isSelected())
{
if (widget->getWidgetType() == "SnapmaticWidget")
{
SnapmaticWidget *picWidget = qobject_cast<SnapmaticWidget*>(widget);
SnapmaticPicture *picture = picWidget->getPicture();
if (pictureExportEnabled) {
if (pictureExportEnabled)
{
QString exportFileName = PictureExport::getPictureFileName(picture);
if (!exportFileName.endsWith(".jpg"))
if (exportFileName.right(4) != ".jpg" && exportFileName.right(4) != ".png")
{
exportFileName += ".jpg";
}
intExportProgress++;
emit exportStringUpdate(ProfileInterface::tr("Export file %1 of %2 files").arg(QString::number(intExportProgress), QString::number(exportCount)));
emit exportProgressUpdate(intExportProgress);
bool isSaved = false;
QSaveFile exportFile(exportDirectory % "/" % exportFileName);
if (exportFile.open(QIODevice::WriteOnly)) {
exportFile.write(picture->getPictureStream());
isSaved = exportFile.commit();
// Scale Picture
QImage exportPicture = picture->getImage();
if (sizeMode == "Desktop")
{
#if QT_VERSION >= 0x050000
qreal screenRatioPR = AppEnv::screenRatioPR();
QRect desktopResolution = QApplication::primaryScreen()->geometry();
int desktopSizeWidth = qRound((double)desktopResolution.width() * screenRatioPR);
int desktopSizeHeight = qRound((double)desktopResolution.height() * screenRatioPR);
#else
QRect desktopResolution = QApplication::desktop()->screenGeometry();
int desktopSizeWidth = desktopResolution.width();
int desktopSizeHeight = desktopResolution.height();
#endif
exportPicture = exportPicture.scaled(desktopSizeWidth, desktopSizeHeight, aspectRatio, Qt::SmoothTransformation);
}
else if (sizeMode == "Custom")
{
exportPicture = exportPicture.scaled(cusExportSize, aspectRatio, Qt::SmoothTransformation);
}
bool isSaved;
if (useCustomQuality)
{
isSaved = exportPicture.save(exportDirectory % "/" % exportFileName, "JPEG", customQuality);
}
else
{
isSaved = exportPicture.save(exportDirectory % "/" % exportFileName, "JPEG", 100);
}
if (!isSaved)
{
failedExportPictures += exportFileName;
}
}
if (pictureCopyEnabled) {
if (pictureCopyEnabled)
{
QString exportFileName = PictureExport::getPictureFileName(picture);
if (!exportFileName.endsWith(".g5e"))
if (exportFileName.right(4) != ".g5e")
{
exportFileName += ".g5e";
}
intExportProgress++;
emit exportStringUpdate(ProfileInterface::tr("Export file %1 of %2 files").arg(QString::number(intExportProgress), QString::number(exportCount)));
emit exportProgressUpdate(intExportProgress);
QString exportFilePath = exportDirectory % "/" % exportFileName;
if (QFile::exists(exportFilePath))
QFile::remove(exportFilePath);
if (QFile::exists(exportFilePath)) {QFile::remove(exportFilePath);}
if (!picture->exportPicture(exportDirectory % "/" % exportFileName, SnapmaticFormat::G5E_Format))
{
failedCopyPictures += exportFileName;
}
}
}
else if (widget->getWidgetType() == "SavegameWidget") {
else if (widget->getWidgetType() == "SavegameWidget")
{
SavegameWidget *sgdWidget = qobject_cast<SavegameWidget*>(widget);
SavegameData *savegame = sgdWidget->getSavegame();
@ -98,10 +172,11 @@ void ExportThread::run()
emit exportProgressUpdate(intExportProgress);
QString exportFilePath = exportDirectory % "/" % exportFileName;
if (QFile::exists(exportFilePath))
QFile::remove(exportFilePath);
if (QFile::exists(exportFilePath)) {QFile::remove(exportFilePath);}
if (!QFile::copy(originalFileName, exportFilePath))
{
failedSavegames += exportFileName;
}
}
}
}

View file

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2023 Syping
* Copyright (C) 2016-2017 Syping
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -29,7 +29,7 @@ class ExportThread : public QThread
{
Q_OBJECT
public:
explicit ExportThread(QHash<ProfileWidget*,QString> profileMap, QString exportDirectory, bool pictureCopyEnabled, bool pictureExportEnabled, int exportCount, QObject *parent = 0);
explicit ExportThread(QMap<ProfileWidget*,QString> profileMap, QString exportDirectory, bool pictureCopyEnabled, bool pictureExportEnabled, int exportCount, QObject *parent = 0);
QStringList getFailedSavegames();
QStringList getFailedCopyPictures();
QStringList getFailedExportPictures();
@ -38,7 +38,7 @@ protected:
void run();
private:
QHash<ProfileWidget*,QString> profileMap;
QMap <ProfileWidget*, QString> profileMap;
QString exportDirectory;
bool pictureCopyEnabled;
bool pictureExportEnabled;

View file

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2017-2023 Syping
* Copyright (C) 2017-2022 Syping
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -18,8 +18,10 @@
#include "ui_ImportDialog.h"
#include "SnapmaticPicture.h"
#include "SidebarGenerator.h"
#include "StandardPaths.h"
#include "ImportDialog.h"
#include "imagecropper.h"
#include "AppEnv.h"
#include "config.h"
#include <QStringBuilder>
@ -47,7 +49,11 @@ ImportDialog::ImportDialog(QString profileName, QWidget *parent) :
ui(new Ui::ImportDialog)
{
// Set Window Flags
#if QT_VERSION >= 0x050900
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
#else
setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint);
#endif
ui->setupUi(this);
ui->cmdOK->setDefault(true);
@ -82,12 +88,12 @@ ImportDialog::ImportDialog(QString profileName, QWidget *parent) :
ui->cmdBackgroundWipe->setVisible(false);
// Snapmatic Resolution
ui->cbResolution->addItem("GTA V (536p)", QSize(960, 536));
ui->cbResolution->addItem("FiveM (1072p)", QSize(1920, 1072));
ui->cbResolution->addItem("RDR 2 (1080p)", QSize(1920, 1080));
ui->cbResolution->addItem("HD (720p)", QSize(1280, 720));
ui->cbResolution->addItem("WQHD (1440p)", QSize(2560, 1440));
ui->cbResolution->addItem("4K/UHD (2160p)", QSize(3840, 2160));
snapmaticResolution = SnapmaticPicture::getSnapmaticResolution();
ui->cbResolution->addItem("GTA V", snapmaticResolution);
ui->cbResolution->addItem("FiveM", QSize(1920, 1072));
ui->cbResolution->addItem("1280x720", QSize(1280, 720));
ui->cbResolution->addItem("1920x1080", QSize(1920, 1080));
ui->cbResolution->addItem("2560x1440", QSize(2560, 1440));
// Set Import Settings
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
@ -119,10 +125,11 @@ ImportDialog::ImportDialog(QString profileName, QWidget *parent) :
#endif
// Options menu
optionsMenu.addAction(tr("&Import new Picture..."), this, &ImportDialog::importNewPicture);
optionsMenu.addAction(tr("&Import new Picture..."), this, SLOT(importNewPicture()));
optionsMenu.addAction(tr("&Crop Picture..."), this, SLOT(cropPicture()));
optionsMenu.addSeparator();
optionsMenu.addAction(tr("&Load Settings..."), this, &ImportDialog::loadImportSettings);
optionsMenu.addAction(tr("&Save Settings..."), this, &ImportDialog::saveImportSettings);
optionsMenu.addAction(tr("&Load Settings..."), this, SLOT(loadImportSettings()));
optionsMenu.addAction(tr("&Save Settings..."), this, SLOT(saveImportSettings()));
ui->cmdOptions->setMenu(&optionsMenu);
const QSize windowSize = sizeHint();
@ -368,7 +375,7 @@ void ImportDialog::processSettings(QString settingsProfile, bool setDefault)
ui->cbForceAvatarColour->setChecked(settings.value("ForceAvatarColour", false).toBool());
ui->cbUnlimited->setChecked(settings.value("UnlimitedBuffer", false).toBool());
ui->cbImportAsIs->setChecked(settings.value("ImportAsIs", false).toBool());
const QVariant data = settings.value("Resolution", QSize(960, 536));
const QVariant data = settings.value("Resolution", SnapmaticPicture::getSnapmaticResolution());
#if QT_VERSION >= 0x060000
if (data.typeId() == QMetaType::QSize)
#else
@ -428,7 +435,7 @@ void ImportDialog::saveSettings(QString settingsProfile)
settings.setValue("Resolution", data);
}
else {
settings.setValue("Resolution", QSize(960, 536));
settings.setValue("Resolution", SnapmaticPicture::getSnapmaticResolution());
}
settings.setValue("UnlimitedBuffer", ui->cbUnlimited->isChecked());
settings.setValue("ImportAsIs", ui->cbImportAsIs->isChecked());
@ -437,6 +444,72 @@ void ImportDialog::saveSettings(QString settingsProfile)
settings.endGroup();
}
void ImportDialog::cropPicture()
{
qreal screenRatio = AppEnv::screenRatio();
QDialog cropDialog(this);
#if QT_VERSION >= 0x050000
cropDialog.setObjectName(QStringLiteral("CropDialog"));
#else
cropDialog.setObjectName(QString::fromUtf8("CropDialog"));
#endif
cropDialog.setWindowTitle(tr("Crop Picture..."));
cropDialog.setWindowFlags(cropDialog.windowFlags()^Qt::WindowContextHelpButtonHint);
cropDialog.setModal(true);
QVBoxLayout cropLayout;
#if QT_VERSION >= 0x050000
cropLayout.setObjectName(QStringLiteral("CropLayout"));
#else
cropLayout.setObjectName(QString::fromUtf8("CropLayout"));
#endif
cropLayout.setContentsMargins(0, 0, 0, 0);
cropLayout.setSpacing(0);
cropDialog.setLayout(&cropLayout);
ImageCropper imageCropper(&cropDialog);
#if QT_VERSION >= 0x050000
imageCropper.setObjectName(QStringLiteral("ImageCropper"));
#else
imageCropper.setObjectName(QString::fromUtf8("ImageCropper"));
#endif
imageCropper.setBackgroundColor(Qt::black);
imageCropper.setCroppingRectBorderColor(QColor(255, 255, 255, 127));
imageCropper.setImage(QPixmap::fromImage(origImage, Qt::AutoColor));
imageCropper.setProportion(QSize(1, 1));
imageCropper.setFixedSize(workImage.size());
cropLayout.addWidget(&imageCropper);
QHBoxLayout buttonLayout;
#if QT_VERSION >= 0x050000
cropLayout.setObjectName(QStringLiteral("ButtonLayout"));
#else
cropLayout.setObjectName(QString::fromUtf8("ButtonLayout"));
#endif
cropLayout.addLayout(&buttonLayout);
QPushButton cropButton(&cropDialog);
#if QT_VERSION >= 0x050000
cropButton.setObjectName(QStringLiteral("CropButton"));
#else
cropButton.setObjectName(QString::fromUtf8("CropButton"));
#endif
cropButton.setMinimumSize(0, 40 * screenRatio);
cropButton.setText(tr("&Crop"));
cropButton.setToolTip(tr("Crop Picture"));
QObject::connect(&cropButton, SIGNAL(clicked(bool)), &cropDialog, SLOT(accept()));
buttonLayout.addWidget(&cropButton);
cropDialog.show();
cropDialog.setFixedSize(cropDialog.sizeHint());
if (cropDialog.exec() == QDialog::Accepted) {
QImage *croppedImage = new QImage(imageCropper.cropImage().toImage());
setImage(croppedImage);
}
}
void ImportDialog::importNewPicture()
{
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
@ -450,7 +523,7 @@ fileDialogPreOpen: //Work?
fileDialog.setViewMode(QFileDialog::Detail);
fileDialog.setAcceptMode(QFileDialog::AcceptOpen);
fileDialog.setOption(QFileDialog::DontUseNativeDialog, dontUseNativeDialog);
fileDialog.setWindowFlag(Qt::WindowContextHelpButtonHint, false);
fileDialog.setWindowFlags(fileDialog.windowFlags()^Qt::WindowContextHelpButtonHint);
fileDialog.setWindowTitle(QApplication::translate("ProfileInterface", "Import..."));
fileDialog.setLabelText(QFileDialog::Accept, QApplication::translate("ProfileInterface", "Import"));
@ -464,6 +537,10 @@ fileDialogPreOpen: //Work?
filters << QApplication::translate("ProfileInterface", "All image files (%1)").arg(imageFormatsStr.trimmed());
filters << QApplication::translate("ProfileInterface", "All files (**)");
fileDialog.setNameFilters(filters);
QList<QUrl> sidebarUrls = SidebarGenerator::generateSidebarUrls(fileDialog.sidebarUrls());
fileDialog.setSidebarUrls(sidebarUrls);
fileDialog.setDirectory(settings.value(profileName % "+Directory", StandardPaths::documentsLocation()).toString());
fileDialog.restoreGeometry(settings.value(profileName % "+Geometry", "").toByteArray());
@ -756,7 +833,7 @@ fileDialogPreOpen:
fileDialog.setViewMode(QFileDialog::Detail);
fileDialog.setAcceptMode(QFileDialog::AcceptOpen);
fileDialog.setOption(QFileDialog::DontUseNativeDialog, dontUseNativeDialog);
fileDialog.setWindowFlag(Qt::WindowContextHelpButtonHint, false);
fileDialog.setWindowFlags(fileDialog.windowFlags()^Qt::WindowContextHelpButtonHint);
fileDialog.setWindowTitle(QApplication::translate("ProfileInterface", "Import..."));
fileDialog.setLabelText(QFileDialog::Accept, QApplication::translate("ProfileInterface", "Import"));
@ -770,6 +847,10 @@ fileDialogPreOpen:
filters << QApplication::translate("ProfileInterface", "All image files (%1)").arg(imageFormatsStr.trimmed());
filters << QApplication::translate("ProfileInterface", "All files (**)");
fileDialog.setNameFilters(filters);
QList<QUrl> sidebarUrls = SidebarGenerator::generateSidebarUrls(fileDialog.sidebarUrls());
fileDialog.setSidebarUrls(sidebarUrls);
fileDialog.setDirectory(settings.value("Directory", StandardPaths::documentsLocation()).toString());
fileDialog.restoreGeometry(settings.value("Geometry", "").toByteArray());
@ -868,7 +949,7 @@ void ImportDialog::on_cbResolution_currentIndexChanged(int index)
#endif
{
const QSize dataSize = data.toSize();
if (dataSize == QSize(960, 536)) {
if (dataSize == SnapmaticPicture::getSnapmaticResolution()) {
ui->cbAvatar->setEnabled(true);
snapmaticResolution = dataSize;
reworkImage();

View file

@ -45,6 +45,7 @@ public:
private slots:
void processImage();
void reworkImage();
void cropPicture();
void importNewPicture();
void loadImportSettings();
void saveImportSettings();

View file

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2017-2023 Syping
* Copyright (C) 2017-2021 Syping
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -19,14 +19,12 @@
#include "JsonEditorDialog.h"
#include "ui_JsonEditorDialog.h"
#include "SnapmaticEditor.h"
#include "SnapmaticJson.h"
#include "AppEnv.h"
#include "config.h"
#include <QStringBuilder>
#include <QJsonDocument>
#include <QJsonObject>
#include <QMessageBox>
#include <QFile>
#if QT_VERSION >= 0x050200
#include <QFontDatabase>
@ -41,8 +39,12 @@ JsonEditorDialog::JsonEditorDialog(SnapmaticPicture *picture, QWidget *parent) :
ui(new Ui::JsonEditorDialog)
{
// Set Window Flags
#if QT_VERSION >= 0x050900
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
setWindowFlag(Qt::WindowMinMaxButtonsHint, true);
#else
setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint^Qt::WindowMinMaxButtonsHint);
#endif
ui->setupUi(this);
ui->cmdClose->setDefault(true);
@ -64,9 +66,16 @@ JsonEditorDialog::JsonEditorDialog(SnapmaticPicture *picture, QWidget *parent) :
ui->cmdSave->setIcon(QIcon::fromTheme("gtk-save"));
}
jsonCode = picture->getJsonStdStr();
jsonCode = picture->getJsonStr();
#if QT_VERSION >= 0x050200
ui->txtJSON->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
#else
QFont jsonFont = ui->txtJSON->font();
jsonFont.setStyleHint(QFont::Monospace);
jsonFont.setFixedPitch(true);
ui->txtJSON->setFont(jsonFont);
#endif
QFontMetrics fontMetrics(ui->txtJSON->font());
#if QT_VERSION >= 0x050B00
ui->txtJSON->setTabStopDistance(fontMetrics.horizontalAdvance(" "));
@ -74,14 +83,9 @@ JsonEditorDialog::JsonEditorDialog(SnapmaticPicture *picture, QWidget *parent) :
ui->txtJSON->setTabStopWidth(fontMetrics.width(" "));
#endif
ui->txtJSON->setStyleSheet("QPlainTextEdit{background-color:rgb(46,47,48);color:rgb(238,231,172);}");
boost::json::error_code ec;
const boost::json::value jsonValue = boost::json::parse(jsonCode, ec);
if (jsonValue.is_object()) {
const boost::json::object jsonObject = jsonValue.get_object();
ui->txtJSON->setPlainText(QString::fromUtf8(SnapmaticJson::serialize(jsonObject, true).c_str()));
}
QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonCode.toUtf8());
ui->txtJSON->setStyleSheet("QPlainTextEdit{background-color: rgb(46, 47, 48); color: rgb(238, 231, 172);}");
ui->txtJSON->setPlainText(QString::fromUtf8(jsonDocument.toJson(QJsonDocument::Indented)).trimmed());
jsonHl = new JSHighlighter(ui->txtJSON->document());
// DPI calculation
@ -100,7 +104,7 @@ JsonEditorDialog::JsonEditorDialog(SnapmaticPicture *picture, QWidget *parent) :
ui->lineJSON->setMaximumHeight(qRound(1 * screenRatio));
ui->lineJSON->setLineWidth(qRound(1 * screenRatio));
}
resize(450 * screenRatio, 560 * screenRatio);
resize(450 * screenRatio, 550 * screenRatio);
}
JsonEditorDialog::~JsonEditorDialog()
@ -111,12 +115,11 @@ JsonEditorDialog::~JsonEditorDialog()
void JsonEditorDialog::closeEvent(QCloseEvent *ev)
{
const QString jsonPatched = QString(ui->txtJSON->toPlainText()).replace("\t", "");
boost::json::error_code ec;
const boost::json::value jsonNew = boost::json::parse(jsonPatched.toUtf8().constData(), ec);
const boost::json::value jsonOriginal = boost::json::parse(jsonCode, ec);
const std::string newCode = SnapmaticJson::serialize(jsonNew);
const std::string originalCode = SnapmaticJson::serialize(jsonOriginal);
QString jsonPatched = QString(ui->txtJSON->toPlainText()).replace("\t", " ");
QJsonDocument jsonNew = QJsonDocument::fromJson(jsonPatched.toUtf8());
QJsonDocument jsonOriginal = QJsonDocument::fromJson(jsonCode.toUtf8());
QString originalCode = QString::fromUtf8(jsonOriginal.toJson(QJsonDocument::Compact));
QString newCode = QString::fromUtf8(jsonNew.toJson(QJsonDocument::Compact));
if (newCode != originalCode) {
QMessageBox::StandardButton button = QMessageBox::warning(this, SnapmaticEditor::tr("Snapmatic Properties"), SnapmaticEditor::tr("<h4>Unsaved changes detected</h4>You want to save the JSON content before you quit?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Cancel);
if (button == QMessageBox::Yes) {
@ -141,13 +144,12 @@ void JsonEditorDialog::closeEvent(QCloseEvent *ev)
bool JsonEditorDialog::saveJsonContent()
{
const QString jsonPatched = QString(ui->txtJSON->toPlainText()).replace("\t", "");
boost::json::error_code ec;
const boost::json::value jsonNew = boost::json::parse(jsonPatched.toUtf8().constData(), ec);
if (jsonNew.is_object()) {
const boost::json::value jsonOriginal = boost::json::parse(jsonCode, ec);
const std::string newCode = SnapmaticJson::serialize(jsonNew);
const std::string originalCode = SnapmaticJson::serialize(jsonOriginal);
QString jsonPatched = QString(ui->txtJSON->toPlainText()).replace("\t", " ");
QJsonDocument jsonNew = QJsonDocument::fromJson(jsonPatched.toUtf8());
if (!jsonNew.isEmpty()) {
QJsonDocument jsonOriginal = QJsonDocument::fromJson(jsonCode.toUtf8());
QString originalCode = QString::fromUtf8(jsonOriginal.toJson(QJsonDocument::Compact));
QString newCode = QString::fromUtf8(jsonNew.toJson(QJsonDocument::Compact));
if (newCode != originalCode) {
QString currentFilePath = smpic->getPictureFilePath();
QString originalFilePath = smpic->getOriginalPictureFilePath();
@ -192,7 +194,7 @@ bool JsonEditorDialog::saveJsonContent()
QJsonDocument jsonDocument;
QJsonObject jsonObject;
jsonObject["Type"] = "JSONEdited";
jsonObject["EditedSize"] = QString::number(smpic->getPictureSize());
jsonObject["EditedSize"] = QString::number(smpic->getContentMaxLength());
#if QT_VERSION >= 0x060000
jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toSecsSinceEpoch());
#else

View file

@ -47,7 +47,7 @@ signals:
void codeUpdated(QString jsonCode);
private:
std::string jsonCode;
QString jsonCode;
JSHighlighter *jsonHl;
SnapmaticPicture *smpic;
Ui::JsonEditorDialog *ui;

View file

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2017-2023 Syping
* Copyright (C) 2017-2021 Syping
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -29,7 +29,11 @@ MapLocationDialog::MapLocationDialog(double x, double y, QWidget *parent) :
ui(new Ui::MapLocationDialog)
{
// Set Window Flags
#if QT_VERSION >= 0x050900
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
#else
setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint);
#endif
ui->setupUi(this);
ui->cmdDone->setVisible(false);

View file

@ -21,6 +21,7 @@
#include "TranslationClass.h"
#include "StandardPaths.h"
#include "UserInterface.h"
#include "wrapper.h"
#include "AppEnv.h"
#include "config.h"
#include <QStringBuilder>
@ -55,15 +56,41 @@ OptionsDialog::OptionsDialog(ProfileDatabase *profileDB, QWidget *parent) :
ui(new Ui::OptionsDialog)
{
// Set Window Flags
#if QT_VERSION >= 0x050900
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
#else
setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint);
#endif
// Setup User Interface
ui->setupUi(this);
ui->tabWidget->setCurrentIndex(0);
ui->labPicCustomRes->setVisible(false);
ui->cmdCancel->setDefault(true);
ui->cmdCancel->setFocus();
#if QT_VERSION >= 0x050000
qreal screenRatioPR = AppEnv::screenRatioPR();
QRect desktopResolution = QApplication::primaryScreen()->geometry();
int desktopSizeWidth = qRound((double)desktopResolution.width() * screenRatioPR);
int desktopSizeHeight = qRound((double)desktopResolution.height() * screenRatioPR);
#else
QRect desktopResolution = QApplication::desktop()->screenGeometry(this);
int desktopSizeWidth = desktopResolution.width();
int desktopSizeHeight = desktopResolution.height();
#endif
aspectRatio = Qt::KeepAspectRatio;
defExportSize = SnapmaticPicture::getSnapmaticResolution();
cusExportSize = defExportSize;
defaultQuality = 100;
customQuality = 100;
contentMode = 0;
settings = new QSettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
percentString = ui->labPicQuality->text();
ui->labPicQuality->setText(percentString.arg(QString::number(defaultQuality)));
ui->rbPicDesktopRes->setText(ui->rbPicDesktopRes->text().arg(QString::number(desktopSizeWidth), QString::number(desktopSizeHeight)));
ui->rbPicDefaultRes->setText(ui->rbPicDefaultRes->text().arg(QString::number(defExportSize.width()), QString::number(defExportSize.height())));
// Set Icon for OK Button
if (QIcon::hasThemeIcon("dialog-ok")) {
@ -86,15 +113,15 @@ OptionsDialog::OptionsDialog(ProfileDatabase *profileDB, QWidget *parent) :
ui->cmdCopyStatsID->setIcon(QIcon::fromTheme("edit-copy"));
}
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
setupTreeWidget();
setupLanguageBox(&settings);
setupRadioButtons(&settings);
setupDefaultProfile(&settings);
setupCustomGameFolder(&settings);
setupInterfaceSettings(&settings);
setupStatisticsSettings(&settings);
setupSnapmaticPictureViewer(&settings);
setupLanguageBox();
setupRadioButtons();
setupDefaultProfile();
setupPictureSettings();
setupCustomGTAFolder();
setupInterfaceSettings();
setupStatisticsSettings();
setupSnapmaticPictureViewer();
setupWindowsGameSettings();
#ifndef Q_QS_ANDROID
@ -110,6 +137,7 @@ OptionsDialog::OptionsDialog(ProfileDatabase *profileDB, QWidget *parent) :
OptionsDialog::~OptionsDialog()
{
delete settings;
qDeleteAll(playerItems.begin(), playerItems.end());
playerItems.clear();
delete ui;
@ -141,7 +169,7 @@ void OptionsDialog::setupTreeWidget()
}
}
void OptionsDialog::setupLanguageBox(QSettings *settings)
void OptionsDialog::setupLanguageBox()
{
settings->beginGroup("Interface");
currentLanguage = settings->value("Language", "System").toString();
@ -152,7 +180,7 @@ void OptionsDialog::setupLanguageBox(QSettings *settings)
"System in context of System default"));
#ifdef Q_OS_WIN
QString cbAutoStr;
if (AppEnv::getGTAVLanguage(AppEnv::getGTAVVersion()) != GameLanguage::Undefined) {
if (AppEnv::getGameLanguage(AppEnv::getGameVersion()) != GameLanguage::Undefined) {
cbAutoStr = tr("%1 (Game language)", "Next closest language compared to the Game settings").arg(tr("Auto", "Automatic language choice."));
}
else {
@ -185,7 +213,12 @@ void OptionsDialog::setupLanguageBox(QSettings *settings)
ui->cbLanguage->addItem(cbLangStr, lang);
}
if (currentLanguage == lang) {
#if QT_VERSION >= 0x050000
ui->cbLanguage->setCurrentText(cbLangStr);
#else
int indexOfLang = ui->cbLanguage->findText(cbLangStr);
ui->cbLanguage->setCurrentIndex(indexOfLang);
#endif
}
}
@ -237,7 +270,7 @@ void OptionsDialog::setupLanguageBox(QSettings *settings)
ui->labCurrentAreaLanguage->setText(tr("Current: %1").arg(currentLocale.nativeLanguageName() % " (" % currentLocale.nativeCountryName() % ") [" % aCurrentAreaLanguage % "]"));
}
void OptionsDialog::setupRadioButtons(QSettings *settings)
void OptionsDialog::setupRadioButtons()
{
bool contentModeOk;
settings->beginGroup("Profile");
@ -269,7 +302,7 @@ void OptionsDialog::setupRadioButtons(QSettings *settings)
}
}
void OptionsDialog::setupInterfaceSettings(QSettings *settings)
void OptionsDialog::setupInterfaceSettings()
{
settings->beginGroup("Startup");
const QString currentStyle = QApplication::style()->objectName();
@ -317,25 +350,24 @@ void OptionsDialog::on_cmdOK_clicked()
void OptionsDialog::applySettings()
{
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
settings.beginGroup("Interface");
settings->beginGroup("Interface");
#if QT_VERSION >= 0x050000
settings.setValue("Language", ui->cbLanguage->currentData());
settings.setValue("AreaLanguage", ui->cbAreaLanguage->currentData());
settings->setValue("Language", ui->cbLanguage->currentData());
settings->setValue("AreaLanguage", ui->cbAreaLanguage->currentData());
#else
settings->setValue("Language", ui->cbLanguage->itemData(ui->cbLanguage->currentIndex()));
settings->setValue("AreaLanguage", ui->cbAreaLanguage->itemData(ui->cbAreaLanguage->currentIndex()));
#endif
#ifdef Q_OS_WIN
#if QT_VERSION >= 0x050200
settings.setValue("NavigationBar", ui->cbSnapmaticNavigationBar->isChecked());
settings->setValue("NavigationBar", ui->cbSnapmaticNavigationBar->isChecked());
#endif
#else
settings.setValue("NavigationBar", ui->cbSnapmaticNavigationBar->isChecked());
settings->setValue("NavigationBar", ui->cbSnapmaticNavigationBar->isChecked());
#endif
settings.endGroup();
settings->endGroup();
settings.beginGroup("Profile");
settings->beginGroup("Profile");
int newContentMode = 20;
if (ui->rbModern->isChecked()) {
newContentMode = 20;
@ -346,76 +378,66 @@ void OptionsDialog::applySettings()
if (ui->cbDoubleclick->isChecked()) {
newContentMode++;
}
settings.setValue("ContentMode", newContentMode);
#if QT_VERSION >= 0x060000
if (ui->cbProfiles->currentData().typeId() == QMetaType::QString)
settings->setValue("ContentMode", newContentMode);
#if QT_VERSION >= 0x050000
settings->setValue("Default", ui->cbProfiles->currentData());
#else
if (ui->cbProfiles->currentData().type() == QVariant::String)
settings->setValue("Default", ui->cbProfiles->itemData(ui->cbProfiles->currentIndex()));
#endif
{
settings.setValue("Default", ui->cbProfiles->currentData());
settings.remove("DefaultGame");
}
#if QT_VERSION >= 0x060000
if (ui->cbProfiles->currentData().typeId() == QMetaType::QStringList)
#else
if (ui->cbProfiles->currentData().type() == QVariant::StringList)
#endif
{
const QStringList dataList = ui->cbProfiles->currentData().toStringList();
if (dataList.length() == 2) {
settings.setValue("Default", dataList.at(0));
settings.setValue("DefaultGame", dataList.at(1));
}
else {
settings.setValue("Default", QVariant());
settings.remove("DefaultGame");
}
}
settings.endGroup();
settings->endGroup();
const bool forceCustomFolder = ui->cbForceCustomFolder->isChecked();
const bool forceCustomFolder_RDR2 = ui->cbForceCustomFolder_RDR2->isChecked();
settings.beginGroup("GameDirectory");
settings.beginGroup("GTA V");
settings.setValue("Directory", ui->txtFolder->text());
settings.setValue("ForceCustom", forceCustomFolder);
settings.endGroup();
settings.beginGroup("RDR 2");
settings.setValue("Directory", ui->txtFolder_RDR2->text());
settings.setValue("ForceCustom", forceCustomFolder_RDR2);
settings.endGroup();
settings.endGroup();
settings->beginGroup("Pictures");
if (ui->cbPicCustomQuality->isChecked()) {
settings->setValue("CustomQuality", ui->hsPicQuality->value());
}
settings->setValue("CustomQualityEnabled", ui->cbPicCustomQuality->isChecked());
QString sizeMode = "Default";
if (ui->rbPicDesktopRes->isChecked()) {
sizeMode = "Desktop";
}
else if (ui->rbPicCustomRes->isChecked()) {
sizeMode = "Custom";
settings->setValue("CustomSize", QSize(ui->sbPicExportWidth->value(), ui->sbPicExportHeight->value()));
}
settings->setValue("ExportSizeMode", sizeMode);
settings->setValue("AspectRatio", aspectRatio);
settings->endGroup();
const bool defaultStyle = ui->cbDefaultStyle->isChecked();
settings.beginGroup("Startup");
bool forceCustomFolder = ui->cbForceCustomFolder->isChecked();
settings->beginGroup("dir");
settings->setValue("dir", ui->txtFolder->text());
settings->setValue("force", forceCustomFolder);
settings->endGroup();
bool defaultStyle = ui->cbDefaultStyle->isChecked();
settings->beginGroup("Startup");
if (!defaultStyle) {
QString newStyle = ui->cbStyleList->currentText();
settings.setValue("CustomStyle", true);
settings.setValue("AppStyle", newStyle);
settings->setValue("CustomStyle", true);
settings->setValue("AppStyle", newStyle);
QApplication::setStyle(QStyleFactory::create(newStyle));
}
else {
settings.setValue("CustomStyle", false);
settings->setValue("CustomStyle", false);
}
const bool defaultFont = ui->cbDefaultFont->isChecked();
bool defaultFont = ui->cbDefaultFont->isChecked();
if (!defaultFont) {
QFont newFont = ui->cbFont->currentFont();
settings.setValue("CustomFont", true);
settings.setValue("AppFont", newFont);
settings->setValue("CustomFont", true);
settings->setValue("AppFont", newFont);
QApplication::setFont(newFont);
}
else {
settings.setValue("CustomFont", false);
settings->setValue("CustomFont", false);
}
settings.endGroup();
settings->endGroup();
#ifdef GTA5SYNC_TELEMETRY
settings.beginGroup("Telemetry");
settings.setValue("PushAppConf", ui->cbAppConfigStats->isChecked());
settings.setValue("PushUsageData", ui->cbUsageData->isChecked());
if (!Telemetry->isStateForced()) { settings.setValue("IsEnabled", ui->cbParticipateStats->isChecked()); }
settings.endGroup();
settings->beginGroup("Telemetry");
settings->setValue("PushAppConf", ui->cbAppConfigStats->isChecked());
settings->setValue("PushUsageData", ui->cbUsageData->isChecked());
if (!Telemetry->isStateForced()) { settings->setValue("IsEnabled", ui->cbParticipateStats->isChecked()); }
settings->endGroup();
Telemetry->refresh();
Telemetry->work();
if (ui->cbUsageData->isChecked() && Telemetry->canPush()) {
@ -432,8 +454,13 @@ void OptionsDialog::applySettings()
}
#endif
const bool languageChanged = ui->cbLanguage->currentData().toString() != currentLanguage;
const bool languageAreaChanged = ui->cbAreaLanguage->currentData().toString() != currentAreaLanguage;
#if QT_VERSION >= 0x050000
bool languageChanged = ui->cbLanguage->currentData().toString() != currentLanguage;
bool languageAreaChanged = ui->cbAreaLanguage->currentData().toString() != currentAreaLanguage;
#else
bool languageChanged = ui->cbLanguage->itemData(ui->cbLanguage->currentIndex()).toString() != currentLanguage;
bool languageAreaChanged = ui->cbAreaLanguage->itemData(ui->cbLanguage->currentIndex()).toString() != currentAreaLanguage;
#endif
if (languageChanged) {
Translator->unloadTranslation(qApp);
Translator->initUserLanguage();
@ -443,36 +470,109 @@ void OptionsDialog::applySettings()
Translator->initUserLanguage();
}
settings.sync();
settings->sync();
emit settingsApplied(newContentMode, languageChanged);
if ((forceCustomFolder && ui->txtFolder->text() != currentCFolder) || (forceCustomFolder != currentFFolder && forceCustomFolder) || (forceCustomFolder_RDR2 && ui->txtFolder_RDR2->text() != currentCFolderR) || (forceCustomFolder_RDR2 != currentFFolderR && forceCustomFolder_RDR2)) {
if ((forceCustomFolder && ui->txtFolder->text() != currentCFolder) || (forceCustomFolder != currentFFolder && forceCustomFolder)) {
QMessageBox::information(this, tr("%1", "%1").arg(GTA5SYNC_APPSTR), tr("The new Custom Folder will initialise after you restart %1.").arg(GTA5SYNC_APPSTR));
}
}
void OptionsDialog::setupDefaultProfile(QSettings *settings)
void OptionsDialog::setupDefaultProfile()
{
settings->beginGroup("Profile");
defaultProfile = settings->value("Default", QString()).toString();
defaultGame = settings->value("DefaultGame", QStringLiteral("GTA V")).toString();
defaultProfile = settings->value("Default", "").toString();
settings->endGroup();
QString cbNoneStr = tr("No Profile", "No Profile, as default");
ui->cbProfiles->addItem(cbNoneStr, QString());
ui->cbProfiles->addItem(cbNoneStr, "");
}
void OptionsDialog::commitProfiles(const QStringList &profiles, const QString &game)
void OptionsDialog::commitProfiles(const QStringList &profiles)
{
for (const QString &profile : profiles) {
ui->cbProfiles->addItem(tr("%2: %1").arg(profile, game), QStringList() << profile << game);
if (defaultGame == game && defaultProfile == profile) {
ui->cbProfiles->setCurrentText(tr("%2: %1").arg(profile, game));
ui->cbProfiles->addItem(tr("Profile: %1").arg(profile), profile);
if (defaultProfile == profile) {
#if QT_VERSION >= 0x050000
ui->cbProfiles->setCurrentText(tr("Profile: %1").arg(profile));
#else
int indexOfProfile = ui->cbProfiles->findText(tr("Profile: %1").arg(profile));
ui->cbProfiles->setCurrentIndex(indexOfProfile);
#endif
}
}
}
void OptionsDialog::setupStatisticsSettings(QSettings *settings)
void OptionsDialog::on_rbPicCustomRes_toggled(bool checked)
{
ui->labPicCustomRes->setEnabled(checked);
ui->sbPicExportWidth->setEnabled(checked);
ui->sbPicExportHeight->setEnabled(checked);
ui->labPicXDescription->setEnabled(checked);
}
void OptionsDialog::on_cbPicCustomQuality_toggled(bool checked)
{
ui->hsPicQuality->setEnabled(checked);
ui->labPicQuality->setEnabled(checked);
ui->labPicQualityDescription->setEnabled(checked);
}
void OptionsDialog::on_hsPicQuality_valueChanged(int value)
{
customQuality = value;
ui->labPicQuality->setText(percentString.arg(QString::number(value)));
}
void OptionsDialog::setupPictureSettings()
{
settings->beginGroup("Pictures");
// Quality Settings
customQuality = settings->value("CustomQuality", defaultQuality).toInt();
if (customQuality < 1 || customQuality > 100) {
customQuality = 100;
}
ui->hsPicQuality->setValue(customQuality);
ui->cbPicCustomQuality->setChecked(settings->value("CustomQualityEnabled", false).toBool());
// Size Settings
cusExportSize = settings->value("CustomSize", defExportSize).toSize();
if (cusExportSize.width() > 3840) {
cusExportSize.setWidth(3840);
}
else if (cusExportSize.height() > 2160) {
cusExportSize.setHeight(2160);
}
if (cusExportSize.width() < 1) {
cusExportSize.setWidth(1);
}
else if (cusExportSize.height() < 1) {
cusExportSize.setHeight(1);
}
ui->sbPicExportWidth->setValue(cusExportSize.width());
ui->sbPicExportHeight->setValue(cusExportSize.height());
QString sizeMode = settings->value("ExportSizeMode", "Default").toString();
if (sizeMode == "Desktop") {
ui->rbPicDesktopRes->setChecked(true);
}
else if (sizeMode == "Custom") {
ui->rbPicCustomRes->setChecked(true);
}
else {
ui->rbPicDefaultRes->setChecked(true);
}
aspectRatio = (Qt::AspectRatioMode)settings->value("AspectRatio", Qt::KeepAspectRatio).toInt();
if (aspectRatio == Qt::IgnoreAspectRatio) {
ui->cbIgnoreAspectRatio->setChecked(true);
}
settings->endGroup();
}
void OptionsDialog::setupStatisticsSettings()
{
#ifdef GTA5SYNC_TELEMETRY
ui->cbParticipateStats->setText(tr("Participate in %1 User Statistics").arg(GTA5SYNC_APPSTR));
@ -558,36 +658,32 @@ void OptionsDialog::setupWindowsGameSettings()
#endif
}
void OptionsDialog::setupCustomGameFolder(QSettings *settings)
void OptionsDialog::on_cbIgnoreAspectRatio_toggled(bool checked)
{
bool ok_GTAV, ok_RDR2;
const QString defaultGameFolder = AppEnv::getGTAVFolder(&ok_GTAV);
const QString defaultGameFolderR = AppEnv::getRDR2Folder(&ok_RDR2);
settings->beginGroup("dir");
currentCFolder = settings->value("dir", QString()).toString();
currentFFolder = settings->value("force", false).toBool();
settings->endGroup();
settings->beginGroup("GameDirectory");
settings->beginGroup("GTA V");
currentCFolder = settings->value("Directory", currentCFolder).toString();
currentFFolder = settings->value("ForceCustom", currentFFolder).toBool();
settings->endGroup();
settings->beginGroup("RDR 2");
currentCFolderR = settings->value("Directory", QString()).toString();
currentFFolderR = settings->value("ForceCustom", false).toBool();
settings->endGroup();
settings->endGroup();
if (currentCFolder.isEmpty() && ok_GTAV)
currentCFolder = defaultGameFolder;
if (currentCFolderR.isEmpty() && ok_RDR2)
currentCFolderR = defaultGameFolderR;
ui->txtFolder->setText(currentCFolder);
ui->cbForceCustomFolder->setChecked(currentFFolder);
ui->txtFolder_RDR2->setText(currentCFolderR);
ui->cbForceCustomFolder_RDR2->setChecked(currentFFolderR);
if (checked) {
aspectRatio = Qt::IgnoreAspectRatio;
}
else {
aspectRatio = Qt::KeepAspectRatio;
}
}
void OptionsDialog::setupSnapmaticPictureViewer(QSettings *settings)
void OptionsDialog::setupCustomGTAFolder()
{
bool ok;
QString defaultGameFolder = AppEnv::getGameFolder(&ok);
settings->beginGroup("dir");
currentCFolder = settings->value("dir", "").toString();
currentFFolder = settings->value("force", false).toBool();
if (currentCFolder == "" && ok) {
currentCFolder = defaultGameFolder;
}
ui->txtFolder->setText(currentCFolder);
ui->cbForceCustomFolder->setChecked(currentFFolder);
settings->endGroup();
}
void OptionsDialog::setupSnapmaticPictureViewer()
{
#ifdef Q_OS_WIN
#if QT_VERSION >= 0x050200
@ -608,19 +704,11 @@ void OptionsDialog::setupSnapmaticPictureViewer(QSettings *settings)
void OptionsDialog::on_cmdExploreFolder_clicked()
{
const QString GTAV_Folder = QFileDialog::getExistingDirectory(this, UserInterface::tr("Select GTA V Folder..."), StandardPaths::documentsLocation(), QFileDialog::ShowDirsOnly);
if (!GTAV_Folder.isEmpty() && QDir(GTAV_Folder).exists()) {
if (QDir(GTAV_Folder).exists()) {
ui->txtFolder->setText(GTAV_Folder);
}
}
void OptionsDialog::on_cmdExploreFolder_RDR2_clicked()
{
const QString RDR2_Folder = QFileDialog::getExistingDirectory(this, UserInterface::tr("Select RDR 2 Folder..."), StandardPaths::documentsLocation(), QFileDialog::ShowDirsOnly);
if (!RDR2_Folder.isEmpty() && QDir(RDR2_Folder).exists()) {
ui->txtFolder_RDR2->setText(RDR2_Folder);
}
}
void OptionsDialog::on_cbDefaultStyle_toggled(bool checked)
{
ui->cbStyleList->setDisabled(checked);

View file

@ -36,13 +36,16 @@ class OptionsDialog : public QDialog
public:
explicit OptionsDialog(ProfileDatabase *profileDB, QWidget *parent = 0);
void commitProfiles(const QStringList &profiles, const QString &game);
void commitProfiles(const QStringList &profiles);
~OptionsDialog();
private slots:
void on_cmdOK_clicked();
void on_rbPicCustomRes_toggled(bool checked);
void on_cbPicCustomQuality_toggled(bool checked);
void on_hsPicQuality_valueChanged(int value);
void on_cbIgnoreAspectRatio_toggled(bool checked);
void on_cmdExploreFolder_clicked();
void on_cmdExploreFolder_RDR2_clicked();
void on_cbDefaultStyle_toggled(bool checked);
void on_cbDefaultFont_toggled(bool checked);
void on_cmdCopyStatsID_clicked();
@ -60,22 +63,25 @@ private:
QString currentAreaLanguage;
QString currentLanguage;
QString currentCFolder;
QString currentCFolderR;
QString defaultProfile;
QString defaultGame;
QString percentString;
QSettings *settings;
bool withoutPlayers;
bool currentFFolder;
bool currentFFolderR;
int contentMode;
int customQuality;
int defaultQuality;
QSize defExportSize;
QSize cusExportSize;
void setupTreeWidget();
void setupLanguageBox(QSettings *settings);
void setupRadioButtons(QSettings *settings);
void setupDefaultProfile(QSettings *settings);
void setupCustomGameFolder(QSettings *settings);
void setupInterfaceSettings(QSettings *settings);
void setupStatisticsSettings(QSettings *settings);
void setupSnapmaticPictureViewer(QSettings *settings);
void setupLanguageBox();
void setupRadioButtons();
void setupDefaultProfile();
void setupPictureSettings();
void setupCustomGTAFolder();
void setupInterfaceSettings();
void setupStatisticsSettings();
void setupSnapmaticPictureViewer();
void setupWindowsGameSettings();
void applySettings();
};

View file

@ -102,36 +102,6 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gbDefaultFolder_RDR2">
<property name="title">
<string>Custom RDR 2 Folder</string>
</property>
<layout class="QVBoxLayout" name="vlCustomRDR2Folder">
<item>
<widget class="QCheckBox" name="cbForceCustomFolder_RDR2">
<property name="text">
<string>Force using Custom Folder</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="hlDefaultFolder_RDR2">
<item>
<widget class="QLineEdit" name="txtFolder_RDR2"/>
</item>
<item>
<widget class="QToolButton" name="cmdExploreFolder_RDR2">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="vsProfile">
<property name="orientation">
@ -152,6 +122,181 @@
<string>Pictures</string>
</attribute>
<layout class="QVBoxLayout" name="vlTabPictures">
<item>
<widget class="QGroupBox" name="gbPicResolution">
<property name="title">
<string>Export Size</string>
</property>
<layout class="QVBoxLayout" name="vlGbPicRes">
<item>
<widget class="QRadioButton" name="rbPicDefaultRes">
<property name="text">
<string>Default: %1x%2</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rbPicDesktopRes">
<property name="text">
<string>Screen Resolution: %1x%2</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="hlCustomRes">
<item>
<widget class="QRadioButton" name="rbPicCustomRes">
<property name="text">
<string>Custom Size:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labPicCustomRes">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Custom Size:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="sbPicExportWidth">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>3840</number>
</property>
<property name="value">
<number>960</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labPicXDescription">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>x</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="sbPicExportHeight">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>2160</number>
</property>
<property name="value">
<number>536</number>
</property>
</widget>
</item>
<item>
<spacer name="hsPicCustomSize">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="hlAspectRatio">
<item>
<widget class="QCheckBox" name="cbIgnoreAspectRatio">
<property name="text">
<string>Ignore Aspect Ratio</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gbPicQuality">
<property name="title">
<string>Export Quality</string>
</property>
<layout class="QVBoxLayout" name="vlHlPicQuality">
<item>
<widget class="QCheckBox" name="cbPicCustomQuality">
<property name="text">
<string>Enable Custom Quality</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="hlPicQuality">
<item>
<widget class="QLabel" name="labPicQualityDescription">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Quality:</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="hsPicQuality">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labPicQuality">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>%1%</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gbSnapmaticPictureViewer">
<property name="title">

View file

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2023 Syping
* Copyright (C) 2016-2021 Syping
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -20,9 +20,11 @@
#include "PictureWidget.h"
#include "ProfileDatabase.h"
#include "ui_PictureDialog.h"
#include "SidebarGenerator.h"
#include "MapLocationDialog.h"
#include "JsonEditorDialog.h"
#include "SnapmaticEditor.h"
#include "StandardPaths.h"
#include "PictureExport.h"
#include "ImportDialog.h"
#include "StringParser.h"
@ -84,7 +86,7 @@
#define crewID QString::number(picture->getSnapmaticProperties().crewID)
#define picArea picture->getSnapmaticProperties().location.area
#define picPath picture->getPictureFilePath()
#define picTitl picture->getPictureTitle().toHtmlEscaped()
#define picTitl StringParser::escapeString(picture->getPictureTitle())
#define plyrsList picture->getSnapmaticProperties().playersList
#if QT_VERSION >= 0x060000
#define created QLocale().toString(picture->getSnapmaticProperties().createdDateTime, QLocale::ShortFormat)
@ -125,8 +127,21 @@ PictureDialog::PictureDialog(bool primaryWindow, ProfileDatabase *profileDB, Cre
void PictureDialog::setupPictureDialog()
{
// Set Window Flags
#if QT_VERSION >= 0x050900
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
setWindowFlag(Qt::CustomizeWindowHint, true);
#else
setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint^Qt::CustomizeWindowHint);
#endif
#ifdef Q_OS_LINUX
// for stupid Window Manager like GNOME
#if QT_VERSION >= 0x050900
setWindowFlag(Qt::Dialog, false);
setWindowFlag(Qt::Window, true);
#else
setWindowFlags(windowFlags()^Qt::Dialog^Qt::Window);
#endif
#endif
// Setup User Interface
ui->setupUi(this);
@ -141,8 +156,8 @@ void PictureDialog::setupPictureDialog()
smpic = nullptr;
crewStr = "";
// Set default Snapmatic resolution (960x536)
const QSize snapmaticResolution = QSize(960, 536);
// Get Snapmatic Resolution
const QSize snapmaticResolution = SnapmaticPicture::getSnapmaticResolution();
// Avatar area
qreal screenRatio = AppEnv::screenRatio();
@ -168,26 +183,27 @@ void PictureDialog::setupPictureDialog()
// Manage menu
manageMenu = new QMenu(this);
manageMenu->addAction(tr("Export as &Picture..."), this, &PictureDialog::exportSnapmaticPicture);
exportPhotoAction = manageMenu->addAction(tr("Export as &RAGE Photo..."), this, &PictureDialog::copySnapmaticPicture);
manageMenu->addAction(tr("Export as &Picture..."), this, SLOT(exportSnapmaticPicture()));
manageMenu->addAction(tr("Export as &Snapmatic..."), this, SLOT(copySnapmaticPicture()));
manageMenu->addSeparator();
manageMenu->addAction(tr("&Edit Properties..."), this, &PictureDialog::editSnapmaticProperties);
manageMenu->addAction(tr("&Overwrite Image..."), this, &PictureDialog::editSnapmaticImage);
manageMenu->addAction(tr("&Edit Properties..."), this, SLOT(editSnapmaticProperties()));
manageMenu->addAction(tr("&Overwrite Image..."), this, SLOT(editSnapmaticImage()));
manageMenu->addSeparator();
openViewerAction = manageMenu->addAction(tr("Open &Map Viewer..."), this, &PictureDialog::openPreviewMap);
openViewerAction->setEnabled(false);
QAction *openViewerAction = manageMenu->addAction(tr("Open &Map Viewer..."), this, SLOT(openPreviewMap()));
openViewerAction->setShortcut(Qt::Key_M);
manageMenu->addAction(tr("Open &JSON Editor..."), this, &PictureDialog::editSnapmaticRawJson);
manageMenu->addAction(tr("Open &JSON Editor..."), this, SLOT(editSnapmaticRawJson()));
ui->cmdManage->setMenu(manageMenu);
// Global map
globalMap = GlobalString::getGlobalMap();
// Set Icon for Close Button
if (QIcon::hasThemeIcon("dialog-close"))
if (QIcon::hasThemeIcon("dialog-close")) {
ui->cmdClose->setIcon(QIcon::fromTheme("dialog-close"));
else if (QIcon::hasThemeIcon("gtk-close"))
}
else if (QIcon::hasThemeIcon("gtk-close")) {
ui->cmdClose->setIcon(QIcon::fromTheme("gtk-close"));
}
installEventFilter(this);
@ -218,10 +234,17 @@ void PictureDialog::closeEvent(QCloseEvent *ev)
void PictureDialog::addPreviousNextButtons()
{
QToolBar *uiToolbar = new QToolBar("Picture Toolbar", this);
#if QT_VERSION < 0x050600
qreal screenRatio = AppEnv::screenRatio();
if (screenRatio != 1) {
QSize iconSize = uiToolbar->iconSize();
uiToolbar->setIconSize(QSize(iconSize.width() * screenRatio, iconSize.height() * screenRatio));
}
#endif
uiToolbar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
uiToolbar->setObjectName("UiToolbar");
uiToolbar->addAction(QIcon(AppEnv::getImagesFolder() % "/back.svgz"), "", this, &PictureDialog::previousPictureRequestedSlot);
uiToolbar->addAction(QIcon(AppEnv::getImagesFolder() % "/next.svgz"), "", this, &PictureDialog::nextPictureRequestedSlot);
uiToolbar->addAction(QIcon(AppEnv::getImagesFolder() % "/back.svgz"), "", this, SLOT(previousPictureRequestedSlot()));
uiToolbar->addAction(QIcon(AppEnv::getImagesFolder() % "/next.svgz"), "", this, SLOT(nextPictureRequestedSlot()));
#ifdef Q_OS_MAC
#if QT_VERSION >= 0x050000
uiToolbar->setStyle(QStyleFactory::create("Fusion"));
@ -233,7 +256,7 @@ void PictureDialog::addPreviousNextButtons()
void PictureDialog::adaptDialogSize()
{
int newDialogHeight = (536 * AppEnv::screenRatio()) + ui->jsonFrame->heightForWidth(width());
int newDialogHeight = (SnapmaticPicture::getSnapmaticResolution().height() * AppEnv::screenRatio()) + ui->jsonFrame->heightForWidth(width());
if (naviEnabled)
newDialogHeight = newDialogHeight + layout()->menuBar()->height();
const QSize windowSize(width(), newDialogHeight);
@ -250,8 +273,9 @@ void PictureDialog::styliseDialog()
MARGINS margins = {0, 0, qRound(layout()->menuBar()->height() * AppEnv::screenRatioPR()), 0};
HRESULT hr = S_OK;
hr = DwmExtendFrameIntoClientArea(reinterpret_cast<HWND>(winId()), &margins);
if (SUCCEEDED(hr))
if (SUCCEEDED(hr)) {
setStyleSheet("PictureDialog{background:transparent}");
}
}
else {
MARGINS margins = {0, 0, 0, 0};
@ -259,8 +283,9 @@ void PictureDialog::styliseDialog()
bool colorOk = false;
QSettings dwmRegistry("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\DWM", QSettings::NativeFormat);
QRgb color = dwmRegistry.value("ColorizationColor").toUInt(&colorOk);
if (colorOk)
if (colorOk) {
setStyleSheet(QString("PictureDialog{background:%1}").arg(QColor::fromRgba(color).name()));
}
else {
HRESULT hr = S_OK;
BOOL isOpaqueBlend;
@ -288,8 +313,9 @@ bool PictureDialog::nativeEvent(const QByteArray &eventType, void *message, long
#endif
{
MSG *msg = reinterpret_cast<MSG*>(message);
if (msg->message == 0x031e || msg->message == 0x0320)
if (msg->message == 0x031e || msg->message == 0x0320) {
styliseDialog();
}
return QWidget::nativeEvent(eventType, message, result);
}
#endif
@ -333,23 +359,23 @@ bool PictureDialog::eventFilter(QObject *obj, QEvent *ev)
case Qt::Key_2:
if (overlayEnabled) {
overlayEnabled = false;
if (!previewMode)
renderPicture();
if (!previewMode) renderPicture();
}
else {
overlayEnabled = true;
if (!previewMode)
renderPicture();
if (!previewMode) renderPicture();
}
break;
case Qt::Key_M:
openPreviewMap();
returnValue = true;
break;
#if QT_VERSION >= 0x050300
case Qt::Key_Exit:
ui->cmdClose->click();
returnValue = true;
break;
#endif
case Qt::Key_Enter: case Qt::Key_Return:
on_labPicture_mouseDoubleClicked(Qt::LeftButton);
returnValue = true;
@ -361,6 +387,7 @@ bool PictureDialog::eventFilter(QObject *obj, QEvent *ev)
}
}
#ifdef Q_OS_WIN
#if QT_VERSION >= 0x050000
if (obj != ui->labPicture && naviEnabled) {
if (ev->type() == QEvent::MouseButtonPress) {
QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent*>(ev);
@ -374,8 +401,9 @@ bool PictureDialog::eventFilter(QObject *obj, QEvent *ev)
if (ev->type() == QEvent::MouseButtonRelease) {
QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent*>(ev);
if (mouseEvent->pos().y() <= layout()->menuBar()->height()) {
if (mouseEvent->button() == Qt::LeftButton)
if (mouseEvent->button() == Qt::LeftButton) {
dragStart = false;
}
}
}
if (dragStart && ev->type() == QEvent::MouseMove) {
@ -389,6 +417,7 @@ bool PictureDialog::eventFilter(QObject *obj, QEvent *ev)
}
}
}
#endif
#endif
}
return returnValue;
@ -444,8 +473,9 @@ void PictureDialog::renderOverlayPicture()
preferedRect.setHeight(71 * screenRatio * screenRatioPR);
overlaySpace.setHeight(80 * screenRatio * screenRatioPR);
}
else
else {
overlaySpace.setHeight(overlaySpace.height() + 6 * screenRatio * screenRatioPR);
}
QImage overlayImage(overlaySpace.size(), QImage::Format_ARGB32_Premultiplied);
overlayImage.fill(Qt::transparent);
@ -456,10 +486,12 @@ void PictureDialog::renderOverlayPicture()
overlayPainter.drawText(preferedRect, Qt::AlignLeft | hOverlay | Qt::TextDontClip | Qt::TextWordWrap, overlayText);
overlayPainter.end();
if (overlaySpace.width() < 194 * screenRatio * screenRatioPR)
if (overlaySpace.width() < 194 * screenRatio * screenRatioPR) {
overlaySpace.setWidth(200 * screenRatio * screenRatioPR);
else
}
else {
overlaySpace.setWidth(overlaySpace.width() + 6 * screenRatio * screenRatioPR);
}
QImage overlayBorderImage(overlaySpace.width(), overlaySpace.height(), QImage::Format_ARGB6666_Premultiplied);
overlayBorderImage.fill(QColor(15, 15, 15, 162));
@ -475,8 +507,8 @@ void PictureDialog::renderOverlayPicture()
void PictureDialog::setSnapmaticPicture(SnapmaticPicture *picture, bool readOk, bool _indexed, int _index)
{
if (smpic != nullptr) {
QObject::disconnect(smpic, &SnapmaticPicture::updated, this, &PictureDialog::updated);
QObject::disconnect(smpic, &SnapmaticPicture::customSignal, this, &PictureDialog::customSignal);
QObject::disconnect(smpic, SIGNAL(updated()), this, SLOT(updated()));
QObject::disconnect(smpic, SIGNAL(customSignal(QString)), this, SLOT(customSignal(QString)));
}
snapmaticPicture = QImage();
indexed = _indexed;
@ -486,45 +518,29 @@ void PictureDialog::setSnapmaticPicture(SnapmaticPicture *picture, bool readOk,
QMessageBox::warning(this, tr("Snapmatic Picture Viewer"), tr("Failed at %1").arg(picture->getLastStep()));
return;
}
setWindowTitle(windowTitleStr.arg(picture->getPictureTitle()));
if (picture->isPicOk()) {
snapmaticPicture = picture->getImage();
renderPicture();
ui->cmdManage->setEnabled(true);
if (smpic->getSnapmaticFormat() == SnapmaticFormat::PGTA5_Format)
exportPhotoAction->setText(tr("Export as &GTA V Snapmatic..."));
else if (smpic->getSnapmaticFormat() == SnapmaticFormat::G5E_Format)
exportPhotoAction->setText(tr("Export as &GTA V Snapmatic..."));
else if (smpic->getSnapmaticFormat() == SnapmaticFormat::PRDR3_Format)
exportPhotoAction->setText(tr("Export as &RDR 2 Photo..."));
else
exportPhotoAction->setText(tr("Export as &RAGE Photo..."));
}
else {
exportPhotoAction->setText(tr("Export as &RAGE Photo..."));
}
if (picture->isJsonOk()) {
crewStr = crewDB->getCrewName(crewID);
if (globalMap.contains(picArea))
if (globalMap.contains(picArea)) {
picAreaStr = globalMap.value(picArea);
else
}
else {
picAreaStr = picArea;
}
setWindowTitle(windowTitleStr.arg(picTitl));
ui->labJSON->setText(jsonDrawString.arg(locX, locY, locZ, generatePlayersString(), generateCrewString(), picTitl, picAreaStr, created));
if (smpic->getSnapmaticFormat() == SnapmaticFormat::PGTA5_Format)
openViewerAction->setEnabled(true);
else if (smpic->getSnapmaticFormat() == SnapmaticFormat::G5E_Format)
openViewerAction->setEnabled(true);
else
openViewerAction->setEnabled(false);
QTimer::singleShot(0, this, &PictureDialog::adaptDialogSize);
QTimer::singleShot(0, this, SLOT(adaptDialogSize()));
}
else {
ui->labJSON->setText(jsonDrawString.arg("0", "0", "0", tr("No Players"), tr("No Crew"), tr("Unknown Location")));
openViewerAction->setEnabled(false);
QTimer::singleShot(0, this, &PictureDialog::adaptDialogSize);
QTimer::singleShot(0, this, SLOT(adaptDialogSize()));
}
QObject::connect(smpic, &SnapmaticPicture::updated, this, &PictureDialog::updated);
QObject::connect(smpic, &SnapmaticPicture::customSignal, this, &PictureDialog::customSignal);
QObject::connect(smpic, SIGNAL(updated()), this, SLOT(updated()));
QObject::connect(smpic, SIGNAL(customSignal(QString)), this, SLOT(customSignal(QString)));
emit newPictureCommited(snapmaticPicture);
}
@ -552,18 +568,21 @@ void PictureDialog::renderPicture()
{
const qreal screenRatio = AppEnv::screenRatio();
const qreal screenRatioPR = AppEnv::screenRatioPR();
const QSize snapmaticResolution = QSize(960, 536);
const QSize snapmaticResolution(SnapmaticPicture::getSnapmaticResolution());
const QSize renderResolution(snapmaticResolution.width() * screenRatio * screenRatioPR, snapmaticResolution.height() * screenRatio * screenRatioPR);
QPixmap shownImagePixmap(renderResolution);
shownImagePixmap.fill(Qt::black);
QPainter shownImagePainter(&shownImagePixmap);
const QImage renderImage = snapmaticPicture.scaled(renderResolution, Qt::KeepAspectRatio, Qt::SmoothTransformation);
if (renderImage.width() < renderResolution.width())
if (renderImage.width() < renderResolution.width()) {
shownImagePainter.drawImage((renderResolution.width() - renderImage.width()) / 2, 0, renderImage, Qt::AutoColor);
else if (renderImage.height() < renderResolution.height())
}
else if (renderImage.height() < renderResolution.height()) {
shownImagePainter.drawImage(0, (renderResolution.height() - renderImage.height()) / 2, renderImage, Qt::AutoColor);
else
}
else {
shownImagePainter.drawImage(0, 0, renderImage, Qt::AutoColor);
}
if (previewMode) {
QFont shownImagePainterFont;
shownImagePainterFont.setPixelSize(12 * screenRatio * screenRatioPR);
@ -572,10 +591,13 @@ void PictureDialog::renderPicture()
shownImagePainter.setFont(shownImagePainterFont);
shownImagePainter.drawText(QRect(3 * screenRatio * screenRatioPR, 3 * screenRatio * screenRatioPR, 140 * screenRatio * screenRatioPR, snapmaticResolution.height() * screenRatio * screenRatioPR), Qt::AlignLeft | Qt::TextWordWrap, tr("Avatar Preview Mode\nPress 1 for Default View"));
}
else if (overlayEnabled)
else if (overlayEnabled) {
shownImagePainter.drawImage(3 * screenRatio * screenRatioPR, 3 * screenRatio * screenRatioPR, overlayTempImage, Qt::AutoColor);
}
shownImagePainter.end();
#if QT_VERSION >= 0x050600
shownImagePixmap.setDevicePixelRatio(screenRatioPR);
#endif
ui->labPicture->setPixmap(shownImagePixmap);
}
@ -586,7 +608,7 @@ void PictureDialog::crewNameUpdated()
if (crewIDStr == crewStr) {
crewStr = crewDB->getCrewName(crewIDStr);
ui->labJSON->setText(jsonDrawString.arg(locX, locY, locZ, generatePlayersString(), generateCrewString(), picTitl, picAreaStr, created));
QTimer::singleShot(0, this, &PictureDialog::adaptDialogSize);
QTimer::singleShot(0, this, SLOT(adaptDialogSize()));
}
}
@ -595,7 +617,7 @@ void PictureDialog::playerNameUpdated()
SnapmaticPicture *picture = smpic; // used by macro
if (plyrsList.count() >= 1) {
ui->labJSON->setText(jsonDrawString.arg(locX, locY, locZ, generatePlayersString(), generateCrewString(), picTitl, picAreaStr, created));
QTimer::singleShot(0, this, &PictureDialog::adaptDialogSize);
QTimer::singleShot(0, this, SLOT(adaptDialogSize()));
}
}
@ -604,10 +626,12 @@ QString PictureDialog::generateCrewString()
SnapmaticPicture *picture = smpic; // used by macro
const QString crewIDStr = crewID; // save operation time
if (crewIDStr != "0" && !crewIDStr.isEmpty()) {
if (crewIDStr != crewStr)
if (crewIDStr != crewStr) {
return QString("<a href=\"https://socialclub.rockstargames.com/crew/" % QString(crewStr).replace(" ", "_") % "/" % crewIDStr % "\">" % crewStr % "</a>");
else
}
else {
return QString(crewIDStr);
}
}
return tr("No Crew");
}
@ -637,10 +661,12 @@ QString PictureDialog::generatePlayersString()
void PictureDialog::exportSnapmaticPicture()
{
if (rqFullscreen && fullscreenWidget != nullptr)
if (rqFullscreen && fullscreenWidget != nullptr) {
PictureExport::exportAsPicture(fullscreenWidget, smpic);
else
}
else {
PictureExport::exportAsPicture(this, smpic);
}
}
void PictureDialog::copySnapmaticPicture()
@ -661,28 +687,35 @@ void PictureDialog::on_labPicture_mouseDoubleClicked(Qt::MouseButton button)
#else
QRect desktopRect = QApplication::desktop()->screenGeometry(this);
#endif
PictureWidget pictureWidget(this);
pictureWidget.setObjectName("PictureWidget");
pictureWidget.setWindowFlag(Qt::FramelessWindowHint, true);
pictureWidget.setWindowFlag(Qt::MaximizeUsingFullscreenGeometryHint, true);
pictureWidget.setWindowTitle(windowTitle());
pictureWidget.setStyleSheet("QLabel#pictureLabel{background-color:black;}");
pictureWidget.setImage(smpic->getImage(), desktopRect);
pictureWidget.setModal(true);
PictureWidget *pictureWidget = new PictureWidget(this); // Work!
pictureWidget->setObjectName("PictureWidget");
#if QT_VERSION >= 0x050900
pictureWidget->setWindowFlag(Qt::FramelessWindowHint, true);
pictureWidget->setWindowFlag(Qt::MaximizeUsingFullscreenGeometryHint, true);
#elif QT_VERSION >= 0x050600
pictureWidget->setWindowFlags(pictureWidget->windowFlags()^Qt::FramelessWindowHint^Qt::MaximizeUsingFullscreenGeometryHint);
#else
pictureWidget->setWindowFlags(pictureWidget->windowFlags()^Qt::FramelessWindowHint);
#endif
pictureWidget->setWindowTitle(windowTitle());
pictureWidget->setStyleSheet("QLabel#pictureLabel{background-color:black;}");
pictureWidget->setImage(smpic->getImage(), desktopRect);
pictureWidget->setModal(true);
fullscreenWidget = &pictureWidget;
QObject::connect(this, &PictureDialog::newPictureCommited, &pictureWidget, QOverload<QImage>::of(&PictureWidget::setImage));
QObject::connect(&pictureWidget, &PictureWidget::nextPictureRequested, this, &PictureDialog::dialogNextPictureRequested);
QObject::connect(&pictureWidget, &PictureWidget::previousPictureRequested, this, &PictureDialog::dialogPreviousPictureRequested);
fullscreenWidget = pictureWidget;
QObject::connect(this, SIGNAL(newPictureCommited(QImage)), pictureWidget, SLOT(setImage(QImage)));
QObject::connect(pictureWidget, SIGNAL(nextPictureRequested()), this, SLOT(dialogNextPictureRequested()));
QObject::connect(pictureWidget, SIGNAL(previousPictureRequested()), this, SLOT(dialogPreviousPictureRequested()));
pictureWidget.move(desktopRect.x(), desktopRect.y());
pictureWidget.resize(desktopRect.width(), desktopRect.height());
pictureWidget.showFullScreen();
pictureWidget.setFocus();
pictureWidget.raise();
pictureWidget.exec();
pictureWidget->move(desktopRect.x(), desktopRect.y());
pictureWidget->resize(desktopRect.width(), desktopRect.height());
pictureWidget->showFullScreen();
pictureWidget->setFocus();
pictureWidget->raise();
pictureWidget->exec();
fullscreenWidget = nullptr;
fullscreenWidget = nullptr; // Work!
delete pictureWidget; // Work!
}
}
@ -752,7 +785,7 @@ void PictureDialog::openPreviewMap()
QJsonObject jsonObject;
jsonObject["Type"] = "LocationEdited";
jsonObject["ExtraFlags"] = "Viewer";
jsonObject["EditedSize"] = QString::number(picture->getPictureSize());
jsonObject["EditedSize"] = QString::number(picture->getContentMaxLength());
#if QT_VERSION >= 0x060000
jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toSecsSinceEpoch());
#else
@ -780,7 +813,11 @@ void PictureDialog::editSnapmaticProperties()
snapmaticEditor->setWindowIcon(windowIcon());
snapmaticEditor->setSnapmaticPicture(picture);
snapmaticEditor->setModal(true);
#ifndef Q_OS_ANDROID
snapmaticEditor->show();
#else
snapmaticEditor->showMaximized();
#endif
snapmaticEditor->exec();
delete snapmaticEditor;
}
@ -801,7 +838,6 @@ void PictureDialog::editSnapmaticImage()
importDialog->setModal(true);
importDialog->exec();
if (importDialog->isImportAgreed()) {
const QSize previousSize = smpic->getPictureResolution();
const QByteArray previousPicture = smpic->getPictureStream();
bool success = smpic->setImage(importDialog->image(), importDialog->isUnlimitedBuffer());
if (success) {
@ -812,7 +848,7 @@ void PictureDialog::editSnapmaticImage()
QFile::copy(currentFilePath, backupFileName);
}
if (!smpic->exportPicture(currentFilePath)) {
smpic->setPictureStream(previousPicture, previousSize.width(), previousSize.height());
smpic->setPictureStream(previousPicture);
QMessageBox::warning(this, QApplication::translate("ImageEditorDialog", "Snapmatic Image Editor"), QApplication::translate("ImageEditorDialog", "Patching of Snapmatic Image failed because of I/O Error"));
return;
}
@ -827,7 +863,7 @@ void PictureDialog::editSnapmaticImage()
QJsonObject jsonObject;
jsonObject["Type"] = "ImageEdited";
jsonObject["ExtraFlags"] = "Viewer";
jsonObject["EditedSize"] = QString::number(smpic->getPictureSize());
jsonObject["EditedSize"] = QString::number(smpic->getContentMaxLength());
#if QT_VERSION >= 0x060000
jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toSecsSinceEpoch());
#else
@ -858,7 +894,11 @@ void PictureDialog::editSnapmaticRawJson()
}
jsonEditor->setWindowIcon(windowIcon());
jsonEditor->setModal(true);
#ifndef Q_OS_ANDROID
jsonEditor->show();
#else
jsonEditor->showMaximized();
#endif
jsonEditor->exec();
delete jsonEditor;
}
@ -867,19 +907,21 @@ void PictureDialog::updated()
{
SnapmaticPicture *picture = smpic; // used by macro
crewStr = crewDB->getCrewName(crewID);
if (globalMap.contains(picArea))
if (globalMap.contains(picArea)) {
picAreaStr = globalMap[picArea];
else
}
else {
picAreaStr = picArea;
}
setWindowTitle(windowTitleStr.arg(picTitl));
ui->labJSON->setText(jsonDrawString.arg(locX, locY, locZ, generatePlayersString(), generateCrewString(), picTitl, picAreaStr, created));
QTimer::singleShot(0, this, &PictureDialog::adaptDialogSize);
QTimer::singleShot(0, this, SLOT(adaptDialogSize()));
}
void PictureDialog::customSignal(QString signal)
{
SnapmaticPicture *picture = smpic; // used by macro
if (signal == QStringLiteral("PictureUpdated")) {
if (signal == "PictureUpdated") {
snapmaticPicture = picture->getImage();
renderPicture();
}

View file

@ -124,8 +124,6 @@ private:
int avatarLocX;
int avatarLocY;
int avatarSize;
QAction *exportPhotoAction;
QAction *openViewerAction;
QMenu *manageMenu;
#ifdef Q_OS_WIN
#if QT_VERSION >= 0x050000

View file

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2023 Syping
* Copyright (C) 2016-2020 Syping
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -21,14 +21,22 @@
#include "PictureExport.h"
#include "PictureDialog.h"
#include "StandardPaths.h"
#include "SidebarGenerator.h"
#include <QStringBuilder>
#include <QApplication>
#include <QMessageBox>
#include <QFileDialog>
#include <QSettings>
#include <QDebug>
#if QT_VERSION < 0x050000
#include <QDesktopWidget>
#endif
#if QT_VERSION >= 0x050000
#include <QSaveFile>
#include <QScreen>
#include <QDebug>
#endif
PictureExport::PictureExport()
{
@ -41,6 +49,30 @@ void PictureExport::exportAsPicture(QWidget *parent, SnapmaticPicture *picture)
// Picture Settings
// Quality Settings
settings.beginGroup("Pictures");
int defaultQuality = 100;
QSize defExportSize = SnapmaticPicture::getSnapmaticResolution();
int customQuality = settings.value("CustomQuality", defaultQuality).toInt();
if (customQuality < 1 || customQuality > 100) {
customQuality = 100;
}
bool useCustomQuality = settings.value("CustomQualityEnabled", false).toBool();
// Size Settings
QSize cusExportSize = settings.value("CustomSize", defExportSize).toSize();
if (cusExportSize.width() > 3840) {
cusExportSize.setWidth(3840);
}
else if (cusExportSize.height() > 2160) {
cusExportSize.setHeight(2160);
}
if (cusExportSize.width() < 1) {
cusExportSize.setWidth(1);
}
else if (cusExportSize.height() < 1) {
cusExportSize.setHeight(1);
}
QString sizeMode = settings.value("ExportSizeMode", "Default").toString();
Qt::AspectRatioMode aspectRatio = (Qt::AspectRatioMode)settings.value("AspectRatio", Qt::KeepAspectRatio).toInt();
QString defaultExportFormat = settings.value("DefaultExportFormat", ".jpg").toString();
settings.endGroup();
// End Picture Settings
@ -57,7 +89,7 @@ fileDialogPreSave: //Work?
fileDialog.setOption(QFileDialog::DontUseNativeDialog, dontUseNativeDialog);
fileDialog.setOption(QFileDialog::DontConfirmOverwrite, true);
fileDialog.setDefaultSuffix("suffix");
fileDialog.setWindowFlag(Qt::WindowContextHelpButtonHint, false);
fileDialog.setWindowFlags(fileDialog.windowFlags()^Qt::WindowContextHelpButtonHint);
fileDialog.setWindowTitle(PictureDialog::tr("Export as Picture..."));
fileDialog.setLabelText(QFileDialog::Accept, PictureDialog::tr("Export"));
@ -65,6 +97,10 @@ fileDialogPreSave: //Work?
filters << PictureDialog::tr("JPEG Graphics (*.jpg *.jpeg)");
filters << PictureDialog::tr("Portable Network Graphics (*.png)");
fileDialog.setNameFilters(filters);
QList<QUrl> sidebarUrls = SidebarGenerator::generateSidebarUrls(fileDialog.sidebarUrls());
fileDialog.setSidebarUrls(sidebarUrls);
fileDialog.setDirectory(settings.value("Directory", StandardPaths::picturesLocation()).toString());
fileDialog.restoreGeometry(settings.value(parent->objectName() % "+Geometry", "").toByteArray());
@ -77,13 +113,16 @@ fileDialogPreSave: //Work?
QString saveFileFormat;
QString selectedFile = selectedFiles.at(0);
if (selectedFile.endsWith(".jpg") || selectedFile.endsWith(".jpeg")) {
if (selectedFile.right(4) == ".jpg") {
saveFileFormat = "JPEG";
}
else if (selectedFile.endsWith(".png")) {
else if (selectedFile.right(4) == ".jpeg") {
saveFileFormat = "JPEG";
}
else if (selectedFile.right(4) == ".png") {
saveFileFormat = "PNG";
}
else if (selectedFile.endsWith(".suffix")) {
else if (selectedFile.right(7) == ".suffix") {
if (fileDialog.selectedNameFilter() == "JPEG picture (*.jpg)") {
selectedFile.replace(".suffix", ".jpg");
}
@ -101,23 +140,49 @@ fileDialogPreSave: //Work?
}
}
// Scale Picture
QImage exportPicture = picture->getImage();
if (sizeMode == "Desktop") {
#if QT_VERSION >= 0x050000
qreal screenRatioPR = AppEnv::screenRatioPR();
QRect desktopResolution = QApplication::primaryScreen()->geometry();
int desktopSizeWidth = qRound((double)desktopResolution.width() * screenRatioPR);
int desktopSizeHeight = qRound((double)desktopResolution.height() * screenRatioPR);
#else
QRect desktopResolution = QApplication::desktop()->screenGeometry();
int desktopSizeWidth = desktopResolution.width();
int desktopSizeHeight = desktopResolution.height();
#endif
exportPicture = exportPicture.scaled(desktopSizeWidth, desktopSizeHeight, aspectRatio, Qt::SmoothTransformation);
}
else if (sizeMode == "Custom") {
exportPicture = exportPicture.scaled(cusExportSize, aspectRatio, Qt::SmoothTransformation);
}
int errorId = 0;
bool isSaved = false;
QSaveFile picFile(selectedFile);
if (picFile.open(QIODevice::WriteOnly)) {
isSaved = exportPicture.save(&picFile, saveFileFormat.toStdString().c_str(), 100);
#if QT_VERSION >= 0x050000
QSaveFile *picFile = new QSaveFile(selectedFile);
#else
QFile *picFile = new QFile(selectedFile);
#endif
if (picFile->open(QIODevice::WriteOnly)) {
isSaved = exportPicture.save(picFile, saveFileFormat.toStdString().c_str(), useCustomQuality ? customQuality : defaultQuality);
#if QT_VERSION >= 0x050000
if (isSaved) {
isSaved = picFile.commit();
isSaved = picFile->commit();
}
else {
errorId = 1;
}
#else
picFile->close();
#endif
}
else {
errorId = 2;
}
delete picFile;
if (!isSaved) {
switch (errorId) {
@ -166,7 +231,7 @@ fileDialogPreSave: //Work?
fileDialog.setOption(QFileDialog::DontUseNativeDialog, dontUseNativeDialog);
fileDialog.setOption(QFileDialog::DontConfirmOverwrite, true);
fileDialog.setDefaultSuffix(".rem");
fileDialog.setWindowFlag(Qt::WindowContextHelpButtonHint, false);
fileDialog.setWindowFlags(fileDialog.windowFlags()^Qt::WindowContextHelpButtonHint);
fileDialog.setWindowTitle(PictureDialog::tr("Export as Snapmatic..."));
fileDialog.setLabelText(QFileDialog::Accept, PictureDialog::tr("Export"));
@ -177,6 +242,10 @@ fileDialogPreSave: //Work?
#endif
filters << PictureDialog::tr("Snapmatic pictures (PGTA*)");
fileDialog.setNameFilters(filters);
QList<QUrl> sidebarUrls = SidebarGenerator::generateSidebarUrls(fileDialog.sidebarUrls());
fileDialog.setSidebarUrls(sidebarUrls);
fileDialog.setDirectory(settings.value("Directory", StandardPaths::documentsLocation()).toString());
fileDialog.restoreGeometry(settings.value(parent->objectName() % "+Geometry", "").toByteArray());
fileDialog.selectFile(QString(picture->getExportPictureFileName() % ".g5e"));
@ -187,14 +256,14 @@ fileDialogPreSave: //Work?
QString selectedFile = selectedFiles.at(0);
bool isAutoExt = false;
#ifndef GTA5SYNC_FLATPAK
if (selectedFile.endsWith(".auto")) {
if (selectedFile.right(5) == ".auto") {
isAutoExt = true;
QString dirPath = QFileInfo(selectedFile).dir().path();
QString stockFileName = sgdFileInfo.fileName();
selectedFile = dirPath % "/" % stockFileName;
}
#endif
if (selectedFile.endsWith(".rem")) {
if (selectedFile.right(4) == ".rem") {
selectedFile.remove(selectedFile.length() - 4, 4);
}
@ -204,7 +273,7 @@ fileDialogPreSave: //Work?
}
}
if (selectedFile.endsWith(".g5e")) {
if (selectedFile.right(4) == ".g5e") {
bool isExported = picture->exportPicture(selectedFile, SnapmaticFormat::G5E_Format);
if (!isExported) {
QMessageBox::warning(parent, PictureDialog::tr("Export as Snapmatic"), PictureDialog::tr("Failed to export current Snapmatic picture"));
@ -212,7 +281,7 @@ fileDialogPreSave: //Work?
}
}
else {
bool isCopied = picture->exportPicture(selectedFile, SnapmaticFormat::PGTA5_Format);
bool isCopied = picture->exportPicture(selectedFile, SnapmaticFormat::PGTA_Format);
if (!isCopied) {
QMessageBox::warning(parent, PictureDialog::tr("Export as Snapmatic"), PictureDialog::tr("Failed to export current Snapmatic picture"));
goto fileDialogPreSave; //Work?

View file

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2023 Syping
* Copyright (C) 2016-2021 Syping
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -18,6 +18,7 @@
#include "PlayerListDialog.h"
#include "ui_PlayerListDialog.h"
#include "wrapper.h"
#include "AppEnv.h"
#include <QStringBuilder>
#include <QFontMetrics>
@ -32,7 +33,11 @@ PlayerListDialog::PlayerListDialog(QStringList players, ProfileDatabase *profile
ui(new Ui::PlayerListDialog)
{
// Set Window Flags
#if QT_VERSION >= 0x050900
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
#else
setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint);
#endif
listUpdated = false;
ui->setupUi(this);

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2023 Syping
* Copyright (C) 2016-2021 Syping
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -24,6 +24,7 @@
#include "ProfileDatabase.h"
#include "DatabaseThread.h"
#include "SavegameWidget.h"
#include "ProfileLoader.h"
#include "ProfileWidget.h"
#include "ExportThread.h"
#include "SavegameData.h"
@ -48,7 +49,7 @@ class ProfileInterface : public QWidget
Q_OBJECT
public:
explicit ProfileInterface(ProfileDatabase *profileDB, CrewDatabase *crewDB, DatabaseThread *threadDB, QWidget *parent = 0);
void setProfileFolder(QString folder, QString profile, quint32 defaultPhotoFormat);
void setProfileFolder(QString folder, QString profile);
void settingsApplied(int contentMode, bool languageChanged);
void setupProfileInterface();
void massTool(MassTool tool);
@ -89,8 +90,10 @@ private slots:
void dialogNextPictureRequested(QWidget *dialog);
void dialogPreviousPictureRequested(QWidget *dialog);
void on_saProfileContent_dropped(const QMimeData *mimeData);
#if QT_VERSION >= 0x050000
void directoryChanged(const QString &path);
void directoryScanned(QVector<QString> savegameFiles, QVector<QString> snapmaticPics);
#endif
protected:
bool eventFilter(QObject *watched, QEvent *event);
@ -101,13 +104,16 @@ private:
DatabaseThread *threadDB;
Ui::ProfileInterface *ui;
ProfileLoader *profileLoader;
ProfileWidget *previousWidget;
QList<SavegameData*> savegames;
QList<SnapmaticPicture*> pictures;
QHash<ProfileWidget*,QString> widgets;
QMap<ProfileWidget*,QString> widgets;
#if QT_VERSION >= 0x050000
QFileSystemWatcher fileSystemWatcher;
QVector<QString> savegameFiles;
QVector<QString> snapmaticPics;
#endif
QSpacerItem *saSpacerItem;
QStringList fixedPictures;
QString enabledPicStr;
@ -115,7 +121,6 @@ private:
QString profileName;
QString loadingStr;
QString language;
quint32 photoFormat;
pcg32_random_t rng;
bool contextMenuOpened;
bool isProfileLoaded;
@ -129,6 +134,7 @@ private:
bool importImage(QImage *snapmaticImage, QDateTime importDateTime);
bool importFilesProgress(QStringList selectedFiles);
bool importSnapmaticPicture(SnapmaticPicture *picture, bool warn = true);
bool importSavegameData(SavegameData *savegame, QString sgdPath, bool warn = true);
void pictureLoaded(SnapmaticPicture *picture, bool inserted);
void savegameLoaded(SavegameData *savegame, QString savegamePath, bool inserted);
void savegameDeleted(SavegameWidget *sgdWidget, bool isRemoteEmited = false);
@ -137,7 +143,7 @@ private:
void insertSnapmaticIPI(QWidget *widget);
void insertSavegameIPI(QWidget *widget);
void sortingProfileInterface();
quint32 getRandomUid();
int getRandomUid();
signals:
void profileLoaded();

View file

@ -20,6 +20,7 @@
#include "ProfileLoader.h"
#include "SavegameData.h"
#include "CrewDatabase.h"
#include "wrapper.h"
#include <QStringBuilder>
#include <QVector>
#include <QString>
@ -28,9 +29,9 @@
#include <QDir>
#include <QList>
#else
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include "sys/types.h"
#include "sys/stat.h"
#include "dirent.h"
#endif
ProfileLoader::ProfileLoader(QString profileFolder, CrewDatabase *crewDB, QObject *parent) : QThread(parent), profileFolder(profileFolder), crewDB(crewDB)
@ -45,18 +46,40 @@ void ProfileLoader::run()
QVector<QString> savegameFiles;
QVector<QString> snapmaticPics;
#ifdef Q_OS_WIN
QDir dir(profileFolder);
const QStringList files = dir.entryList(QDir::Files);
for (const QString &fileName : files) {
if ((fileName.startsWith("SGTA5") || fileName.startsWith("SRDR3")) && !fileName.endsWith(".bak")) {
if (fileName.startsWith("SGTA5") && !fileName.endsWith(".bak")) {
savegameFiles << fileName;
maximumV++;
}
if ((fileName.startsWith("PGTA5") || fileName.startsWith("PRDR3")) && !fileName.endsWith(".bak")) {
if (fileName.startsWith("PGTA5") && !fileName.endsWith(".bak")) {
snapmaticPics << fileName;
maximumV++;
}
}
#else
DIR *dirp = opendir(profileFolder.toUtf8().constData());
struct dirent *dp;
while ((dp = readdir(dirp)) != 0) {
const QString fileName = QString::fromUtf8(dp->d_name);
const QString filePath = profileFolder % "/" % fileName;
struct stat fileStat;
stat(filePath.toUtf8().constData(), &fileStat);
if (S_ISREG(fileStat.st_mode) != 0) {
if (fileName.startsWith("SGTA5") && !fileName.endsWith(".bak")) {
savegameFiles << fileName;
maximumV++;
}
if (fileName.startsWith("PGTA5") && !fileName.endsWith(".bak")) {
snapmaticPics << fileName;
maximumV++;
}
}
}
closedir(dirp);
#endif
// Directory successfully scanned
emit directoryScanned(savegameFiles, snapmaticPics);
@ -78,8 +101,8 @@ void ProfileLoader::run()
SnapmaticPicture *picture = new SnapmaticPicture(picturePath);
if (picture->readingPicture(true)) {
if (picture->isFormatSwitched()) {
picture->setSnapmaticFormat(SnapmaticFormat::PGTA5_Format);
if (picture->exportPicture(picturePath, SnapmaticFormat::PGTA5_Format)) {
picture->setSnapmaticFormat(SnapmaticFormat::PGTA_Format);
if (picture->exportPicture(picturePath, SnapmaticFormat::PGTA_Format)) {
emit pictureFixed(picture);
}
}

View file

@ -15,7 +15,7 @@ Open Source Snapmatic and Savegame viewer/editor for GTA V
# Note: Install Docker Community Edition and Git before continuing
docker pull sypingauto/gta5view-build:1.10-static
git clone --recurse-submodules https://gitlab.com/Syping/gta5view
git clone https://gitlab.com/Syping/gta5view
docker run --rm -v "$PWD/gta5view:/gta5view" -it sypingauto/gta5view-build:1.10-static
mingw64-qt-cmake -B /gta5view/build /gta5view
cmake --build /gta5view/build
@ -23,7 +23,7 @@ Open Source Snapmatic and Savegame viewer/editor for GTA V
#### Build gta5view for Debian/Ubuntu
sudo apt-get install cmake git gcc g++ libqt5svg5-dev make qtbase5-dev qttranslations5-l10n
git clone --recurse-submodules https://gitlab.com/Syping/gta5view
git clone https://gitlab.com/Syping/gta5view
cmake -B gta5view-build gta5view
cmake --build gta5view-build
sudo cmake --install gta5view-build
@ -31,7 +31,7 @@ Open Source Snapmatic and Savegame viewer/editor for GTA V
#### Build gta5view for Arch/Manjaro
sudo pacman -S cmake gcc git make qt5-base qt5-svg qt5-tools qt5-translations
git clone --recurse-submodules https://gitlab.com/Syping/gta5view
git clone https://gitlab.com/Syping/gta5view
cmake -B gta5view-build gta5view
cmake --build gta5view-build
sudo cmake --install gta5view-build
@ -39,7 +39,7 @@ Open Source Snapmatic and Savegame viewer/editor for GTA V
#### Build gta5view for Fedora/RHEL
sudo dnf install cmake git gcc gcc-c++ make qt5-qtbase-devel qt5-qtsvg-devel qt5-qttranslations
git clone --recurse-submodules https://gitlab.com/Syping/gta5view
git clone https://gitlab.com/Syping/gta5view
cmake -B gta5view-build gta5view
cmake --build gta5view-build
sudo cmake --install gta5view-build

893
RagePhoto.cpp Normal file
View file

@ -0,0 +1,893 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2020-2022 Syping
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#include "RagePhoto.h"
#include <QJsonDocument>
#include <QBuffer>
#include <QFile>
#if QT_VERSION < 0x060000
#include <QTextCodec>
#else
#include <QStringEncoder>
#include <QStringDecoder>
#endif
#ifdef RAGEPHOTO_BENCHMARK
#include <QFileInfo>
#include <chrono>
#endif
inline quint32 joaatFromSI(const char *data, size_t size)
{
quint32 val = 0xE47AB81CUL;
for (size_t i = 0; i != size; i++) {
val += data[i];
val += (val << 10);
val ^= (val >> 6);
}
val += (val << 3);
val ^= (val >> 11);
val += (val << 15);
return val;
}
RagePhoto::RagePhoto()
{
p_photoFormat = PhotoFormat::Undefined;
p_isLoaded = false;
p_inputMode = -1;
}
RagePhoto::RagePhoto(const QByteArray &data) : p_fileData(data)
{
p_photoFormat = PhotoFormat::Undefined;
p_isLoaded = false;
p_inputMode = 0;
}
RagePhoto::RagePhoto(const QString &filePath) : p_filePath(filePath)
{
p_photoFormat = PhotoFormat::Undefined;
p_isLoaded = false;
p_inputMode = 1;
}
RagePhoto::RagePhoto(QIODevice *ioDevice) : p_ioDevice(ioDevice)
{
p_photoFormat = PhotoFormat::Undefined;
p_isLoaded = false;
p_inputMode = 2;
}
bool RagePhoto::isLoaded()
{
return p_isLoaded;
}
bool RagePhoto::load()
{
if (p_inputMode == -1)
return false;
if (p_isLoaded)
clear();
if (p_inputMode == 1) {
QFile pictureFile(p_filePath);
if (pictureFile.open(QIODevice::ReadOnly)) {
p_fileData = pictureFile.readAll();
}
pictureFile.close();
}
else if (p_inputMode == 2) {
if (!p_ioDevice->isOpen()) {
if (!p_ioDevice->open(QIODevice::ReadOnly))
return false;
}
p_fileData = p_ioDevice->readAll();
}
QBuffer dataBuffer(&p_fileData);
dataBuffer.open(QIODevice::ReadOnly);
#ifdef RAGEPHOTO_BENCHMARK
auto benchmark_parse_start = std::chrono::high_resolution_clock::now();
#endif
char uInt32Buffer[4];
qint64 size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4)
return false;
quint32 format = charToUInt32LE(uInt32Buffer);
if (format == static_cast<quint32>(PhotoFormat::GTA5)) {
char photoHeader[256];
size = dataBuffer.read(photoHeader, 256);
if (size != 256) {
return false;
}
for (const QChar &photoChar : utf16LEToString(photoHeader, 256)) {
if (photoChar.isNull())
break;
p_photoString += photoChar;
}
size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4)
return false;
p_headerSum = charToUInt32LE(uInt32Buffer);
size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4)
return false;
p_endOfFile = charToUInt32LE(uInt32Buffer);
size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4)
return false;
p_jsonOffset = charToUInt32LE(uInt32Buffer);
size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4)
return false;
p_titlOffset = charToUInt32LE(uInt32Buffer);
size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4)
return false;
p_descOffset = charToUInt32LE(uInt32Buffer);
char markerBuffer[4];
size = dataBuffer.read(markerBuffer, 4);
if (size != 4)
return false;
if (strncmp(markerBuffer, "JPEG", 4) != 0)
return false;
size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4)
return false;
p_photoBuffer = charToUInt32LE(uInt32Buffer);
size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4)
return false;
quint32 t_photoSize = charToUInt32LE(uInt32Buffer);
char *photoData = static_cast<char*>(malloc(t_photoSize));
if (!photoData)
return false;
size = dataBuffer.read(photoData, t_photoSize);
if (size != t_photoSize) {
free(photoData);
return false;
}
p_photoData = QByteArray(photoData, t_photoSize);
free(photoData);
dataBuffer.seek(p_jsonOffset + 264);
size = dataBuffer.read(markerBuffer, 4);
if (size != 4)
return false;
if (strncmp(markerBuffer, "JSON", 4) != 0)
return false;
size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4)
return false;
p_jsonBuffer = charToUInt32LE(uInt32Buffer);
char *jsonBytes = static_cast<char*>(malloc(p_jsonBuffer));
if (!jsonBytes)
return false;
size = dataBuffer.read(jsonBytes, p_jsonBuffer);
if (size != p_jsonBuffer) {
free(jsonBytes);
return false;
}
quint32 i;
for (i = 0; i != p_jsonBuffer; i++) {
if (jsonBytes[i] == '\x00')
break;
}
p_jsonData = QByteArray(jsonBytes, i);
free(jsonBytes);
QJsonDocument t_jsonDocument = QJsonDocument::fromJson(p_jsonData);
if (t_jsonDocument.isNull())
return false;
p_jsonObject = t_jsonDocument.object();
dataBuffer.seek(p_titlOffset + 264);
size = dataBuffer.read(markerBuffer, 4);
if (size != 4)
return false;
if (strncmp(markerBuffer, "TITL", 4) != 0)
return false;
size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4)
return false;
p_titlBuffer = charToUInt32LE(uInt32Buffer);
char *titlBytes = static_cast<char*>(malloc(p_titlBuffer));
if (!titlBytes)
return false;
size = dataBuffer.read(titlBytes, p_titlBuffer);
if (size != p_titlBuffer){
free(titlBytes);
return false;
}
for (i = 0; i != p_titlBuffer; i++) {
if (titlBytes[i] == '\x00')
break;
}
p_titleString = QString::fromUtf8(titlBytes, i);
free(titlBytes);
dataBuffer.seek(p_descOffset + 264);
size = dataBuffer.read(markerBuffer, 4);
if (size != 4)
return false;
if (strncmp(markerBuffer, "DESC", 4) != 0)
return false;
size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4)
return false;
p_descBuffer = charToUInt32LE(uInt32Buffer);
char *descBytes = static_cast<char*>(malloc(p_descBuffer));
if (!descBytes)
return false;
size = dataBuffer.read(descBytes, p_descBuffer);
if (size != p_descBuffer) {
free(descBytes);
return false;
}
for (i = 0; i != p_descBuffer; i++) {
if (descBytes[i] == '\x00')
break;
}
p_descriptionString = QString::fromUtf8(descBytes, i);
free(descBytes);
dataBuffer.seek(p_endOfFile + 260);
size = dataBuffer.read(markerBuffer, 4);
if (size != 4)
return false;
if (strncmp(markerBuffer, "JEND", 4) != 0)
return false;
#ifdef RAGEPHOTO_BENCHMARK
auto benchmark_parse_end = std::chrono::high_resolution_clock::now();
auto benchmark_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(benchmark_parse_end - benchmark_parse_start);
if (p_inputMode == 1) {
QTextStream(stdout) << QFileInfo(p_filePath).fileName() << ": " << benchmark_ns.count() << "ns" << Qt::endl;
}
else {
QTextStream(stdout) << "PGTA5" << p_jsonObject.value("uid").toInt() << ": " << benchmark_ns.count() << "ns" << Qt::endl;
}
#endif
if (p_photoFormat != PhotoFormat::G5EX)
p_photoFormat = PhotoFormat::GTA5;
p_fileData.clear();
p_isLoaded = true;
return true;
}
else if (format == static_cast<quint32>(PhotoFormat::G5EX)) {
size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4)
return false;
format = charToUInt32LE(uInt32Buffer);
if (format == static_cast<quint32>(ExportFormat::G5E3P)) {
size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4)
return false;
quint32 compressedSize = charToUInt32LE(uInt32Buffer);
char *compressedPhotoHeader = static_cast<char*>(malloc(compressedSize));
if (!compressedPhotoHeader)
return false;
size = dataBuffer.read(compressedPhotoHeader, compressedSize);
if (size != compressedSize) {
free(compressedPhotoHeader);
return false;
}
QByteArray t_photoHeader = QByteArray::fromRawData(compressedPhotoHeader, compressedSize);
t_photoHeader = qUncompress(t_photoHeader);
free(compressedPhotoHeader);
if (t_photoHeader.isEmpty())
return false;
p_photoString = QString::fromUtf8(t_photoHeader);
size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4)
return false;
p_headerSum = charToUInt32LE(uInt32Buffer);
size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4)
return false;
p_photoBuffer = charToUInt32LE(uInt32Buffer);
size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4)
return false;
compressedSize = charToUInt32LE(uInt32Buffer);
char *compressedPhoto = static_cast<char*>(malloc(compressedSize));
if (!compressedPhoto)
return false;
size = dataBuffer.read(compressedPhoto, compressedSize);
if (size != compressedSize) {
free(compressedPhoto);
return false;
}
QByteArray t_photoData = QByteArray::fromRawData(compressedPhoto, compressedSize);
p_photoData = qUncompress(t_photoData);
free(compressedPhoto);
size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4)
return false;
p_jsonOffset = charToUInt32LE(uInt32Buffer);
size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4)
return false;
p_jsonBuffer = charToUInt32LE(uInt32Buffer);
size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4)
return false;
compressedSize = charToUInt32LE(uInt32Buffer);
char *compressedJson = static_cast<char*>(malloc(compressedSize));
if (!compressedJson)
return false;
size = dataBuffer.read(compressedJson, compressedSize);
if (size != compressedSize) {
free(compressedJson);
return false;
}
QByteArray t_jsonBytes = QByteArray::fromRawData(compressedJson, compressedSize);
p_jsonData = qUncompress(t_jsonBytes);
free(compressedJson);
if (p_jsonData.isEmpty())
return false;
QJsonDocument t_jsonDocument = QJsonDocument::fromJson(p_jsonData);
if (t_jsonDocument.isNull())
return false;
p_jsonObject = t_jsonDocument.object();
size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4)
return false;
p_titlOffset = charToUInt32LE(uInt32Buffer);
size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4)
return false;
p_titlBuffer = charToUInt32LE(uInt32Buffer);
size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4)
return false;
compressedSize = charToUInt32LE(uInt32Buffer);
char *compressedTitl = static_cast<char*>(malloc(compressedSize));
if (!compressedTitl)
return false;
size = dataBuffer.read(compressedTitl, compressedSize);
if (size != compressedSize) {
free(compressedTitl);
return false;
}
QByteArray t_titlBytes = QByteArray::fromRawData(compressedTitl, compressedSize);
t_titlBytes = qUncompress(t_titlBytes);
free(compressedTitl);
p_titleString = QString::fromUtf8(t_titlBytes);
size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4)
return false;
p_descOffset = charToUInt32LE(uInt32Buffer);
size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4)
return false;
p_descBuffer = charToUInt32LE(uInt32Buffer);
size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4)
return false;
compressedSize = charToUInt32LE(uInt32Buffer);
char *compressedDesc = static_cast<char*>(malloc(compressedSize));
if (!compressedDesc)
return false;
size = dataBuffer.read(compressedDesc, compressedSize);
if (size != compressedSize) {
free(compressedDesc);
return false;
}
QByteArray t_descBytes = QByteArray::fromRawData(compressedDesc, compressedSize);
t_descBytes = qUncompress(t_descBytes);
free(compressedDesc);
p_descriptionString = QString::fromUtf8(t_descBytes);
size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4)
return false;
p_endOfFile = charToUInt32LE(uInt32Buffer);
#ifdef RAGEPHOTO_BENCHMARK
auto benchmark_parse_end = std::chrono::high_resolution_clock::now();
auto benchmark_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(benchmark_parse_end - benchmark_parse_start);
if (p_inputMode == 1) {
QTextStream(stdout) << QFileInfo(p_filePath).fileName() << ": " << benchmark_ns.count() << "ns" << Qt::endl;
}
else {
QTextStream(stdout) << "PGTA5" << p_jsonObject.value("uid").toInt() << ": " << benchmark_ns.count() << "ns" << Qt::endl;
}
#endif
p_photoFormat = PhotoFormat::G5EX;
p_fileData.clear();
p_isLoaded = true;
return true;
}
else if (format == static_cast<quint32>(ExportFormat::G5E2P)) {
p_photoFormat = PhotoFormat::G5EX;
p_fileData = qUncompress(dataBuffer.readAll());
if (p_fileData.isEmpty())
return false;
p_inputMode = 0;
return load();
}
else if (format == static_cast<quint32>(ExportFormat::G5E1P)) {
#if QT_VERSION >= 0x050A00
size = dataBuffer.skip(1);
if (size != 1)
return false;
#else
if (!dataBuffer.seek(dataBuffer.pos() + 1))
return false;
#endif
char length[1];
size = dataBuffer.read(length, 1);
if (size != 1)
return false;
int i_length = QByteArray::number(static_cast<int>(length[0]), 16).toInt() + 6;
#if QT_VERSION >= 0x050A00
size = dataBuffer.skip(i_length);
if (size != i_length)
return false;
#else
if (!dataBuffer.seek(dataBuffer.pos() + i_length))
return false;
#endif
p_photoFormat = PhotoFormat::G5EX;
p_fileData = qUncompress(dataBuffer.readAll());
if (p_fileData.isEmpty())
return false;
p_inputMode = 0;
return load();
}
else {
return false;
}
}
else {
return false;
}
}
void RagePhoto::clear()
{
p_photoFormat = PhotoFormat::Undefined;
p_jsonObject = QJsonObject();
p_descriptionString.clear();
p_jsonData.clear();
p_photoData.clear();
p_photoString.clear();
p_titleString.clear();
p_headerSum = 0;
p_isLoaded = false;
}
void RagePhoto::setDescription(const QString &description)
{
p_descriptionString = description;
}
void RagePhoto::setFileData(const QByteArray &data)
{
p_fileData = data;
p_inputMode = 0;
}
void RagePhoto::setFilePath(const QString &filePath)
{
p_filePath = filePath;
p_inputMode = 1;
}
void RagePhoto::setIODevice(QIODevice *ioDevice)
{
p_ioDevice = ioDevice;
p_inputMode = 2;
}
bool RagePhoto::setJsonData(const QByteArray &data)
{
QJsonDocument t_jsonDocument = QJsonDocument::fromJson(data);
if (t_jsonDocument.isNull())
return false;
p_jsonObject = t_jsonDocument.object();
// serializer band-aid
QJsonObject t_jsonObject = p_jsonObject;
t_jsonObject["sign"] = "__gta5view.sign";
t_jsonDocument.setObject(t_jsonObject);
p_jsonData = t_jsonDocument.toJson(QJsonDocument::Compact);
char sign_char[24];
sprintf(sign_char, "%llu", (0x100000000000000ULL | joaatFromSI(p_photoData.constData(), p_photoData.size())));
p_jsonData.replace("\"__gta5view.sign\"", sign_char);
return true;
}
bool RagePhoto::setPhotoBuffer(quint32 size, bool moveOffsets)
{
if (size < static_cast<quint32>(p_photoData.size()))
return false;
p_photoBuffer = size;
if (moveOffsets) {
p_jsonOffset = size + 28;
p_titlOffset = p_jsonOffset + p_jsonBuffer + 8;
p_descOffset = p_titlOffset + p_titlBuffer + 8;
p_endOfFile = p_descOffset + p_descBuffer + 12;
}
return true;
}
bool RagePhoto::setPhotoData(const QByteArray &data)
{
quint32 size = data.size();
if (size > p_photoBuffer)
return false;
p_photoData = data;
// serializer band-aid
setJsonData(p_jsonData);
return true;
}
bool RagePhoto::setPhotoData(const char *data, int size)
{
if (static_cast<quint32>(size) > p_photoBuffer)
return false;
p_photoData = QByteArray(data, size);
// serializer band-aid
setJsonData(p_jsonData);
return true;
}
void RagePhoto::setPhotoFormat(PhotoFormat photoFormat)
{
p_photoFormat = photoFormat;
}
void RagePhoto::setTitle(const QString &title)
{
p_titleString = title;
}
const QByteArray RagePhoto::jsonData(JsonFormat jsonFormat)
{
if (jsonFormat == JsonFormat::Compact) {
return QJsonDocument(p_jsonObject).toJson(QJsonDocument::Compact);
}
else if (jsonFormat == JsonFormat::Indented) {
return QJsonDocument(p_jsonObject).toJson(QJsonDocument::Indented);
}
else {
return p_jsonData;
}
}
const QJsonObject RagePhoto::jsonObject()
{
return p_jsonObject;
}
const QByteArray RagePhoto::photoData()
{
return p_photoData;
}
const QString RagePhoto::description()
{
return p_descriptionString;
}
const QString RagePhoto::photoString()
{
return p_photoString;
}
const QString RagePhoto::title()
{
return p_titleString;
}
quint32 RagePhoto::photoBuffer()
{
return p_photoBuffer;
}
quint32 RagePhoto::photoSize()
{
return p_photoData.size();
}
RagePhoto::PhotoFormat RagePhoto::photoFormat()
{
return p_photoFormat;
}
QByteArray RagePhoto::save(PhotoFormat photoFormat)
{
QByteArray data;
QBuffer dataBuffer(&data);
dataBuffer.open(QIODevice::WriteOnly);
save(&dataBuffer, photoFormat);
return data;
}
void RagePhoto::save(QIODevice *ioDevice, PhotoFormat photoFormat)
{
// serializer band-aid
setJsonData(p_jsonData);
if (photoFormat == PhotoFormat::G5EX) {
char uInt32Buffer[4];
quint32 format = static_cast<quint32>(PhotoFormat::G5EX);
uInt32ToCharLE(format, uInt32Buffer);
ioDevice->write(uInt32Buffer, 4);
format = static_cast<quint32>(ExportFormat::G5E3P);
uInt32ToCharLE(format, uInt32Buffer);
ioDevice->write(uInt32Buffer, 4);
QByteArray compressedData = qCompress(p_photoString.toUtf8(), 9);
quint32 compressedSize = compressedData.size();
uInt32ToCharLE(compressedSize, uInt32Buffer);
ioDevice->write(uInt32Buffer, 4);
ioDevice->write(compressedData);
uInt32ToCharLE(p_headerSum, uInt32Buffer);
ioDevice->write(uInt32Buffer, 4);
uInt32ToCharLE(p_photoBuffer, uInt32Buffer);
ioDevice->write(uInt32Buffer, 4);
compressedData = qCompress(p_photoData, 9);
compressedSize = compressedData.size();
uInt32ToCharLE(compressedSize, uInt32Buffer);
ioDevice->write(uInt32Buffer, 4);
ioDevice->write(compressedData);
uInt32ToCharLE(p_jsonOffset, uInt32Buffer);
ioDevice->write(uInt32Buffer, 4);
uInt32ToCharLE(p_jsonBuffer, uInt32Buffer);
ioDevice->write(uInt32Buffer, 4);
compressedData = qCompress(p_jsonData, 9);
compressedSize = compressedData.size();
uInt32ToCharLE(compressedSize, uInt32Buffer);
ioDevice->write(uInt32Buffer, 4);
ioDevice->write(compressedData);
uInt32ToCharLE(p_titlOffset, uInt32Buffer);
ioDevice->write(uInt32Buffer, 4);
uInt32ToCharLE(p_titlBuffer, uInt32Buffer);
ioDevice->write(uInt32Buffer, 4);
compressedData = qCompress(p_titleString.toUtf8(), 9);
compressedSize = compressedData.size();
uInt32ToCharLE(compressedSize, uInt32Buffer);
ioDevice->write(uInt32Buffer, 4);
ioDevice->write(compressedData);
uInt32ToCharLE(p_descOffset, uInt32Buffer);
ioDevice->write(uInt32Buffer, 4);
uInt32ToCharLE(p_descBuffer, uInt32Buffer);
ioDevice->write(uInt32Buffer, 4);
compressedData = qCompress(p_descriptionString.toUtf8(), 9);
compressedSize = compressedData.size();
uInt32ToCharLE(compressedSize, uInt32Buffer);
ioDevice->write(uInt32Buffer, 4);
ioDevice->write(compressedData);
uInt32ToCharLE(p_endOfFile, uInt32Buffer);
ioDevice->write(uInt32Buffer, 4);
}
else if (photoFormat == PhotoFormat::GTA5) {
char uInt32Buffer[4];
quint32 format = static_cast<quint32>(PhotoFormat::GTA5);
uInt32ToCharLE(format, uInt32Buffer);
ioDevice->write(uInt32Buffer, 4);
QByteArray photoHeader = stringToUtf16LE(p_photoString);
if (photoHeader.startsWith("\xFF\xFE")) {
photoHeader.remove(0, 2);
}
qint64 photoHeaderSize = photoHeader.size();
if (photoHeaderSize > 256) {
photoHeader = photoHeader.left(256);
photoHeaderSize = 256;
}
ioDevice->write(photoHeader);
for (qint64 size = photoHeaderSize; size < 256; size++) {
ioDevice->write("\x00", 1);
}
uInt32ToCharLE(p_headerSum, uInt32Buffer);
ioDevice->write(uInt32Buffer, 4);
uInt32ToCharLE(p_endOfFile, uInt32Buffer);
ioDevice->write(uInt32Buffer, 4);
uInt32ToCharLE(p_jsonOffset, uInt32Buffer);
ioDevice->write(uInt32Buffer, 4);
uInt32ToCharLE(p_titlOffset, uInt32Buffer);
ioDevice->write(uInt32Buffer, 4);
uInt32ToCharLE(p_descOffset, uInt32Buffer);
ioDevice->write(uInt32Buffer, 4);
ioDevice->write("JPEG", 4);
uInt32ToCharLE(p_photoBuffer, uInt32Buffer);
ioDevice->write(uInt32Buffer, 4);
quint32 t_photoSize = p_photoData.size();
uInt32ToCharLE(t_photoSize, uInt32Buffer);
ioDevice->write(uInt32Buffer, 4);
ioDevice->write(p_photoData);
for (qint64 size = t_photoSize; size < p_photoBuffer; size++) {
ioDevice->write("\x00", 1);
}
ioDevice->seek(p_jsonOffset + 264);
ioDevice->write("JSON", 4);
uInt32ToCharLE(p_jsonBuffer, uInt32Buffer);
ioDevice->write(uInt32Buffer, 4);
qint64 dataSize = p_jsonData.size();
ioDevice->write(p_jsonData);
for (qint64 size = dataSize; size < p_jsonBuffer; size++) {
ioDevice->write("\x00", 1);
}
ioDevice->seek(p_titlOffset + 264);
ioDevice->write("TITL", 4);
uInt32ToCharLE(p_titlBuffer, uInt32Buffer);
ioDevice->write(uInt32Buffer, 4);
QByteArray data = p_titleString.toUtf8();
dataSize = data.size();
ioDevice->write(data);
for (qint64 size = dataSize; size < p_titlBuffer; size++) {
ioDevice->write("\x00", 1);
}
ioDevice->seek(p_descOffset + 264);
ioDevice->write("DESC", 4);
uInt32ToCharLE(p_descBuffer, uInt32Buffer);
ioDevice->write(uInt32Buffer, 4);
data = p_descriptionString.toUtf8();
dataSize = data.size();
ioDevice->write(data);
for (qint64 size = dataSize; size < p_descBuffer; size++) {
ioDevice->write("\x00", 1);
}
ioDevice->seek(p_endOfFile + 260);
ioDevice->write("JEND", 4);
}
}
RagePhoto* RagePhoto::loadFile(const QString &filePath)
{
RagePhoto *ragePhoto = new RagePhoto(filePath);
ragePhoto->load();
return ragePhoto;
}
quint32 RagePhoto::charToUInt32BE(char *x)
{
return (static_cast<unsigned char>(x[0]) << 24 |
static_cast<unsigned char>(x[1]) << 16 |
static_cast<unsigned char>(x[2]) << 8 |
static_cast<unsigned char>(x[3]));
}
quint32 RagePhoto::charToUInt32LE(char *x)
{
return (static_cast<unsigned char>(x[3]) << 24 |
static_cast<unsigned char>(x[2]) << 16 |
static_cast<unsigned char>(x[1]) << 8 |
static_cast<unsigned char>(x[0]));
}
void RagePhoto::uInt32ToCharBE(quint32 x, char *y)
{
y[0] = x >> 24;
y[1] = x >> 16;
y[2] = x >> 8;
y[3] = x;
}
void RagePhoto::uInt32ToCharLE(quint32 x, char *y)
{
y[0] = x;
y[1] = x >> 8;
y[2] = x >> 16;
y[3] = x >> 24;
}
const QByteArray RagePhoto::stringToUtf16LE(const QString &string)
{
#if QT_VERSION >= 0x060000
return QStringEncoder(QStringEncoder::Utf16LE)(string);
#else
return QTextCodec::codecForName("UTF-16LE")->fromUnicode(string);
#endif
}
const QString RagePhoto::utf16LEToString(const QByteArray &data)
{
#if QT_VERSION >= 0x060000
return QStringDecoder(QStringDecoder::Utf16LE)(data);
#else
return QTextCodec::codecForName("UTF-16LE")->toUnicode(data);
#endif
}
const QString RagePhoto::utf16LEToString(const char *data, int size)
{
#if QT_VERSION >= 0x060000
return QStringDecoder(QStringDecoder::Utf16LE)(QByteArray::fromRawData(data, size));
#else
return QTextCodec::codecForName("UTF-16LE")->toUnicode(data, size);
#endif
}

110
RagePhoto.h Normal file
View file

@ -0,0 +1,110 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2020 Syping
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifndef RAGEPHOTO_H
#define RAGEPHOTO_H
#include <QJsonObject>
#include <QIODevice>
#include <QObject>
class RagePhoto : public QObject
{
Q_OBJECT
public:
enum class JsonFormat : quint8 {
Original = 0,
Compact = 1,
Indented = 2,
};
enum class ExportFormat : quint32 {
G5E1P = 0x454C0010U,
G5E2P = 0x01000032U,
G5E2S = 0x02000032U,
G5E3P = 0x01000033U,
G5E3S = 0x02000033U,
Undefined = 0,
};
enum class PhotoFormat : quint32 {
G5EX = 0x45354700U,
GTA5 = 0x01000000U,
RDR2 = 0x04000000U,
Undefined = 0,
};
explicit RagePhoto();
explicit RagePhoto(const QByteArray &data);
explicit RagePhoto(const QString &filePath);
explicit RagePhoto(QIODevice *ioDevice);
bool isLoaded();
bool load();
void clear();
void setDescription(const QString &description);
void setFileData(const QByteArray &data);
void setFilePath(const QString &filePath);
void setIODevice(QIODevice *ioDevice);
bool setJsonData(const QByteArray &data);
bool setPhotoBuffer(quint32 size, bool moveOffsets = true);
bool setPhotoData(const QByteArray &data);
bool setPhotoData(const char *data, int size);
void setPhotoFormat(PhotoFormat photoFormat);
void setTitle(const QString &title);
const QJsonObject jsonObject();
const QByteArray jsonData(JsonFormat jsonFormat = JsonFormat::Original);
const QByteArray photoData();
const QString description();
const QString photoString();
const QString title();
quint32 photoBuffer();
quint32 photoSize();
PhotoFormat photoFormat();
QByteArray save(PhotoFormat photoFormat);
void save(QIODevice *ioDevice, PhotoFormat photoFormat);
static RagePhoto* loadFile(const QString &filePath);
private:
inline quint32 charToUInt32BE(char *x);
inline quint32 charToUInt32LE(char *x);
inline void uInt32ToCharBE(quint32 x, char *y);
inline void uInt32ToCharLE(quint32 x, char *y);
inline const QByteArray stringToUtf16LE(const QString &string);
inline const QString utf16LEToString(const QByteArray &data);
inline const QString utf16LEToString(const char *data, int size);
PhotoFormat p_photoFormat;
QJsonObject p_jsonObject;
QByteArray p_fileData;
QByteArray p_jsonData;
QByteArray p_photoData;
QIODevice *p_ioDevice;
QString p_descriptionString;
QString p_filePath;
QString p_photoString;
QString p_titleString;
quint32 p_descBuffer;
quint32 p_descOffset;
quint32 p_endOfFile;
quint32 p_headerSum;
quint32 p_jsonBuffer;
quint32 p_jsonOffset;
quint32 p_photoBuffer;
quint32 p_titlBuffer;
quint32 p_titlOffset;
bool p_isLoaded;
int p_inputMode;
};
#endif // RAGEPHOTO_H

View file

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2023 Syping
* Copyright (C) 2016-2017 Syping
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -16,6 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#include "SidebarGenerator.h"
#include "SavegameWidget.h"
#include "StandardPaths.h"
#include "SavegameCopy.h"
@ -27,6 +28,7 @@
SavegameCopy::SavegameCopy()
{
}
void SavegameCopy::copySavegame(QWidget *parent, QString sgdPath)
@ -46,47 +48,54 @@ fileDialogPreSave: //Work?
fileDialog.setOption(QFileDialog::DontUseNativeDialog, dontUseNativeDialog);
fileDialog.setOption(QFileDialog::DontConfirmOverwrite, true);
fileDialog.setDefaultSuffix("");
fileDialog.setWindowFlag(Qt::WindowContextHelpButtonHint, false);
fileDialog.setWindowFlags(fileDialog.windowFlags()^Qt::WindowContextHelpButtonHint);
fileDialog.setWindowTitle(SavegameWidget::tr(("Export Savegame...")));
fileDialog.setLabelText(QFileDialog::Accept, SavegameWidget::tr("Export"));
QStringList filters;
const QString fileName = sgdFileInfo.fileName();
if (fileName.startsWith("SGTA5"))
filters << SavegameWidget::tr("GTA V Savegames files (%1)").arg("SGTA5*");
else if (fileName.startsWith("SRDR3"))
filters << SavegameWidget::tr("RDR 2 Savegames files (%1)").arg("SRDR3*");
filters << SavegameWidget::tr("All files (%1)").arg("**");
filters << SavegameWidget::tr("Savegame files (SGTA*)");
filters << SavegameWidget::tr("All files (**)");
fileDialog.setNameFilters(filters);
QList<QUrl> sidebarUrls = SidebarGenerator::generateSidebarUrls(fileDialog.sidebarUrls());
fileDialog.setSidebarUrls(sidebarUrls);
fileDialog.setDirectory(settings.value("Directory", StandardPaths::picturesLocation()).toString());
fileDialog.restoreGeometry(settings.value(parent->objectName() % "+Geometry", "").toByteArray());
fileDialog.selectFile(sgdFileInfo.fileName());
if (fileDialog.exec()) {
if (fileDialog.exec())
{
QStringList selectedFiles = fileDialog.selectedFiles();
if (selectedFiles.length() == 1) {
if (selectedFiles.length() == 1)
{
QString selectedFile = selectedFiles.at(0);
if (QFile::exists(selectedFile)) {
if (QMessageBox::Yes == QMessageBox::warning(parent, SavegameWidget::tr("Export Savegame"), SavegameWidget::tr("Overwrite %1 with current Savegame?").arg("\""+selectedFile+"\""), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)) {
if (!QFile::remove(selectedFile)) {
if (QFile::exists(selectedFile))
{
if (QMessageBox::Yes == QMessageBox::warning(parent, SavegameWidget::tr("Export Savegame"), SavegameWidget::tr("Overwrite %1 with current Savegame?").arg("\""+selectedFile+"\""), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes))
{
if (!QFile::remove(selectedFile))
{
QMessageBox::warning(parent, SavegameWidget::tr("Export Savegame"), SavegameWidget::tr("Failed to overwrite %1 with current Savegame").arg("\""+selectedFile+"\""));
goto fileDialogPreSave; //Work?
}
}
else {
else
{
goto fileDialogPreSave; //Work?
}
}
bool isCopied = QFile::copy(sgdPath, selectedFile);
if (!isCopied) {
if (!isCopied)
{
QMessageBox::warning(parent, SavegameWidget::tr("Export Savegame"), SavegameWidget::tr("Failed to export current Savegame"));
goto fileDialogPreSave; //Work?
}
}
else {
else
{
QMessageBox::warning(parent, SavegameWidget::tr("Export Savegame"), SavegameWidget::tr("No valid file is selected"));
goto fileDialogPreSave; //Work?
}

View file

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2023 Syping
* Copyright (C) 2016-2017 Syping
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -17,6 +17,7 @@
*****************************************************************************/
#include "SnapmaticPicture.h"
#include "StringParser.h"
#include "SavegameData.h"
#include <QStringBuilder>
#include <QByteArray>
@ -24,8 +25,7 @@
#include <QFile>
#define savegameHeaderLength 260
#define verificationValue_GTA5 QByteArray::fromHex("00000001")
#define verificationValue_RDR2 QByteArray::fromHex("00000004")
#define verificationValue QByteArray::fromHex("00000001")
SavegameData::SavegameData(const QString &fileName, QObject *parent) : QObject(parent), savegameFileName(fileName)
{
@ -58,10 +58,13 @@ bool SavegameData::readingSavegame()
return false;
}
QByteArray savegameHeaderLine = saveFile->read(savegameHeaderLength);
if (savegameHeaderLine.startsWith(verificationValue_GTA5) || savegameHeaderLine.startsWith(verificationValue_RDR2)) {
if (savegameHeaderLine.left(4) == verificationValue)
{
savegameStr = getSavegameDataString(savegameHeaderLine);
if (savegameStr.length() >= 1)
{
savegameOk = true;
}
}
saveFile->close();
saveFile->deleteLater();
@ -72,25 +75,23 @@ bool SavegameData::readingSavegame()
QString SavegameData::getSavegameDataString(const QByteArray &savegameHeader)
{
QByteArray savegameBytes = savegameHeader.left(savegameHeaderLength);
char split_byte = 0x00;
if (savegameHeader.startsWith(verificationValue_GTA5))
split_byte = 0x01;
else if (savegameHeader.startsWith(verificationValue_RDR2))
split_byte = 0x04;
QList<QByteArray> savegameBytesList = savegameBytes.split(split_byte);
QList<QByteArray> savegameBytesList = savegameBytes.split(char(0x01));
savegameBytes = savegameBytesList.at(1);
savegameBytesList.clear();
return SnapmaticPicture::parseTitleString(savegameBytes);
return SnapmaticPicture::parseTitleString(savegameBytes, savegameBytes.length());
}
bool SavegameData::readingSavegameFromFile(const QString &fileName)
{
if (fileName != "") {
if (fileName != "")
{
savegameFileName = fileName;
return readingSavegame();
}
else
{
return false;
}
}
bool SavegameData::isSavegameOk()

View file

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2023 Syping
* Copyright (C) 2016-2018 Syping
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -28,7 +28,11 @@ SavegameDialog::SavegameDialog(QWidget *parent) :
ui(new Ui::SavegameDialog)
{
// Set Window Flags
#if QT_VERSION >= 0x050900
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
#else
setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint);
#endif
// Setup User Interface
ui->setupUi(this);

View file

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2023 Syping
* Copyright (C) 2016-2021 Syping
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -18,6 +18,7 @@
#include "SavegameWidget.h"
#include "ui_SavegameWidget.h"
#include "SidebarGenerator.h"
#include "SavegameDialog.h"
#include "StandardPaths.h"
#include "SavegameData.h"
@ -96,19 +97,27 @@ void SavegameWidget::renderString(const QString &savegameString, const QString &
QString savegameName = tr("WRONG FORMAT");
QString savegameDate = tr("WRONG FORMAT");
QStringList savegameNDL = QString(savegameString).split(" - ");
if (savegameNDL.length() >= 2) {
if (savegameNDL.length() >= 2)
{
savegameDate = savegameNDL.at(savegameNDL.length() - 1);
savegameName = QString(savegameString).remove(savegameString.length() - savegameDate.length() - 3, savegameDate.length() + 3);
}
int savegameNumber = QString(fileName).remove(0, 5).toInt(&validNumber) + 1;
if (validNumber) {
int savegameNumber = QString(fileName).remove(0,5).toInt(&validNumber) + 1;
if (validNumber)
{
if (savegameNumber == 16)
{
ui->labSavegameStr->setText(labelAutosaveStr.arg(savegameDate, savegameName));
}
else
{
ui->labSavegameStr->setText(labelSaveStr.arg(savegameDate, savegameName, QString::number(savegameNumber)));
}
}
else
{
ui->labSavegameStr->setText(labelSaveStr.arg(savegameDate, savegameName, tr("UNKNOWN")));
}
}
void SavegameWidget::retranslate()
@ -128,15 +137,18 @@ void SavegameWidget::on_cmdCopy_clicked()
void SavegameWidget::on_cmdDelete_clicked()
{
int uchoice = QMessageBox::question(this, tr("Delete Savegame"), tr("Are you sure to delete %1 from your savegames?").arg("\""+sgdStr+"\""), QMessageBox::No | QMessageBox::Yes, QMessageBox::No);
if (uchoice == QMessageBox::Yes) {
if (!QFile::exists(sgdPath)) {
if (uchoice == QMessageBox::Yes)
{
if (!QFile::exists(sgdPath))
{
emit savegameDeleted();
#ifdef GTA5SYNC_TELEMETRY
QSettings telemetrySettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
telemetrySettings.beginGroup("Telemetry");
bool pushUsageData = telemetrySettings.value("PushUsageData", false).toBool();
telemetrySettings.endGroup();
if (pushUsageData && Telemetry->canPush()) {
if (pushUsageData && Telemetry->canPush())
{
QJsonDocument jsonDocument;
QJsonObject jsonObject;
jsonObject["Type"] = "DeleteSuccess";
@ -151,13 +163,15 @@ void SavegameWidget::on_cmdDelete_clicked()
}
#endif
}
else if (QFile::remove(sgdPath)) {
else if (QFile::remove(sgdPath))
{
#ifdef GTA5SYNC_TELEMETRY
QSettings telemetrySettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
telemetrySettings.beginGroup("Telemetry");
bool pushUsageData = telemetrySettings.value("PushUsageData", false).toBool();
telemetrySettings.endGroup();
if (pushUsageData && Telemetry->canPush()) {
if (pushUsageData && Telemetry->canPush())
{
QJsonDocument jsonDocument;
QJsonObject jsonObject;
jsonObject["Type"] = "DeleteSuccess";
@ -174,17 +188,25 @@ void SavegameWidget::on_cmdDelete_clicked()
emit savegameDeleted();
}
else
{
QMessageBox::warning(this, tr("Delete Savegame"), tr("Failed at deleting %1 from your savegames").arg("\""+sgdStr+"\""));
}
}
}
void SavegameWidget::on_cmdView_clicked()
{
SavegameDialog savegameDialog(this);
savegameDialog.setSavegameData(sgdata, sgdPath, true);
savegameDialog.setModal(true);
savegameDialog.show();
savegameDialog.exec();
SavegameDialog *savegameDialog = new SavegameDialog(this);
savegameDialog->setSavegameData(sgdata, sgdPath, true);
savegameDialog->setModal(true);
#ifdef Q_OS_ANDROID
// Android ...
savegameDialog->showMaximized();
#else
savegameDialog->show();
#endif
savegameDialog->exec();
delete savegameDialog;
}
void SavegameWidget::mousePressEvent(QMouseEvent *ev)
@ -195,20 +217,31 @@ void SavegameWidget::mousePressEvent(QMouseEvent *ev)
void SavegameWidget::mouseReleaseEvent(QMouseEvent *ev)
{
ProfileWidget::mouseReleaseEvent(ev);
if (ui->cbSelected->isVisible()) {
if (ui->cbSelected->isVisible())
{
if (rect().contains(ev->pos()) && ev->button() == Qt::LeftButton)
{
ui->cbSelected->setChecked(!ui->cbSelected->isChecked());
}
}
else {
else
{
const int contentMode = getContentMode();
if ((contentMode == 0 || contentMode == 10 || contentMode == 20) && rect().contains(ev->pos()) && ev->button() == Qt::LeftButton) {
if ((contentMode == 0 || contentMode == 10 || contentMode == 20) && rect().contains(ev->pos()) && ev->button() == Qt::LeftButton)
{
if (ev->modifiers().testFlag(Qt::ShiftModifier))
{
ui->cbSelected->setChecked(!ui->cbSelected->isChecked());
}
else
{
on_cmdView_clicked();
}
}
else if (!ui->cbSelected->isVisible() && (contentMode == 1 || contentMode == 11 || contentMode == 21) && ev->button() == Qt::LeftButton && ev->modifiers().testFlag(Qt::ShiftModifier))
{
ui->cbSelected->setChecked(!ui->cbSelected->isChecked());
}
}
}
@ -218,7 +251,9 @@ void SavegameWidget::mouseDoubleClickEvent(QMouseEvent *ev)
const int contentMode = getContentMode();
if (!ui->cbSelected->isVisible() && (contentMode == 1 || contentMode == 11 || contentMode == 21) && ev->button() == Qt::LeftButton)
{
on_cmdView_clicked();
}
}
void SavegameWidget::setSelected(bool isSelected)
@ -239,9 +274,13 @@ void SavegameWidget::contextMenuEvent(QContextMenuEvent *ev)
void SavegameWidget::on_cbSelected_stateChanged(int arg1)
{
if (arg1 == Qt::Checked)
{
emit widgetSelected();
}
else if (arg1 == Qt::Unchecked)
{
emit widgetDeselected();
}
}
bool SavegameWidget::isSelected()
@ -271,5 +310,5 @@ SavegameData* SavegameWidget::getSavegame()
QString SavegameWidget::getWidgetType()
{
return QStringLiteral("SavegameWidget");
return "SavegameWidget";
}

View file

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2023 Syping
* Copyright (C) 2016-2017 Syping
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -44,17 +44,15 @@ public:
void retranslate();
~SavegameWidget();
public slots:
private slots:
void on_cmdView_clicked();
void on_cmdCopy_clicked();
void on_cmdDelete_clicked();
void on_cbSelected_stateChanged(int arg1);
void savegameSelected();
void selectAllWidgets();
void deselectAllWidgets();
private slots:
void on_cbSelected_stateChanged(int arg1);
protected:
void mouseDoubleClickEvent(QMouseEvent *ev);
void mouseReleaseEvent(QMouseEvent *ev);

61
SidebarGenerator.cpp Normal file
View file

@ -0,0 +1,61 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2017 Syping
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#include "SidebarGenerator.h"
#include "StandardPaths.h"
#include "AppEnv.h"
#include <QList>
#include <QUrl>
#include <QDir>
SidebarGenerator::SidebarGenerator()
{
}
QList<QUrl> SidebarGenerator::generateSidebarUrls(QList<QUrl> sidebarUrls)
{
QDir dir;
dir.setPath(StandardPaths::picturesLocation());
if (dir.exists())
{
sidebarUrls += QUrl::fromLocalFile(dir.absolutePath());
}
dir.setPath(StandardPaths::documentsLocation());
if (dir.exists())
{
sidebarUrls += QUrl::fromLocalFile(dir.absolutePath());
}
bool gameFolderExists;
QString gameFolder = AppEnv::getGameFolder(&gameFolderExists);
if (gameFolderExists)
{
sidebarUrls += QUrl::fromLocalFile(gameFolder);
}
dir.setPath(StandardPaths::desktopLocation());
if (dir.exists())
{
sidebarUrls += QUrl::fromLocalFile(dir.absolutePath());
}
return sidebarUrls;
}

View file

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2023 Syping
* Copyright (C) 2016-2017 Syping
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -16,18 +16,17 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifndef SNAPMATICJSON_H
#define SNAPMATICJSON_H
#ifndef Q_MOC_RUN
#ifndef SIDEBARGENERATOR_H
#define SIDEBARGENERATOR_H
#include <boost/json.hpp>
#include <QList>
#include <QUrl>
class SnapmaticJson
class SidebarGenerator
{
public:
static std::string serialize(const boost::json::value &jv, bool do_indent = false);
boost::json::object jsonObject;
SidebarGenerator();
static QList<QUrl> generateSidebarUrls(QList<QUrl> sidebarUrls);
};
#endif // Q_MOC_RUN
#endif // SNAPMATICJSON_H
#endif // SIDEBARGENERATOR_H

View file

@ -21,6 +21,7 @@
#include "SnapmaticPicture.h"
#include "PlayerListDialog.h"
#include "StringParser.h"
#include "wrapper.h"
#include "AppEnv.h"
#include "config.h"
#include <QStringBuilder>
@ -41,7 +42,11 @@ SnapmaticEditor::SnapmaticEditor(CrewDatabase *crewDB, ProfileDatabase *profileD
ui(new Ui::SnapmaticEditor)
{
// Set Window Flags
#if QT_VERSION >= 0x050900
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
#else
setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint);
#endif
ui->setupUi(this);
ui->cmdCancel->setDefault(true);
@ -231,7 +236,7 @@ void SnapmaticEditor::setSnapmaticTitle(const QString &title)
snapmaticTitle = title;
}
QString editStr = QString("<a href=\"g5e://edittitle\" style=\"text-decoration: none;\">%1</a>").arg(tr("Edit"));
QString titleStr = tr("Title: %1 (%2)").arg(snapmaticTitle.toHtmlEscaped(), editStr);
QString titleStr = tr("Title: %1 (%2)").arg(StringParser::escapeString(snapmaticTitle), editStr);
ui->labTitle->setText(titleStr);
if (SnapmaticPicture::verifyTitle(snapmaticTitle)) {
ui->labAppropriate->setText(tr("Appropriate: %1").arg(QString("<span style=\"color: green\">%1</span>").arg(tr("Yes", "Yes, should work fine"))));
@ -239,21 +244,25 @@ void SnapmaticEditor::setSnapmaticTitle(const QString &title)
else {
ui->labAppropriate->setText(tr("Appropriate: %1").arg(QString("<span style=\"color: red\">%1</span>").arg(tr("No", "No, could lead to issues"))));
}
#ifndef Q_OS_ANDROID
ui->gbValues->resize(ui->gbValues->width(), ui->gbValues->heightForWidth(ui->gbValues->width()));
ui->frameWidget->resize(ui->gbValues->width(), ui->frameWidget->heightForWidth(ui->frameWidget->width()));
if (heightForWidth(width()) > height())
resize(width(), heightForWidth(width()));
#endif
}
void SnapmaticEditor::setSnapmaticCrew(const QString &crew)
{
QString editStr = QString("<a href=\"g5e://editcrew\" style=\"text-decoration: none;\">%1</a>").arg(tr("Edit"));
QString crewStr = tr("Crew: %1 (%2)").arg(crew.toHtmlEscaped(), editStr);
QString crewStr = tr("Crew: %1 (%2)").arg(StringParser::escapeString(crew), editStr);
ui->labCrew->setText(crewStr);
#ifndef Q_OS_ANDROID
ui->gbValues->resize(ui->gbValues->width(), ui->gbValues->heightForWidth(ui->gbValues->width()));
ui->frameWidget->resize(ui->gbValues->width(), ui->frameWidget->heightForWidth(ui->frameWidget->width()));
if (heightForWidth(width()) > height())
resize(width(), heightForWidth(width()));
#endif
}
QString SnapmaticEditor::returnCrewName(int crewID_)
@ -306,7 +315,7 @@ void SnapmaticEditor::on_cmdApply_clicked()
QJsonDocument jsonDocument;
QJsonObject jsonObject;
jsonObject["Type"] = "PropertyEdited";
jsonObject["EditedSize"] = QString::number(smpic->getPictureSize());
jsonObject["EditedSize"] = QString::number(smpic->getContentMaxLength());
#if QT_VERSION >= 0x060000
jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toSecsSinceEpoch());
#else

Some files were not shown because too many files have changed in this diff Show more